Native support for middlewares

Notice: This thread is very old.
Jan Tvrdík
Nette guru | 2595
+
+17
-

About middlewares

A middleware is an object which

  1. can modify request object (returns the modified object)
  2. can modify response object (returns the modified object)
  3. 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 | 1834
+
0
-

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
+
+9
-

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.

Felix
Nette Core | 1185
+
0
-

I'm working on same thing. :-)

My concept was about the first prototype.

callable(Request, Response, callable(Request): Response next): Response

I think it's pretty standard now. And it also satisfy @OndřejMirtes, right?

David Grudl
Nette Core | 8099
+
0
-

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
+
0
-

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 | 1834
+
0
-
  • 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)