Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added en/_static/img/middleware-request.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added en/_static/img/middleware-setup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 15 additions & 6 deletions en/appendices/3-5-migration-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ behavior that may affect your application:
New Features
============

Scoped Middleware
-----------------

Middleware can now be conditionally applied to routes in specific URL
scopes. This allows you to build specific stacks of middleware for different
parts of your application without having to write URL checking code in your
middleware. See the :ref:`connecting-scoped-middleware` section for more
information.

New Console Runner
------------------

Expand All @@ -146,13 +155,13 @@ how they are named and how the shells get their dependencies. Adopting this new
class requires replacing the contents of your ``bin/cake.php`` file with the
`following file <https://github.com/cakephp/app/tree/3.next/bin/cake.php>`_.

Cache
-----
Cache Engine Fallbacks
----------------------

* Cache engines can now be configured with a ``fallback`` key that defines a
cache configuration to fall back to if the engine is misconfigured (or
unavailable). See :ref:`cache-configuration-fallback` for more information on
configuring fallbacks.
Cache engines can now be configured with a ``fallback`` key that defines a
cache configuration to fall back to if the engine is misconfigured (or
unavailable). See :ref:`cache-configuration-fallback` for more information on
configuring fallbacks.

Core
----
Expand Down
79 changes: 52 additions & 27 deletions en/controllers/middleware.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,34 @@ Middleware
##########

Middleware objects give you the ability to 'wrap' your application in re-usable,
composable layers of Request handling, or response building logic. Middleware
are part of the new HTTP stack in CakePHP that leverages the PSR-7 request and
response interfaces. By leveraging the PSR-7 standard you can use any PSR-7
compatible middleware available on `The Packagist <https://packagist.org>`__.
composable layers of Request handling, or response building logic. Visually,
your application ends up at the center, and middleware is wrapped aroud the app
like an onion. Here we can see an application wrapped with Routes, Assets,
Exception Handling and CORS header middleware.

CakePHP provides several middleware out of the box:
.. image:: /_static/img/middleware-setup.png

When a request is handled by your application it enters from the outermost
middleware. Each middleware can either delegate the request/response to the next
layer, or return a response. Returning a response prevents lower layers from
ever seeing the request. An example of that is the AssetMiddleware handling
a request for a plugin image during development.

.. image:: /_static/img/middleware-request.png

If no middleware take action to handle the request, a controller will be located
and have its action invoked, or an exception will be raised generating an error
page.

Middleware are part of the new HTTP stack in CakePHP that leverages the PSR-7
request and response interfaces. Because CakePHP is leveraging the PSR-7
standard you can use any PSR-7 compatible middleware available on `The Packagist
<https://packagist.org>`__.

Middleware in CakePHP
=====================

CakePHP provides several middleware to handle common tasks in web applications:

* ``Cake\Error\Middleware\ErrorHandlerMiddleware`` traps exceptions from the
wrapped middleware and renders an error page using the
Expand All @@ -32,11 +54,14 @@ CakePHP provides several middleware out of the box:
Using Middleware
================

You attach middleware in your ``App\Application`` class' ``middleware`` method.
If you don't have an ``App\Application`` class, see the section on
:ref:`adding-http-stack` for more information. Your application's ``middleware``
hook method will be called early in the request process, you can use the
``Middleware`` object to attach middleware::
Middleware can be applied to your application globally, or to individual
routing scopes.

To apply middleware to all requests, use the ``middleware`` method of your
``App\Application`` class. If you don't have an ``App\Application`` class, see
the section on :ref:`adding-http-stack` for more information. Your application's
``middleware`` hook method will be called at the beginning of the request
process, you can use the ``MiddlewareQueue`` object to attach middleware::

namespace App;

Expand All @@ -45,11 +70,11 @@ hook method will be called early in the request process, you can use the

class Application extends BaseApplication
{
public function middleware($middlewareStack)
public function middleware($middlewareQueue)
{
// Bind the error handler into the middleware queue.
$middlewareStack->add(new ErrorHandlerMiddleware());
return $middlewareStack;
$middlewareQueue->add(new ErrorHandlerMiddleware());
return $middlewareQueue;
}
}

Expand All @@ -59,27 +84,27 @@ a variety of operations::
$layer = new \App\Middleware\CustomMiddleware;

// Added middleware will be last in line.
$middlewareStack->add($layer);
$middlewareQueue->add($layer);

// Prepended middleware will be first in line.
$middlewareStack->prepend($layer);
$middlewareQueue->prepend($layer);

// Insert in a specific slot. If the slot is out of
// bounds, it will be added to the end.
$middlewareStack->insertAt(2, $layer);
$middlewareQueue->insertAt(2, $layer);

// Insert before another middleware.
// If the named class cannot be found,
// an exception will be raised.
$middlewareStack->insertBefore(
$middlewareQueue->insertBefore(
'Cake\Error\Middleware\ErrorHandlerMiddleware',
$layer
);

// Insert after another middleware.
// If the named class cannot be found, the
// middleware will added to the end.
$middlewareStack->insertAfter(
$middlewareQueue->insertAfter(
'Cake\Error\Middleware\ErrorHandlerMiddleware',
$layer
);
Expand All @@ -100,8 +125,8 @@ scripts, that add middleware::

EventManager::instance()->on(
'Server.buildMiddleware',
function ($event, $middlewareStack) {
$middlewareStack->add(new ContactPluginMiddleware());
function ($event, $middlewareQueueStack) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... this gave me some laughs... :)

So now it is sometimes middlewareQueue and sometimes middlewareQueueStack ;)?

Personally I'd rename the core class to MiddlewareStack and alias it from middlewareQueue to be removed in 4.0. Stack is easier to type and speak.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a mistake. I will get that fixed.

$middlewareQueueStack->add(new ContactPluginMiddleware());
});

