Update documentation for version 4.0
This commit is contained in:
parent
de46c8e089
commit
36b03b6ca2
|
|
@ -11,8 +11,12 @@ That being said, there are a number or situations that come up that warrant solu
|
||||||
`Error Handling`_
|
`Error Handling`_
|
||||||
Classes to facilitate error handling including
|
Classes to facilitate error handling including
|
||||||
|
|
||||||
|
`Test Components`_
|
||||||
|
Test cases and doubles for use with WellRESTed
|
||||||
|
|
||||||
Or, see WellRESTed_ on GitHub.
|
Or, see WellRESTed_ on GitHub.
|
||||||
|
|
||||||
.. _HTTP Exceptions: https://github.com/wellrestedphp/http-exceptions
|
.. _HTTP Exceptions: https://github.com/wellrestedphp/http-exceptions
|
||||||
.. _Error Handling: https://github.com/wellrestedphp/error-handling
|
.. _Error Handling: https://github.com/wellrestedphp/error-handling
|
||||||
|
.. _Test Components: https://github.com/wellrestedphp/test
|
||||||
.. _WellRESTed: https://github.com/wellrestedphp
|
.. _WellRESTed: https://github.com/wellrestedphp
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
Changes from Version 3
|
||||||
|
======================
|
||||||
|
|
||||||
|
If your project uses WellRESTed version 3, you can most likely upgrade to to version 4 without making any changes to your code. However, there are a few changes that may affect some users.
|
||||||
|
|
||||||
|
Unhandled Requests
|
||||||
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In version 3, when a router fails to match the route for a request, the router returns a response with a 404 status code and stops delegating to upstream middleware. Version 4 changes this to allow for multiple routers. In verson 4, when a router fails to match a route, it sends the request up to the next middleware to give it a change to handle the request.
|
||||||
|
|
||||||
|
The server now provides the mechanism for responding with a 404 error when no handlers handle the request. This occurs when a request is dispatched all through through the server's middleware stack.
|
||||||
|
|
||||||
|
For most applications, this should not cause a problem. However, if your application uses "double pass" middleware—such as legacy ``WellRESTed\MiddlewareInterface`` implementations—and your handlers call ``$next`` after assembling the handled response, you will need to make adjustments. Return the response without calling ``$next`` in these handlers to avoid returning a 404 response.
|
||||||
|
|
||||||
|
Server Configuration
|
||||||
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Version 4 allows for easier customization of the server than version 3. Previously, to customize the Server, you would need subclass Server and override protected methods that provided a default request, response, transmitter, etc. The Server in version 4 now provides the following setters for providing custom behaviour:
|
||||||
|
|
||||||
|
- ``setAttributes(array $attributes)``
|
||||||
|
- ``setDispatcher(DispatcherInterface $dispatcher)``
|
||||||
|
- ``setPathVariablesAttributeName(string $name)``
|
||||||
|
- ``setRequest(ServerRequestInterface $request)``
|
||||||
|
- ``setResponse(ResponseInterface $response)``
|
||||||
|
- ``setTransmitter(TransmitterInterface $transmitter)``
|
||||||
|
- ``setUnhandledResponse(ResponseInterface $response)``
|
||||||
|
|
@ -26,8 +26,8 @@ master_doc = 'index'
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = u'WellRESTed'
|
project = u'WellRESTed'
|
||||||
copyright = u'2018, PJ Dietz'
|
copyright = u'2018, PJ Dietz'
|
||||||
version = '3.1.0'
|
version = '4.0.0'
|
||||||
release = '3.1.0'
|
release = '4.0.0'
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,12 @@ We can register the handler and these dependencies in a Pimple_ service provider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
To register this handler with a router, we can pass the service:
|
||||||
|
|
||||||
|
.. code-block:: php
|
||||||
|
|
||||||
|
$router->register('GET', '/foo', $c['fooHandler']);
|
||||||
|
|
||||||
By "protecting" the ``fooHandler`` service, we are delaying the instantiation of the ``FooHandler``, the ``Bar``, and the ``Baz`` until the handler needs to be dispatched. This works because we're not passing instance of ``FooHandler`` when we register this with a router, we're passing a function to it that does the instantiation on demand.
|
By "protecting" the ``fooHandler`` service, we are delaying the instantiation of the ``FooHandler``, the ``Bar``, and the ``Baz`` until the handler needs to be dispatched. This works because we're not passing instance of ``FooHandler`` when we register this with a router, we're passing a function to it that does the instantiation on demand.
|
||||||
|
|
||||||
.. _Pimple: https://pimple.symfony.com/
|
.. _Pimple: https://pimple.symfony.com/
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ Custom Dispatcher
|
||||||
|
|
||||||
Wrapping works well when you have one or two handlers implementing a third-party interface. If you want to integrate a lot of classes that implement a given third-party interface, you're might consider customizing the dispatcher.
|
Wrapping works well when you have one or two handlers implementing a third-party interface. If you want to integrate a lot of classes that implement a given third-party interface, you're might consider customizing the dispatcher.
|
||||||
|
|
||||||
The dispatcher is an instance that unpacks your handlers and middleware and sends the request and response through it. A default dispatcher is created for you when you instantiate your ``WellRESTed\Server`` (without passing the second argument). The server instantiates a ``WellRESTed\Dispatching\Dispatcher`` which is capable of running handlers and middleware as described in the `Handlers and Middleware`_.
|
The dispatcher is an instance that unpacks your handlers and middleware and sends the request and response through it. A default dispatcher is created for you when you use your ``WellRESTed\Server``.
|
||||||
|
|
||||||
If you need the ability to dispatch other types of middleware, you can create your own by implementing ``WellRESTed\Dispatching\DispatcherInterface``. The easiest way to do this is to subclass ``WellRESTed\Dispatching\Dispatcher``. Here's an example that extends ``Dispatcher`` and adds support for ``OtherHandlerInterface``:
|
If you need the ability to dispatch other types of middleware, you can create your own by implementing ``WellRESTed\Dispatching\DispatcherInterface``. The easiest way to do this is to subclass ``WellRESTed\Dispatching\Dispatcher``. Here's an example that extends ``Dispatcher`` and adds support for ``OtherHandlerInterface``:
|
||||||
|
|
||||||
|
|
@ -86,59 +86,25 @@ If you need the ability to dispatch other types of middleware, you can create yo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
To use this dispatcher, pass it to the constructor of ``WellRESTed\Server`` as the second argument. (The first argument is a hash array to use as `request attributes`_.)
|
To use this dispatcher, create an instance implementing ``WellRESTed\Dispatching\DispatcherInterface`` and pass it to the server's ``setDispatcher`` method.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
// Create an instance of your custom dispatcher.
|
$server = new WellRESTed\Server();
|
||||||
$dispatcher = new MyApi\CustomDispatcher;
|
$server->setDispatcher(new MyApi\CustomDispatcher());
|
||||||
|
|
||||||
// Pass this dispatcher to the server.
|
|
||||||
$server = new WellRESTed\Server(null, $dispatcher);
|
|
||||||
|
|
||||||
// Now, you can add any handlers implementing OtherHandlerInterface
|
|
||||||
$other = new OtherHandler();
|
|
||||||
$server->add($other);
|
|
||||||
|
|
||||||
Message Customization
|
Message Customization
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
In the example above, we passed a custom dispatcher to the server. You can also customize your server in other ways. For example, if you have a different implementation of PSR-7_ messages that you prefer, you can pass them into the ``Server::respond`` method:
|
In the example above, we passed a custom dispatcher to the server. You can also customize your server in other ways. For example, when the server reaches these end of its stack of middleware and has not had the response handled, it returns a blank 404 error response. You can customize this by passing a response to the server's ``setUnhandledResponse`` method.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
// Represents the request submitted by the client.
|
$unhandled = (new Response(404))
|
||||||
$request = new ThirdParty\Request();
|
->withHeader('text/html')
|
||||||
// A "blank" response.
|
->withBody($fancy404message);
|
||||||
$response = new ThirdParty\Response();
|
|
||||||
|
|
||||||
$server = new WellRESTed\Server();
|
$server->setUnhandledResponse($unhandled);
|
||||||
// ...add middleware and handlers...
|
|
||||||
|
|
||||||
// Pass your request and response to Server::respond
|
|
||||||
$server->response($request, $response);
|
|
||||||
|
|
||||||
Even if you don't want to use a different implementation, you may still find a reason to provide you're own messages. For example, the default response status code for a ``WellRESTed\Message\Response`` is 500. If you wanted to make the default 200 instead, you could do something like this:
|
|
||||||
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
// The first argument is the status code.
|
|
||||||
$response = new \WellRESTed\Message\Response(200);
|
|
||||||
|
|
||||||
$server = new \WellRESTed\Server();
|
|
||||||
// ...add middleware...
|
|
||||||
|
|
||||||
// Pass the response to respond()
|
|
||||||
$server->respond(null, $response);
|
|
||||||
|
|
||||||
Server Customization
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
As an alternative to passing you preferred request and response instances into ``Server::respond``, you can extend ``Server`` to obtain default values from a different source.
|
|
||||||
|
|
||||||
Classes such as ``Server`` that create dependencies as defaults keep the instantiation isolated in easy-to-override methods. For example, ``Server`` has a protected method ``getResponse`` that instantiates and returns a new response. You can easily replace this method with your own that returns the default response of your choice.
|
|
||||||
|
|
||||||
In addition to the messages, you can do similar customization for other ``Server`` dependencies such as the dispatcher (see above), the transmitter (which writes the response out to the client), and the routers that are created with ``Server::createRouter``. These dependencies are instantiated in isolated methods as with the request and response to make this sort of customization easy, and other classes such as ``Router`` use this pattern as well.
|
|
||||||
|
|
||||||
.. _PSR-7: https://www.php-fig.org/psr/psr-7/
|
.. _PSR-7: https://www.php-fig.org/psr/psr-7/
|
||||||
.. _Handlers and Middleware: handlers-and-middleware.html
|
.. _Handlers and Middleware: handlers-and-middleware.html
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,9 @@ Routes can be static (like the one above that matches only ``/hello``), or they
|
||||||
Middleware
|
Middleware
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
||||||
In addition to handlers, WellRESTed also supports middlware. Middleware allows you to compose your application in multiple pieces. In the example, we'll use middleware to add a header to every responce, regardless of which handler is called.
|
In addition to handlers, which provide responses directly, WellRESTed also supports middlware to act on the requests and then pass them on for other middleware or handlers to work with.
|
||||||
|
|
||||||
|
Middleware allows you to compose your application in multiple pieces. In the example, we'll use middleware to add a header to every responce, regardless of which handler is called.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
|
@ -145,7 +147,7 @@ In addition to handlers, WellRESTed also supports middlware. Middleware allows y
|
||||||
// Create a server
|
// Create a server
|
||||||
$server = new Server();
|
$server = new Server();
|
||||||
|
|
||||||
// Add the header adding middleware to the server first so that it will
|
// Add the header-adding middleware to the server first so that it will
|
||||||
// forward requests on to the router.
|
// forward requests on to the router.
|
||||||
$server->add(new CustomHeaderMiddleware());
|
$server->add(new CustomHeaderMiddleware());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ You may also use a ``callable`` similar to the legacy ``WellRESTed\MiddlewareInt
|
||||||
Using Handlers and Middleware
|
Using Handlers and Middleware
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Methods that accept handlers and middleware (e.g., ``Server::add``, ``Router::register``) allow you to provide them in a number of ways. For example, you can provide an instance, a ``callable`` that provides an instance, or an ``array`` of handlers to use in sequence. The following examples will demonstrate all of the ways you can register handlers and middleware.
|
Methods that accept handlers and middleware (e.g., ``Server::add``, ``Router::register``) allow you to provide them in a number of ways. For example, you can provide an instance, a ``callable`` that returns an instance, or an ``array`` of middleware to use in sequence. The following examples will demonstrate all of the ways you can register handlers and middleware.
|
||||||
|
|
||||||
Factory Functions
|
Factory Functions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
@ -183,7 +183,7 @@ If you're using ``Pimple``, a popular `dependency injection`_ container for PHP,
|
||||||
Instance
|
Instance
|
||||||
--------
|
--------
|
||||||
|
|
||||||
WellRESTed also allows you to pass an instance of a handler directly. This may be useful for smaller handlers that don't require many dependencies, although it is generally better to use the factory callable approach.
|
WellRESTed also allows you to pass an instance of a handler directly. This may be useful for smaller handlers that don't require many dependencies, although the factory function approach is better in most cases.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,12 @@ The middleware_ system allows you to build your Web service out of discrete, mod
|
||||||
Lazy Loading
|
Lazy Loading
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
Handlers and middleware can be registered using `factory functions`_ so that they are only instantiated if needed. This way, a Web service with hundreds of handlers and middleware still only creates instances required for the current request-response cycle.
|
Handlers and middleware can be registered using `factory functions`_ so that they are only instantiated if needed. This way, a Web service with hundreds of handlers and middleware only creates instances required for the current request-response cycle.
|
||||||
|
|
||||||
Extensible
|
Extensible
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
||||||
All classes are coded to interfaces to allow you to provide your own implementations and use them in place of the built-in classes. For example, if your Web service needs to be able to dispatch middleware that implements a different interface, you can provide your own custom ``DispatcherInterface`` implementation.
|
Most classes are coded to interfaces to allow you to provide your own implementations and use them in place of the built-in classes. For example, if your Web service needs to be able to dispatch middleware that implements a third-party interface, you can provide your own custom ``DispatcherInterface`` implementation.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
@ -138,6 +138,7 @@ Contents
|
||||||
dependency-injection
|
dependency-injection
|
||||||
additional
|
additional
|
||||||
web-server-configuration
|
web-server-configuration
|
||||||
|
changes-from-version-3
|
||||||
|
|
||||||
.. _PSR-7: https://www.php-fig.org/psr/psr-7/
|
.. _PSR-7: https://www.php-fig.org/psr/psr-7/
|
||||||
.. _PSR-15: https://www.php-fig.org/psr/psr-15/
|
.. _PSR-15: https://www.php-fig.org/psr/psr-15/
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
Messages and PSR-7
|
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 about PSR-7_.
|
WellRESTed uses the PSR-7_ 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 about PSR-7_.
|
||||||
|
|
||||||
Requests
|
Requests
|
||||||
--------
|
--------
|
||||||
|
|
@ -12,7 +12,7 @@ Let's start with a very simple GET request to the path ``/cats/?color=orange``.
|
||||||
|
|
||||||
.. code-block:: http
|
.. code-block:: http
|
||||||
|
|
||||||
GET /cats/ HTTP/1.1
|
GET /cats/?color=orange HTTP/1.1
|
||||||
Host: example.com
|
Host: example.com
|
||||||
Cache-control: no-cache
|
Cache-control: no-cache
|
||||||
|
|
||||||
|
|
@ -46,8 +46,6 @@ This example shows that you can use:
|
||||||
- ``getMethod()`` to read the HTTP verb (e.g., GET, POST, OPTIONS, DELETE)
|
- ``getMethod()`` to read the HTTP verb (e.g., GET, POST, OPTIONS, DELETE)
|
||||||
- ``getQueryParams()`` to read the query as an associative array
|
- ``getQueryParams()`` to read the query as an associative array
|
||||||
|
|
||||||
Let's move on to some more interesting features.
|
|
||||||
|
|
||||||
Headers
|
Headers
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
|
|
@ -73,7 +71,7 @@ Call ``getHeaderLine($name)`` and pass the case-insensitive name of a header. Th
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
All methods relating to headers treat header field name case insensitively.
|
All methods relating to headers treat header field names 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, particularly with the ``Set-cookie`` header, but is still possible for requests.
|
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, particularly with the ``Set-cookie`` header, but is still possible for requests.
|
||||||
|
|
@ -89,7 +87,7 @@ PSR-7_ provides access to the body of the request as a stream and—when possibl
|
||||||
Parsed Body
|
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.
|
For POST requests for forms (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:
|
Given this request:
|
||||||
|
|
||||||
|
|
@ -168,7 +166,7 @@ We can read and parse the JSON body, and even provide it as the parsedBody for l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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).
|
Because the entity body of a request or response can be very large, PSR-7_ represents bodies as streams using the ``Psr\Http\Message\StreamInterface`` (see `PSR-7 Section 1.3`_).
|
||||||
|
|
||||||
The JSON example casts the stream to a string, but we can also do things like copy the stream to a local file:
|
The JSON example casts the stream to a string, but we can also do things like copy the stream to a local file:
|
||||||
|
|
||||||
|
|
@ -187,7 +185,7 @@ The JSON example casts the stream to a string, but we can also do things like co
|
||||||
Parameters
|
Parameters
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
|
|
||||||
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``.
|
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::
|
.. list-table::
|
||||||
:header-rows: 1
|
:header-rows: 1
|
||||||
|
|
@ -247,7 +245,7 @@ Middleware can also use attributes as a way to provide extra information to subs
|
||||||
): ResponseInterface
|
): ResponseInterface
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user = readUserFromCredentials($request);
|
$user = $this->readUserFromCredentials($request);
|
||||||
} catch (NoCredentialsSupplied $e) {
|
} catch (NoCredentialsSupplied $e) {
|
||||||
return $response->withStatus(401);
|
return $response->withStatus(401);
|
||||||
} catch (UserNotAllowedHere $e) {
|
} catch (UserNotAllowedHere $e) {
|
||||||
|
|
@ -257,8 +255,7 @@ Middleware can also use attributes as a way to provide extra information to subs
|
||||||
// Store this as an attribute.
|
// Store this as an attribute.
|
||||||
$request = $request->withAttribute("user", $user);
|
$request = $request->withAttribute("user", $user);
|
||||||
|
|
||||||
// Call the next handler, passing the request with the added attribute.
|
// Delegate to the handler, passing the request with the "user" attribute.
|
||||||
// Send the request to the next handler.
|
|
||||||
return $handler->handle($request);
|
return $handler->handle($request);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -318,8 +315,7 @@ Provide the status code for your response with the ``withStatus`` method. When y
|
||||||
|
|
||||||
The "reason phrase" is the text description of the status that appears in the status line of the response. The "status line" is the very first line in the response that appears before the first header.
|
The "reason phrase" is the text description of the status that appears in the status line of the response. The "status line" is the very first line in the response that appears before the first header.
|
||||||
|
|
||||||
|
Although the PSR-7_ ``ResponseInterface::withStatus`` method accepts the reason phrase as an optional second parameter, you generally shouldn't pass anything unless you are using a non-standard status code. (And you probably shouldn't be using a non-standard status code.)
|
||||||
Although the PSR-7_ ``ResponseInterface::withStatus`` method accepts the reason phrase as an optional second parameter, you generally shouldn't pass anything unless you are using a non-standard status code. (And you probably shouldn't be using a non-standard status code.)
|
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
|
@ -336,19 +332,19 @@ Although the PSR-7_ ``ResponseInterface::withStatus`` method accepts the reason
|
||||||
Headers
|
Headers
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
||||||
Use the ``withHeader`` method to add a header to a response. ``withHeader`` will add the header if not already set, or replace the value of an existing header with that name.
|
Use the ``withHeader`` method to add a header to a response. ``withHeader`` will add the header if not already set, or replace the value of an existing header with the same name.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
// Add a "Content-type" header.
|
// Add a "Content-type" header.
|
||||||
$response = $response->withHeader("Content-type", "text/plain");
|
$response = $response->withHeader("Content-type", "text/plain");
|
||||||
$response->getHeaderLine("Content-type");
|
$response->getHeaderLine("Content-type");
|
||||||
// text/plain
|
// "text/plain"
|
||||||
|
|
||||||
// Calling withHeader a second time updates the value.
|
// Calling withHeader a second time updates the value.
|
||||||
$response = $response->withHeader("Content-type", "text/html");
|
$response = $response->withHeader("Content-type", "text/html");
|
||||||
$response->getHeaderLine("Content-type");
|
$response->getHeaderLine("Content-type");
|
||||||
// text/html
|
// "text/html"
|
||||||
|
|
||||||
To set multiple values for a given header field name (e.g., for ``Set-cookie`` headers), call ``withAddedHeader``. ``withAddedHeader`` adds the new header without altering existing headers with the same name.
|
To set multiple values for a given header field name (e.g., for ``Set-cookie`` headers), call ``withAddedHeader``. ``withAddedHeader`` adds the new header without altering existing headers with the same name.
|
||||||
|
|
||||||
|
|
@ -438,6 +434,7 @@ Each PSR-7_ message MUST have a body, so there's no ``withoutBody`` method. You
|
||||||
|
|
||||||
.. _HTTP Status Code Registry: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
.. _HTTP Status Code Registry: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
|
||||||
.. _PSR-7: http://www.php-fig.org/psr/psr-7/
|
.. _PSR-7: http://www.php-fig.org/psr/psr-7/
|
||||||
|
.. _PSR-7 Section 1.3: https://www.php-fig.org/psr/psr-7/#13-streams
|
||||||
.. _Getting Started: getting-started.html
|
.. _Getting Started: getting-started.html
|
||||||
.. _Middleware: middleware.html
|
.. _Middleware: middleware.html
|
||||||
.. _template routes: router.html#template-routes
|
.. _template routes: router.html#template-routes
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ The recommended method for installing WellRESTed is to use Composer_. Add an ent
|
||||||
|
|
||||||
{
|
{
|
||||||
"require": {
|
"require": {
|
||||||
"wellrested/wellrested": "~3.1"
|
"wellrested/wellrested": "^4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
Router
|
Router
|
||||||
======
|
======
|
||||||
|
|
||||||
A router is a type of handler that organizes the components of a site by associating HTTP methods and paths with other handler and middleware. When the router receives a request, it examines the path components of the request's URI, determines which "route" matches, and dispatches the associated handler. The dispatched handler is then responsible for reacting to the request and providing a response.
|
A router is a type of middleware that organizes the components of a site by associating HTTP methods and paths with handlers and middleware. When the router receives a request, it examines the path components of the request's URI, determines which "route" matches, and dispatches the associated handler. The dispatched handler is then responsible for reacting to the request and providing a response.
|
||||||
|
|
||||||
Basic Usage
|
Basic Usage
|
||||||
^^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
@ -13,7 +13,7 @@ Typically, you will want to use the ``WellRESTed\Server::createRouter`` method t
|
||||||
$server = new WellRESTed\Server();
|
$server = new WellRESTed\Server();
|
||||||
$router = $server->createRouter();
|
$router = $server->createRouter();
|
||||||
|
|
||||||
Suppose ``$catHandler`` is a middleware that you want to dispatch whenever a client makes a ``GET`` request to the path ``/cats/``. Use the ``register`` method map it to that path and method.
|
Suppose ``$catHandler`` is a handler that you want to dispatch whenever a client makes a ``GET`` request to the path ``/cats/``. Use the ``register`` method map it to that path and method.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
|
|
@ -111,7 +111,6 @@ For a request to ``/cats/molly-90``:
|
||||||
[1] => molly
|
[1] => molly
|
||||||
[number] => 12
|
[number] => 12
|
||||||
[2] => 12
|
[2] => 12
|
||||||
... Plus any other attributes that were set ...
|
|
||||||
)
|
)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -124,7 +123,6 @@ A router will often contain many routes, and sometimes more than one route will
|
||||||
#. If one prefix route matches the beginning of the path, dispatch it.
|
#. If one prefix route matches the beginning of the path, dispatch it.
|
||||||
#. If multiple prefix routes match, dispatch the longest matching prefix route.
|
#. If multiple prefix routes match, dispatch the longest matching prefix route.
|
||||||
#. Inspect each pattern route (template and regular expression) in the order in which they were added to the router. Dispatch the first route that matches.
|
#. Inspect each pattern route (template and regular expression) in the order in which they were added to the router. Dispatch the first route that matches.
|
||||||
#. If no pattern routes match, return a response with a ``404 Not Found`` status.
|
|
||||||
|
|
||||||
Static vs. Prefix
|
Static vs. Prefix
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
@ -167,7 +165,7 @@ Given these routes:
|
||||||
->register("GET", "/dogs/*", $prefix);
|
->register("GET", "/dogs/*", $prefix);
|
||||||
->register("GET", "/dogs/{group}/{breed}", $pattern);
|
->register("GET", "/dogs/{group}/{breed}", $pattern);
|
||||||
|
|
||||||
``$pattern`` will never be dispatched because any route that matches ``/dogs/{group}/{breed}`` also matches ``/dogs/*``, and prefix routes have priority over pattern routes.
|
``$pattern`` will **never** be dispatched because any route that matches ``/dogs/{group}/{breed}`` also matches ``/dogs/*``, and prefix routes have priority over pattern routes.
|
||||||
|
|
||||||
Pattern vs. Pattern
|
Pattern vs. Pattern
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
@ -192,7 +190,7 @@ This will **NOT** work:
|
||||||
// Matches only when the variables are digits.
|
// Matches only when the variables are digits.
|
||||||
$router->register("GET", "~/dogs/([0-9]+)/([0-9]+)", $numbers);
|
$router->register("GET", "~/dogs/([0-9]+)/([0-9]+)", $numbers);
|
||||||
|
|
||||||
This is because ``/dogs/{group}/{breed}`` will match both ``/dogs/102/132`` and ``/dogs/herding/australian-shepherd``. If it is added to the router before the route for ``$numbers``, it will be dispatched before the route for ``$numbers`` is ever evaluated.
|
This is because ``/dogs/{group}/{breed}`` will match both ``/dogs/102/132`` **and** ``/dogs/herding/australian-shepherd``. If it is added to the router before the route for ``$numbers``, it will be dispatched before the route for ``$numbers`` is ever evaluated.
|
||||||
|
|
||||||
Methods
|
Methods
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
@ -284,38 +282,49 @@ A ``POST`` request to ``/cats/12`` will provide:
|
||||||
HTTP/1.1 405 Method Not Allowed
|
HTTP/1.1 405 Method Not Allowed
|
||||||
Allow: GET,PUT,DELETE,HEAD,OPTIONS
|
Allow: GET,PUT,DELETE,HEAD,OPTIONS
|
||||||
|
|
||||||
|
|
||||||
Error Responses
|
Error Responses
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
When a router is unable to dispatch a route because either the path or method does not match a defined route, it will provide an appropriate error response code—either ``404 Not Found`` or ``405 Method Not Allowed``.
|
Then a router is able to locate a route that matches the path, but that route doesn't support the request's method, the router will respond ``405 Method Not Allowed``.
|
||||||
|
|
||||||
The router always checks the path first. If route for that path matches, the router responds ``404 Not Found``.
|
When a router is unable to match the route, it will delegate to the next middleware.
|
||||||
|
|
||||||
If the router is able to locate a route that matches the path, but that route doesn't support the request's method, the router will respond ``405 Method Not Allowed``.
|
.. note::
|
||||||
|
|
||||||
Given this router:
|
When no route matches, the Router will delegate to the next middleware in the server. This is a change from previous versions of WellRESTed where there Router would return a 404 Not Found reponse. This new behaviour allows a servers to have multiple routers.
|
||||||
|
|
||||||
|
Router-specific Middleware
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
WellRESTed version 4 allows a Router to have a set of middleware to dispatch whenever it finds a route that matches. This middleware runs before the handler for the matched route, and only if a route matches.
|
||||||
|
|
||||||
|
This feature allows you to build a site where some sections use certain middleware and other do not. For example, suppose your site has a public section that does not require authentication and a section that does require authentication. We can use a different router for each section, and provide authentication middleware on only the router for the private area.
|
||||||
|
|
||||||
.. code-block:: php
|
.. code-block:: php
|
||||||
|
|
||||||
$router
|
$server = new Server();
|
||||||
->register("GET", "/cats/", $catReader)
|
|
||||||
->register("POST", "/cats/", $catWriter)
|
|
||||||
->register("GET", "/dogs/", $catItemReader)
|
|
||||||
|
|
||||||
The following requests wil provide these responses:
|
// Add the "public" section.
|
||||||
|
$public = $server->createRouter();
|
||||||
|
$public->register('GET', '/', $homeHandler);
|
||||||
|
$public->register('GET', '/about', $homeHandler);
|
||||||
|
$server->add($public);
|
||||||
|
|
||||||
====== ========== ========
|
// Add the "private" section.
|
||||||
Method Path Response
|
$private = $server->createRouter();
|
||||||
====== ========== ========
|
// Authorizaiton middleware checks for an Authorization header and
|
||||||
GET /hamsters/ 404 Not Found
|
// responds 401 when the header is missing or invalid.
|
||||||
PUT /cats/ 405 Method Not Allowed
|
$private->addMiddleware($authorizaitonMiddleware);
|
||||||
====== ========== ========
|
$private->register('GET', '/secret', $secretHandler);
|
||||||
|
$private->register('GET', '/members-only', $otherHandler);
|
||||||
|
$server->add($private);
|
||||||
|
|
||||||
|
$server->respond();
|
||||||
|
|
||||||
Nested Routers
|
Nested Routers
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
For large Web services with large numbers of endpoints, a single, monolithic router may not to optimal. To avoid having each request test every pattern-based route, you can break up a router into sub-routers.
|
For large Web services with large numbers of endpoints, a single, monolithic router may not to optimal. To avoid having each request test every pattern-based route, you can break up a router into a hierarchy of routers.
|
||||||
|
|
||||||
Here's an example where all of the traffic beginning with ``/cats/`` is sent to one router, and all the traffic for endpoints beginning with ``/dogs/`` is sent to another.
|
Here's an example where all of the traffic beginning with ``/cats/`` is sent to one router, and all the traffic for endpoints beginning with ``/dogs/`` is sent to another.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,11 @@ To read a path variable, router inspects the request attribute named ``"id"``, s
|
||||||
|
|
||||||
// For a request to /widgets/12
|
// For a request to /widgets/12
|
||||||
$id = $request->getAttribute("id");
|
$id = $request->getAttribute("id");
|
||||||
// 12
|
// "12"
|
||||||
|
|
||||||
// For a request to /widgets/mega-widget
|
// For a request to /widgets/mega-widget
|
||||||
$id = $request->getAttribute("id");
|
$id = $request->getAttribute("id");
|
||||||
// mega-widget
|
// "mega-widget"
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
|
|
@ -36,7 +36,7 @@ To read a path variable, router inspects the request attribute named ``"id"``, s
|
||||||
Multiple Variables
|
Multiple Variables
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
The example above included one variable, but URI Templates may include multiple variables. Each variable will be provided as a request attribute, so be sure to give your variables unique names.
|
The example above included one variable, but URI Templates may include multiple. Each variable will be provided as a request attribute, so be sure to give your variables unique names.
|
||||||
|
|
||||||
Here's an example with a handful of variables. Suppose we have a template describing the path for a user's avatar image. The image is identified by a username and the image dimensions.
|
Here's an example with a handful of variables. Suppose we have a template describing the path for a user's avatar image. The image is identified by a username and the image dimensions.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,5 +54,29 @@ $router = $server->createRouter();
|
||||||
$router->register("GET", "/", new HomePageHandler());
|
$router->register("GET", "/", new HomePageHandler());
|
||||||
$server->add($router);
|
$server->add($router);
|
||||||
|
|
||||||
|
|
||||||
|
$server->add($server->createRouter()
|
||||||
|
->register('GET, POST', '/cat', function ($rqst, $resp, $next) {
|
||||||
|
|
||||||
|
$resp = $resp
|
||||||
|
->withStatus(200)
|
||||||
|
->withHeader('Content-type', 'text/html')
|
||||||
|
->withBody(new Stream('Molly'));
|
||||||
|
return $next($rqst, $resp);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$server->add($server->createRouter()
|
||||||
|
->register('GET', '/cat', function ($rqst, $resp) {
|
||||||
|
|
||||||
|
$body = (string) $resp->getBody();
|
||||||
|
|
||||||
|
|
||||||
|
return (new Response(200))
|
||||||
|
->withHeader('Content-type', 'text/html')
|
||||||
|
->withBody(new Stream($body . ' Oscar'));
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Read the request from the client, dispatch a handler, and output.
|
// Read the request from the client, dispatch a handler, and output.
|
||||||
$server->respond();
|
$server->respond();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue