Add documentation for Messages

This commit is contained in:
PJ Dietz 2015-05-30 19:06:16 -04:00
parent 375ba819ef
commit a8b3ce9829
3 changed files with 308 additions and 1 deletions

View File

@ -103,6 +103,7 @@ Contents
overview
getting-started
messages
middleware
router
uri-templates

307
docs/source/messages.rst Normal file
View File

@ -0,0 +1,307 @@
Messages and PSR-7
==================
WellRESTed uses PSR-7_ as the interfaces for HTTP messages. This section provides an introduction to working with these interfaces and the implementations provided with WellRESTed. For more information, please read PSR-7_.
Obtaining Instances
-------------------
When working with middleware_, you generally will not need to create requests and responses yourself, as these are passed into the middleware when it is dispatched.
In `Getting Started`_, we saw that middleware looks like this:
.. code-block:: php
/**
* @param Psr\Http\Message\ServerRequestInterface $request
* @param Psr\Http\Message\ResponseInterface $response
* @param callable $next
* @return Psr\Http\Message\ResponseInterface
*/
function ($request, $response, $next) { }
When middleware is called, it receives a ``Psr\Http\Message\ServerRequestInterface`` instance representing the client's request and a ``Psr\Http\Message\ResponseInterface`` instance that serves as a starting place for the response to output to the client. These instances are created by the ``WellRESTed\Server`` when you call ``WellRESTed\Server::respond``.
.. note::
If you want to provide your own custom request and response (either to adjust the initial settings or to use a different implementation), you can do so by passing request and response instances as the first and second parameters to ``WellRESTed\Server::respond``.
Requests
--------
The ``$request`` variable passed to middleware represents the request message sent by the client. Middleware can inspect this variable to read information such as the request path, method, query, headers, and body.
Let's start with a very simple GET request to the path ``/cats/?color=orange``.
.. code-block:: http
GET /cats/ HTTP/1.1
Host: example.com
Cache-control: no-cache
You can read information from the request in your middleware like this:
.. code-block:: php
function ($request, $response, $next) {
$path = $request->getRequestTarget();
// "/cats/?color=orange"
$method = $request->getMethod();
// "GET"
$query = $request->getQueryParams();
/*
Array
(
[color] => orange
)
*/
}
This example middleware shows that you can use:
- ``getRequestTarget()`` to read the path and query string for the request
- ``getMethod()`` to read the HTTP verb (e.g., GET, POST, OPTIONS, DELETE)
- ``getQueryParams()`` to read the query as an associative array
Let's move on to some more intersting features.
Headers
^^^^^^^
The request above also included a ``Cache-control: no-cache`` header. You can read this header a number of ways. The simplest way is with the ``getHeaderLine($name)`` method.
Call ``getHeaderLine($name)`` and pass the case-insensitive name of a header. The method will return the value for the header, or an empty string.
.. code-block:: php
function ($request, $response, $next) {
// This message contains a "Cache-control: no-cache" header.
$cacheControl = $request->getHeaderLine("cache-control");
// "no-cache"
// This message does not contain any authoriation headers.
$authoriziation = $request->getHeaderLine("authorization");
// ""
}
.. note::
All methods relating to headers treat header field name case insensitively.
Because HTTP messages may contain multiple headers with the same field name, ``getHeaderLine($name)`` has one other feature: If multiple headers with the same field name are present in the message, ``getHeaderLine($name)`` returns a string containing all of the values for that field, concatenated by commas. This is more common with responses, paricularaly with the ``Set-cookie`` header, but is still possible for requests.
You may also use ``hasHeader($name)`` to test if a header exists, ``getHeader($name)`` to receive an array of values for this field name, and ``getHeaders()`` to receive an associative array of headers where each key is a field name and each value is an array of field values.
Body
^^^^
PSR-7_ provides access to the body of the request as a stream and—when possible—as a parsed object or array. Let's start by looking at a request with form fields made available as an array.
Parsed Body
~~~~~~~~~~~
When the request contains form fields (i.e., the ``Content-type`` header is either ``application/x-www-form-urlencoded`` or ``multipart/form-data``), the request makes the form fields available via the ``getParsedBody`` method. This provides access to the fields without needing to rely on the ``$_POST`` superglobal.
Given this request:
.. code-block:: http
POST /cats/ HTTP/1.1
Host: example.com
Content-type: application/x-www-form-urlencoded
Content-length: 23
name=Molly&color=Calico
We can read the parsed body like this:
.. code-block:: php
function ($request, $response, $next) {
$cat = $request->getParsedBody();
/*
Array
(
[name] => Molly
[color] => calico
)
*/
}
Body Stream
~~~~~~~~~~~
For other content types, use the ``getBody`` method to get a stream containing the contents of request entity body.
Using a JSON representation of our cat, we can make a request like this:
.. code-block:: http
POST /cats/ HTTP/1.1
Host: example.com
Content-type: application/json
Content-length: 46
{
"name": "Molly",
"color": "Calico"
}
We can read and parse the JSON body, and even provide it **as** the parsedBody for later middleware like this:
.. code-block:: php
function ($request, $response, $next) {
$cat = json_decode((string) $request->getBody());
/*
stdClass Object
(
[name] => Molly
[color] => calico
)
*/
$request = $request->withParsedBody($cat);
}
Because the entity body of a request or response can be very large, PSR-7_ represents bodies as streams using the ``Psr\Htt\Message\StreamInterface`` (see PSR-7_ Section 1.3).
The JSON example cast the stream to a string, but we can also do things like copy the stream to a local file:
.. code-block:: php
function ($request, $response, $next) {
// Store the body to a temp file.
$chunkSize = 2048; // Number of bytes to read at once.
$localPath = tempnam(sys_get_temp_dir(), "body");
$h = fopen($localPath, "wb");
$body = $rqst->getBody();
while (!$body->eof()) {
fwrite($h, $body->read($chunkSize));
}
fclose($h);
}
Paramters
^^^^^^^^^
PSR-7_ eliminates the need to read from many of the superglobals. We already saw how ``getParsedBody`` takes the place of reading directly from ``$_POST`` and ``getQueryParams`` replaces reading from ``$_GET``. Here are some other ``ServerRequestInterface`` methods with **brief** descriptions. Please see PSR-7_ for full details, particularly for ``getUploadedFiles``.
.. list-table::
:header-rows: 1
* - Method
- Replaces
- Note
* - getServerParams
- $_SERVER
- Data related to the request environment
* - getCookieParams
- $_COOKIE
- Compatible with the structure of $_COOKIE
* - getQueryParams
- $_GET
- Deserialized query string arguments, if any
* - getParsedBody
- $_POST
- Request body as an object or array
* - getUploadedFiles
- $_FILES
- Normalized tree of file upload data
Attributes
^^^^^^^^^^
``ServerRequestInterface`` provides another useful feature called "attributes". Attributes are key-value pairs associated with the request that can be, well, pretty much anything.
The primary use for attributes in WellRESTed is to provide access to path variables when using `template routes`_ or `regex routes`_.
For example, the template route ``/cats/{name}`` matches routes such as ``/cats/Molly`` and ``/cats/Oscar``. When the route is dispatched, the router takes the portion of the actual request path matched by ``{name}`` and provides it as an attribute.
For a request to ``/cats/Rufus``:
.. code-block:: php
function ($request, $response, $next) {
$name = $request->getAttribute("name");
// "Rufus"
}
When calling ``getAttribute``, you can optionally provide a default value as the second argument. The value of this argument will be returned if the request has no attribute with that name.
.. code-block:: php
function ($request, $response, $next) {
// Request has no attribute "dog"
$name = $request->getAttribute("dog", "Bear");
// "Bear"
}
Middleware can also use attributes as a way to provide extra information to subsequent middleware. For example, an authorization middleware could obtain an object representing a user and store is as the "user" attribute which later middleware could read.
.. code-block:: php
$auth = function ($request, $response, $next) {
try {
$user = readUserFromCredentials($request);
} catch (NoCredentialsSupplied $e) {
return $response->withStatus(401);
} catch (UserNotAllowedHere $e) {
return $response->withStatus(403);
}
// Store this as an attribute.
$request = $request->withAttribute("user", $user);
// Call $next, passing the request with the added attribute.
return $next($request, $response);
};
$subsequent = function ($request, $response, $next) {
// Read the "user" attribute added by a previous middleware.
$user = $request->getAttribute("user");
// Do something with $user
}
$server = new \WellRESTed\Server();
$server->add($auth);
$server->add($subsequent); // Must be added AFTER $auth to get "user"
$server->respond();
Finally, attributes provide a nice way to provide a `dependency injection`_ container for to your middleware.
Requests
--------
Coming soon!
.. _PSR-7: http://www.php-fig.org/psr/psr-7/
.. _Getting Started: getting-started.html
.. _Middleware: middleware.html
.. _template routes: router.html#template-routes
.. _regex routes: router.html#regex-routes
.. _dependency injection: dependency-injection.html

View File

@ -47,4 +47,3 @@ Licensed using the `MIT license <http://opensource.org/licenses/MIT>`_.
THE SOFTWARE.
.. _Composer: http://getcomposer.org/
.. _PHP cURL: http://php.net/manual/en/book.curl.php