Middleware
Introduction
Middleware is code that affects the request pipeline. It enables applications to customize how the server handles requests. This customization comes in three different shapes:
request filters
response filters
response and request filters wrapped up in middleware classes.
These can help with a range of issues, such as security, logging, wrapping, request modification, and more.
An example of this is the Launcher which uses request filters to wrap responses from other applications into its own response.
All middleware is registered with the Application.Current.Use
API which has three overloads corresponding to the different types of middleware listed above. The registration is usually done at the entry point of the application and can look something like this:
Middleware does not impact internal Self.GET
calls.
Request Filters
When allowing external HTTP requests, it might be useful to pre-process or filter out certain requests before the designated handler is called. Request filters make it possible to do exactly that. They are lists of user-supplied delegates, or filters, that are executed on external requests before the actual handlers are called. These filters are executed one by one until one of the filters returns a non-null Response
. If a Response
was returned from the request filter, then this response is returned to the client without calling the handler. If none of the filters returned a Response
object, then the request will be passed on and dealt with by the handler.
An example of this can be an basic spam filter:
When there is an incoming request, this request filter checks if the URI contains the string "spam", and returns a Response
object if that's the case. This means that the request will be blocked without reaching the handler. Otherwise, it returns null
and allows the request to move on to the next request filter or go to the handler if there was only one request filter.
Skip Request Filters
To let requests to a certain handler bypass all request filters, use the class HandlerOptions
and set SkipRequestFilters
to true
. Like this:
Response Filters
Response filters do the opposite of request filters; they make alterations to outgoing responses. They work similarly to request filters by being executed one by one until one returns a non-null response. The main difference is that response filters are called after the handler has been called while request filters are called before. Response filters can either create entirely new responses and return those, or modify the response coming from the handler.
For example, response filters makes it possible to add a certain HTTP header to responses for requests with a /special
URI prefix after the request has been dealt with by the handler:
In this case, a new header would be added to the response if the URI of the incoming request started with /special
. It would then return the response and no other response filters would be called. If the request URI did not start with /special
, then the next response filter would be called or the response would be returned if there were no more response filters to call. Take a look at this response filter by comparison:
In this case, the next response filter is never called since a response is always returned. That is important to keep in mind when building response filters.
In the examples above, the response filter checks for information in the request. It's also possible to check for information in the response, such as in this example:
Here, the response filter makes it possible to return a descriptive 404
page by checking the outgoing responses for the 404
status code and return a response containing the "not found" HTML page.
Skip response filters
For a handler to bypass all response filters, use the class HandlerOptions
and set SkipResponseFilters
to true
. Like this:
Response and Request Filter Interaction
When using both request and response filters, response filters will intercept responses coming from request filters. Consider the following example:
When sending a request to the /Test
handler, the request filter will intercept it and create a Response
object. This response is then intercepted by the response filter that creates another Response
object which will be the one received by the client. When this code runs, "THIS IS FROM THE RESPONSE FILTER" displays.
Middleware Interaction with Other Applications
Both request and response filters catch requests to handlers in other applications that are running simultaneously.
Consider the following request filter:
By having this request filter in one application, all requests, to all applications, will hit the 404
response.
Adding this if
statement fixes it:
Middleware Classes
Application.Current.Use
also allows for exposing custom middleware classes. These middleware classes implement the IMiddleware
interface.
Here's an application using a middleware class:
Here, we use the IMiddleware
interface to create a custom middleware class. The middleware created is a request filter that returns an unhelpful response no matter what the incoming request is.
Middleware classes makes it possible to hide the implementation of middleware.
These classes do not have to contain request or response filters. Although, that is the common way of using middleware classes.
HtmlFromJsonProvider
HtmlFromJsonProvider
is a custom middleware class provided by Starcounter. It acts as a response filter by intercepting outgoing responses containing JSON objects and instead responding with the corresponding HTML. For example, look at the following application:
When there is a call to/person
, this is what will happen:
The handler returns The JSON object
Person
The
HtmlFromJsonProvider
intercepts the objectIt looks in the JSON file for the
Html
property and finds/person.html
It returns the HTML found at that path
If the HTML at the path would be a complete HTML document, this would be enough. Though, because the HTML provided in this example is a HTML template, it's necessary to add another layer of middleware to convert the template to an HTML document that the browser can render. That's what PartialToStandaloneHtmlProvider
does.
ScErrInvalidOperation
If you have a JSON file without an `Html` property, HtmlFromJsonProvider
will throw this exception:
If the JSON has a corresponding HTML file, add an `Html` property with the path to the HTML file. That will fix it.
If you don't have an Html
property and don't intend to return HTML, but to return the JSON instead, set IgnoreJsonWithoutHtml
to true
:
PartialToStandaloneHtmlProvider
This middleware class checks if the HTML is a full document, or essentially if it starts with a <!DOCTYPE html>
. If it's not a full HTML document, it wraps the existing HTML inside the body of an HTML document that contains the following:
A
palindrom-client
element to create a WebSocket connectionImport links to the Starcounter custom elements
starcounter-include
andstarcounter-debug-aid
Import links to the outside libraries Polymer and Bootstrap
The session URL which makes it possible for PuppetJs to request the relevant JSON in a future request
It's possible to override this default HTML by passing a string containing HTML as a parameter. Here's an example of that:
Since PartialToStandaloneHtmlProvider
wraps the actual response from the handler, it will have the HTTP status code of the response.
When building Starcounter applications the recommended way, use the two middleware classes mentioned above to send complete HTML documents to the client even if the handler returns Typed JSON classes.
This is how it should look:
Last updated