-
Notifications
You must be signed in to change notification settings - Fork 65
Parsing API Parameters
JSON API clients should send data to servers
- As URL parameters
- In HTTP headers
- In HTTP body
The server should parse those data, check them and act accordingly. This package provides a few helpers that simplify these tasks.
As an interface for HTTP Requests PSR-7 is used (\Psr\Http\Message\ServerRequestInterface
in particularly).
If your application does not supports PSR-7 you can use a wrapper \Neomerx\JsonApi\Http\Request
which requires only a couple of methods to be implemented.
$nonPsr7request = ...;
$psr7request = new Request(function ($name) use ($nonPsr7request) {
return $nonPsr7request->getHeader($name);
}, function () use ($nonPsr7request) {
return $nonPsr7request->getQueryParams();
});
Note:
\Neomerx\JsonApi\Http\Request
is not a full implementation of PSR-7 but implements only a few methods required for this package.
Application may use the following parameters in URL
The package provides parser for these parameters that returns ParametersInterface
which is used for checking validity of input parameters and in application logic (data fetching from database, sorting, filtering, etc).
/** @var \Psr\Http\Message\ServerRequestInterface $request */
$request = ...;
$factory = new Factory();
$parameters = $factory->createParametersParser()->parse($request);
As the specification says
Clients MUST indicate that they can accept the JSON API media type, per the semantics of the HTTP
Accept
header.
which basically means JSON API server must handle those headers in accordance with RFC 2616. That's an example of a header that servers must support
Accept: application/vnd.api+json;q=0.5,text/html;q=0.8;*/*;q=0.1
Note: the package supports media type parameters and extensions as well. Specification reserves 'ext' parameter for future use and do not use media type extensions.
And server may choose to support text/html in order to simplify viewing content via a web browser.
Clients send their preferences in Accept
and Content-Type
headers. In order to parse HTTP headers in accordance with RFC 7231 the package provides implementation for CodecMatcherInterface
. It registers mapping between input/output media types (Content-Type
/Accept
headers) and decoder/encoder handlers (Closures
). Then CodecMatcher
can find best match for decoder/encoder by media types provided.
$factory = new Factory();
$matcher = $factory->createCodecMatcher();
// Register 'media type' => decoder / encoder
$decoderClosure = function () {
/** @var \Neomerx\JsonApi\Contracts\Decoder\DecoderInterface $decoder */
$decoder = ...;
return $decoder;
};
$encoderClosure = function () {
/** @var \Neomerx\JsonApi\Contracts\Encoder\EncoderInterface $encoder */
$encoder = ...;
return $encoder;
};
$jsonApiType = $factory->createMediaType(
MediaTypeInterface::JSON_API_TYPE,
MediaTypeInterface::JSON_API_SUB_TYPE
);
$jsonApiTypeUtf8 = $factory->createMediaType(
MediaTypeInterface::JSON_API_TYPE,
MediaTypeInterface::JSON_API_SUB_TYPE,
['charset' => 'UTF-8']
);
$matcher->registerEncoder($jsonApiType, $encoderClosure);
$matcher->registerDecoder($jsonApiType, $decoderClosure);
$matcher->registerEncoder($jsonApiTypeUtf8, $encoderClosure);
$matcher->registerDecoder($jsonApiTypeUtf8, $decoderClosure);
Then decoders/encoders could be matched by media types
$acceptHeader = AcceptHeader::parse(
'type1/subtype1;q=1.0, type1/subtype1;ext=ext1;q=0.8, */*;q=0.1'
);
$contentTypeHeader = Header::parse(
'type1/subtype1',
HeaderInterface::HEADER_CONTENT_TYPE
);
$matcher->matchEncoder($acceptHeader);
$encoder = $matcher->getEncoder();
$type = $matcher->getEncoderHeaderMatchedType();
$matcher->matchDecoder($contentTypeHeader);
$decoder = $matcher->getDecoder();
$type = $matcher->getDecoderHeaderMatchedType();
According to specification servers have some responsibilities to respond with 406 and 415 HTTP codes in some cases. The package provides with implementations for HeadersCheckerInterface
and QueryCheckerInterface
which accept ParametersInterface
and validate it against negotiation requirements and allowed headers and parameters for include paths, sparse field sets, sorting, pagination and filtering.
$factory = new Factory();
$allowUnrecognised = true;
$includePaths = ['author', 'comments'];
$fieldSetTypes = ['header', 'created-at', 'updated-at'];
$sortParameters = ['created-at', 'updated-at'];
$pagingParameters = ['first', 'last'];
$filteringParameters = ['header', 'body', 'created-at', 'updated-at'];
$checker = $factory->createParametersChecker(
$matcher,
$allowUnrecognised,
$includePaths,
$fieldSetTypes,
$sortParameters,
$pagingParameters,
$filteringParameters
);
// throws JsonApiException if validtion fails
$checker->check($parameters);
JSON API has a variety of possible HTTP responses. The package provides \Neomerx\JsonApi\Http\Responses
class which is designed to be combined with Encoder
, Parameters
, MediaType
and provide easy data transformation to HTTP Response
.
Integration sample
$parameters = $this->getRequestParameters();
$matcher = $this->getCodecMatcher();
$matcher->matchEncoder($parameters->getAcceptHeader());
$responses = new Responses(
$parameters,
$matcher->getEncoderRegisteredMatchedType(),
$this->getSupportedExtensions(),
$matcher->getEncoder(),
$this->getSchemaContainer(),
$this->getEncoderOptions()->getUrlPrefix()
);
// response for newly created resource with
// HTTP code 201 + 'location' header
$response = $responses->getCreatedResponse($newAuthor);