PSR-7 Requests and Responses
Expand Down Expand Up @@ -253,14 +278,14 @@ application::

class Application
{
public function middleware($middlewareStack)
public function middleware($middlewareQueueStack)
{
// Add your simple middleware onto the queue
$middlewareStack->add(new TrackingCookieMiddleware());
$middlewareQueueStack->add(new TrackingCookieMiddleware());

// Add some more middleware onto the queue

return $middlewareStack;
return $middlewareQueueStack;
}
}

Expand Down Expand Up @@ -293,7 +318,7 @@ your application's middleware stack::
->noOpen()
->noSniff();

$middleware->add($headers);
$middlewareQueue->add($headers);

.. versionadded:: 3.5.0
The ``SecurityHeadersMiddleware`` was added in 3.5.0
Expand All @@ -316,7 +341,7 @@ Cookie data is encrypted with via OpenSSL using AES::
Configure::read('Security.cookieKey')
);

$middleware->add($cookies);
$middlewareQueue->add($cookies);

.. note::
It is recommended that the encryption key you use for cookie data, is used
Expand All @@ -337,13 +362,13 @@ CSRF protection can be applied to your entire application, or to specific scopes
by applying the ``CsrfProtectionMiddleware`` to your middleware stack::

use Cake\Http\Middleware\CsrfProtectionMiddleware;

$options = [
// ...
];
$csrf = new CsrfProtectionMiddleware($options);

$middleware->add($csrf);
$middlewareQueue->add($csrf);

Options can be passed into the middleware's constructor.
The available configuration options are:
Expand Down
64 changes: 54 additions & 10 deletions en/development/routing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -878,9 +878,13 @@ to do automatic view switching based on content types.
Connecting Scoped Middleware
----------------------------

Middleware can be applied to your entire application, or to an individual
routing scope. Before middleware can be applied to a scope, it needs to be
registered::
While Middleware can be applied to your entire application, applying middleware
to specific routing scopes offers more flexibility, as you can apply middleware
only where it is needed allowing your middleware to not concern itself with
how/where it is being applied.

Before middleware can be applied to a scope, it needs to be
registered into the route collection::

// in config/routes.php
use Cake\Http\Middleware\CsrfProtectionMiddleware;
Expand All @@ -891,21 +895,61 @@ registered::
$routes->registerMiddleware('cookies', new EncryptedCookiesMiddleware());
});

Once registered into the route builder, middleware can be applied to specific
Once registered, scoped middleware can be applied to specific
scopes::

$routes->scope('/cms', function ($routes) {
// Enable registered middleware for this scope.
// Enable CSRF & cookies middleware
$routes->applyMiddleware('csrf', 'cookies');
$routes->get('/articles/:action/*', ['controller' => 'Articles'])
});

In situations where you have nested scopes, inner scopes will inherit the
middleware applied in the containing scope::

$routes->scope('/api', function ($routes) {
$routes->applyMiddleware('ratelimit', 'auth.api');
$routes->scope('/v1', function ($routes) {
$routes->applyMiddleware('v1compat');
// Define routes here.
});
});

In the above example, the routes defined in ``/v1`` will have 'ratelimit',
'auth.api', and 'v1compat' middleware applied. If you re-open a scope, the
middleware applied to routes in each scope will be isolated::

$routes->scope('/blog', function ($routes) {
$routes->applyMiddleware('auth');
// Connect the authenticated actions for the blog here.
});
$routes->scope('/blog', function ($routes) {
// Connect the public actions for the blog here.
});

In the above example, the two uses of the ``/blog`` scope do not share
middleware. However, both of these scopes will inherit middleware defied in
their enclosing scopes.


Grouping Middleware
-------------------

To help keep your route code :abbr:`DRY (Do not Repeat Yourself)` middleware can
be combined into groups. Once combined groups can be applied like middleware
can::

$routes->registerMiddleware('cookie', new EncryptedCookieMiddleware());
$routes->registerMiddleware('auth', new AuthenticationMiddleware());
$routes->registerMiddleware('csrf', new CsrfProtectionMiddleware());
$routes->middlewareGroup('web', ['cookie', 'auth', 'csrf']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"web" seems a very general. How about "authenticated_site" or amything like that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like web, because it is not about auth here but about the context of middleware application.
On the web context you pass request/response through cookie, auth, csrf


// Apply the group
$routes->applyMiddleware('web');

In situations where you have nested scopes, all middleware applied in each scope
will be applied starting from the top-most scope and ending with the nested
scopes. By applying middleware in specific scopes you can omit complicated URL
matching logic out of your middleware layers and let them focus on their task.

.. versionadded:: 3.5.0
Scoped middleware support was added in 3.5.0
Scoped middleware & middleware groups were added in 3.5.0

.. _resource-routes:

Expand Down