Native support for middlewares
- Jan Tvrdík
- Nette guru | 2595
About middlewares
A middleware is an object which
- can modify request object (returns the modified object)
- can modify response object (returns the modified object)
- can return response early (by not calling the “next” callback)
See also Express.js documentation for more thorough explanation.
The most common interface is
callable(Request, Response, callable(Request): Response next): Response
However a simple interface without the 2nd response argument is considered better.
callable(Request, callable(Request): Response): Response
Real-world middleware examples (more examples)
Nette Workarounds
The closest simple alternative Nette has to middleware is IRouter interface which can be used to modify application request object. However IRouter cannot simply be used to modify response and stacking multiple IRouter objects to modify request object decreases link generation performance.
Events on Application can also be used in some cases.
Proposal
I would like to propose to add native support for middlewares to Application, maybe implementing presenter execution as middleware as well.
- hrach
- Member | 1838
I, personally, prefer the second
(callable(Request, callable(Request): Response): Response
). Nette
has currently the biggest problem in it's response header object,
which doesn't hold the Http Response, but one specific is hold by
Application layer.
Last edited by hrach (2016-12-13 08:19)
- Ondřej Mirtes
- Member | 1536
I'd love if the implementation adhered to the PSR-7 standard to be able to use the vast ecosystem of existing middlewares: https://github.com/…er/README.md
There's no point in supporting middlewares if we had to implement them again in a specific Nette-only way.
- David Grudl
- Nette Core | 8218
It seems useful and the 2nd approach is definitely better.
hrach wrote:
Nette has currently the biggest problem in it's response header, which doesn't hold the Http Response.
Yes, it is a pain.
- Jan Tvrdík
- Nette guru | 2595
Here is the important part. Traditionally middlewares are implemented with HTTP request object & response. However I'd like for Nette middlewares to have access to both application and HTTP request/response objects (therefore they would need to be called between IRouter::match & IPresenter::run and between IPresenter::run && IResponse::send). There are IMHO use-cases for HTTP-only middlewares which would be called before IRouter::match, but such cases can be more or less implemented with custom Http\RequestFactory.
This also probably partially answers the point mentioned by @OndřejMirtes regarding PSR-7 / PSR-15. An odd alternative would be using PSR-7 HTTP request/response objects & Nette app request/response objects.
Providing access to both application and HTTP request/response objects unfortunately complicated the signature
callable(HttpRequest, AppRequest, callable(HttpRequest, AppRequest): [HttpResponse, AppResponse]): [HttpResponse, AppResponse]
Because AppResponse has (in a way) access to HttpResponse, maybe the signature could be simplified to
callable(HttpRequest, AppRequest, callable(HttpRequest, AppRequest): AppResponse): AppResponse
If we disallow modifications of HttpRequest object, we can further simplify the signature to
callable(HttpRequest, AppRequest, callable(AppRequest): AppResponse): AppResponse
- hrach
- Member | 1838
- 1 month ago I did a private research for our company's middleware interface and not many people are using PSR-7
- I strongly believe that the Response should be created by the “endpoint” or the “middleware” itself.
- In Sygic Travel, we got inspired by https://github.com/…24/api-nette
- Out current interfaces are:
interface IMiddlerware
{
public function __invoke(Request $request, Nette\Http\IResponse $httpResponse, callable $next): Response;
}
interface IEndpoint
{
public function __invoke(Request $request, Nette\Http\IResponse $httpResponse): Response;
}
class Request
{
public function __construct(array $parameters, Nette\Http\IRequest $httpRequest)
{
$this->parameters = $parameters;
$this->httpRequest = $httpRequest;
}
...
}
class Response
{
public function __construct($data = null)
{
$this->data = $data;
}
}
Last edited by hrach (2016-12-13 08:29)