Skip to content

Filters

Carl Mastrangelo edited this page Jan 24, 2020 · 14 revisions

Filters are the core of Zuul's functionality. They are responsible for the business logic of the application and can perform a variety of tasks.

  • Type: most often defines the stage during the routing flow when the Filter will be applied (although it can be any custom string)
  • Async: define if the filter is sync or async, generally meaning do you need to make an external call or just doing work on-box
  • Execution Order: applied within the Type, defines the order of execution across multiple Filters
  • Criteria: the conditions required in order for the Filter to be executed
  • Action: the action to be executed if the Criteria is met

Zuul provides a framework to dynamically read, compile, and run these Filters. Filters do not communicate with each other directly - instead they share state through a RequestContext which is unique to each request.

Filters are currently written in Groovy, although Zuul supports any JVM-based language. The source code for each Filter is written to a specified set of directories on the Zuul server that are periodically polled for changes. Updated filters are read from disk, dynamically compiled into the running server, and are invoked by Zuul for each subsequent request.

Incoming

Incoming filters get executed before the request gets proxied to the origin. This is generally where the majority of the business logic gets executed. For example: authentication, dynamic routing, rate limiting, DDoS protection, metrics.

Endpoint

Endpoint filters are responsible for handling the request based on the execution of the incoming filters. Zuul comes with a built-in filter (ProxyEndpoint) for proxying requests to backend servers, so the typical use for these filters is for static endpoints. For example: health check responses, static error responses, 404 responses.

Outgoing

Outgoing filters handle actions after receiving a response from the backend. Typically they're used more for shaping the response and adding metrics than any heavy lifting. For example: storing statistics, adding/stripping standard headers, sending events to real-time streams, gziping responses.

Async

Filters can be executed either synchronously or asynchronously. If your filter isn't doing a lot of work and is not blocking or going off-box, you can safely use a sync filter by extending HttpInboundSyncFilter or HttpOutboundSyncFilter.

However, in some cases you need to get some data from another service or a cache, or perhaps do some heavy computations. In these cases, you should not block the Netty threads and should use an async filter that returns an Observable wrapper for a response. For these filters you would extend the HttpInboundFilter or HttpOutboundFilter.

Example of a sync filter: Routes

Example of an async filter: SampleServiceFilter

Extracting Body Content

By default Zuul doesn't buffer body content, meaning it streams the received headers to the origin before the body has been received. This streaming behavior is very efficient and desirable, as long as your filter logic depends on header data. However, if you want to extract the request or response body in your inbound or outbound filter, you will need to explicitly tell Zuul to buffer the body. You can do this by overriding the needsBodyBuffered() method in your filter:

    @Override
    boolean needsBodyBuffered(HttpResponseMessage input) {
        return true
    }

Useful Filters

Zuul comes with some useful filters built and examples of other in the sample app.

Sample Filters

  • DebugRequest - look for a query param to add extra debug logging for a request
  • Healthcheck - simple static endpoint filter that returns 200, if everything is bootstrapped correctly
  • ZuulResponseFilter - add informational headers to provide extra details on routing, request execution, status and error cause

Core Filters