skip to content

What filters ship with REST Assured out of the box, and how do you register one on a request?

level: juniorimportance: should knowfreq 58%

answer

  1. four packages under io.restassured.filter
  2. four print, one times, two remember
  3. given().filter(...) or RestAssured.filters(...)
  4. seven shipped implementations, no retry filter

basics

~20 s

REST Assured ships seven ready-made filters: RequestLoggingFilter, ResponseLoggingFilter, ErrorLoggingFilter, StatusCodeBasedLoggingFilter, TimingFilter, CookieFilter and SessionFilter. Four of them print, one records elapsed time, and two remember state between calls. Register one with given().filter(...), or on every call through RestAssured.filters(...).

solid answer

~50 s

REST Assured's public filter packages -- `io.restassured.filter.log`, `.cookie`, `.session` and `.time` -- hold seven implementations, and most suites never need more. Four of them print: `RequestLoggingFilter` prints the built request specification, `ResponseLoggingFilter` prints the response and can be gated by a status matcher, `ErrorLoggingFilter` prints the response when the status is 400 to 500 inclusive, and `StatusCodeBasedLoggingFilter` is the shared base the last two extend rather than something you instantiate. `TimingFilter` records elapsed milliseconds into the filter context. `CookieFilter` and `SessionFilter` are the two that carry state, and each keeps it on the filter *instance*, so the same object has to be reused. Register with `given().filter(f)`, `given().filters(...)`, `RequestSpecBuilder.addFilter(f)` or the static `RestAssured.filters(...)`; `noFilters()` and `noFiltersOfType(Class)` drop inherited ones again. None of the seven is registered by default, with one exception: REST Assured appends a `TimingFilter` itself when the chain has none.

code

java · 39 lines
java
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.filter.cookie.CookieFilter;
import io.restassured.filter.log.ErrorLoggingFilter;
import io.restassured.specification.RequestSpecification;

import static io.restassured.RestAssured.given;

public class MuseumTicketingSpec {

    // one instance, reused: the cookie store lives on the filter
    private static final CookieFilter COOKIES = new CookieFilter();

    private static final RequestSpecification SPEC = new RequestSpecBuilder()
            .setBaseUri("https://api.museum.example")
            .setBasePath("/v1")
            .addFilter(COOKIES)
            .addFilter(new ErrorLoggingFilter())
            .build();

    public void buyAndCheckIn() {
        given().spec(SPEC)
                .formParam("email", "[email protected]")
                .formParam("password", "s3cret")
                .post("/sessions")
                .then().statusCode(200);

        String ticketCode = given().spec(SPEC)
                .contentType("application/json")
                .body("{\"exhibitionId\":\"pompeii\",\"slotId\":\"2026-04-11T10:00\"}")
                .post("/tickets")
                .then().statusCode(201)
                .extract().path("ticketCode");

        given().spec(SPEC)
                .noFiltersOfType(ErrorLoggingFilter.class)
                .post("/visits/{code}/check-in", ticketCode)
                .then().statusCode(200);
    }
}

go deeper

for a junior

Be ready to name the shipped filters without notes and to show the one-line registration, given().filter(new RequestLoggingFilter()). Grouping them as four printers, one timer and two rememberers is worth more than reciting seven class names.

for a middle

Explain the mechanics: which package each filter lives in, that ErrorLoggingFilter and ResponseLoggingFilter share a base class, and that the two stateful ones keep their memory on the instance rather than on the specification.

for a senior

Show suite-wide judgment. Put logging and cookie filters on a shared request specification or the static list rather than in every test, and use noFiltersOfType(...) to keep one chatty endpoint out of the CI log.

for a principal

Own the line between what the library already ships and what your team writes. Every house-written filter is code someone maintains, and a filter registered globally runs on every request in the suite -- that cost is a design decision, not an afterthought.

A **filter** in REST Assured is the library's around-advice for one HTTP call: it sees the request specification before the call is committed and the response before your expectations run. Seven implementations ship inside the library, spread across four public packages, and between them they cover the cross-cutting jobs an API suite usually needs -- printing, timing, and carrying cookies forward. ## The seven, and what each one does | Filter | Package | What it does | | --- | --- | --- | | `RequestLoggingFilter` | `io.restassured.filter.log` | Prints the built request specification, then delegates | | `ResponseLoggingFilter` | `io.restassured.filter.log` | Prints the response, optionally gated by a status matcher | | `ErrorLoggingFilter` | `io.restassured.filter.log` | Prints the response when the status is 400 to 500 inclusive | | `StatusCodeBasedLoggingFilter` | `io.restassured.filter.log` | Shared base of the two above; not declared `public` | | `TimingFilter` | `io.restassured.filter.time` | Records elapsed milliseconds into the filter context | | `CookieFilter` | `io.restassured.filter.cookie` | Replays and stores cookies across calls | | `SessionFilter` | `io.restassured.filter.session` | Replays and stores the session identifier across calls | Three lines in that table are easy to state wrongly: - `StatusCodeBasedLoggingFilter` is declared **without** the `public` modifier. It ships, and both `ResponseLoggingFilter` and `ErrorLoggingFilter` extend it, but you cannot call its constructor from your own package -- you reach the same behaviour through `new ResponseLoggingFilter(matcher)`, which forwards to it. - `ErrorLoggingFilter`'s window is **inclusive at both ends**: `allOf(greaterThanOrEqualTo(400), lessThanOrEqualTo(500))`. A 500 triggers it; a 503 does not. - Only `CookieFilter` and `SessionFilter` remember anything between calls. The other five begin every request with a clean slate. ## Static factories, because the naming is not uniform Several of the seven carry static factory methods that read better inside a chain than a bare constructor: 1. `RequestLoggingFilter.logRequestTo(stream)` and `RequestLoggingFilter.with(LogDetail...)`. 2. `ResponseLoggingFilter.responseLogger()`, `logResponseTo(stream)`, `logResponseIfStatusCodeIs(int)` and `logResponseIfStatusCodeMatches(Matcher<Integer>)`. 3. `ErrorLoggingFilter.errorLogger()` and `ErrorLoggingFilter.logErrorsTo(stream)`. 4. `TimingFilter.measureTime()`. 5. `CookieFilter` and `SessionFilter` have none at all -- you call `new`, and you keep the reference, because the reference *is* the memory. ## Four places you can register one Take a suite against a museum ticketing API. A single call takes a filter directly, and everything wider is built from the same idea: - `given().filter(new ErrorLoggingFilter())` -- one filter, one call. - `given().filters(listOfFilters)` or `given().filters(first, second)` -- several at once. - `new RequestSpecBuilder().addFilter(f).build()` -- baked into a request specification every test reuses. - `RestAssured.filters(new ErrorLoggingFilter())` -- a static default applied to every subsequent request. `RestAssured.replaceFiltersWith(...)` swaps the whole list, and `RestAssured.reset()` empties it along with the other statics. Going the other way matters just as much in a real suite. `given().noFilters()` drops every inherited filter for one call, and `given().noFiltersOfType(ResponseLoggingFilter.class)` drops one kind -- which is what you want when `GET /v1/exhibitions/{exhibitionId}/slots` returns a large slot inventory that you do not want printed into a CI log on every run. ## Where the seven sit in the chain None of the seven implements `OrderedFilter`, so every one of them takes the default precedence of `1000`, and the sort REST Assured applies is stable. **Registration order therefore decides** among the shipped filters, and two consequences follow that bite in practice: - A `RequestLoggingFilter` registered *before* a `CookieFilter` prints its output before the cookie filter has added the stored cookies, so those cookies are absent from the log even though they are sent. - REST Assured appends its own `TimingFilter` last, immediately before the internal filter that actually sends the request, unless you have already registered one yourself. ## A worked museum-ticketing setup A suite that logs in through `POST /v1/sessions`, buys through `POST /v1/tickets` and then checks in through `POST /v1/visits/{ticketCode}/check-in` usually wants two of the seven and no more. An `ErrorLoggingFilter` on the shared specification means a failing purchase explains itself in the build output without any per-test wiring. A `CookieFilter` held as a field -- one instance, reused -- means the cookie set at login survives into the purchase and the check-in. What the shipped set deliberately does not give you is equally worth naming in an interview: - There is **no retry filter**, and no retry anywhere in REST Assured; a retry is a loop or a runner feature outside the library. - There is no correlation-id filter, no schema-check filter and no request-signing filter. - Anything on that list is a hand-written `Filter` implementation, which is a separate skill from knowing the seven that already exist. The practical summary an interviewer is listening for is short: four printers, one timer, two rememberers; register with `filter(...)` or `filters(...)`; and the two that remember must be the same instance every time.

  • Which of the seven shipped filters need you to hold on to the instance, and why?
    Only `CookieFilter` and `SessionFilter`. Their memory is instance state -- a `BasicCookieStore` and an `AtomicReference<String>` respectively -- so a `new CookieFilter()` created inside each test method starts empty and carries nothing forward. The logging filters and `TimingFilter` hold no per-request memory, so a fresh instance behaves identically to a reused one.
  • How do you turn a globally registered logging filter off for one noisy endpoint?
    Use `given().noFiltersOfType(ResponseLoggingFilter.class)` on that one call, which drops just that kind while leaving the rest of the inherited filters in place. `given().noFilters()` is the blunter form and removes all of them. Neither touches the static list, so the next request still gets whatever `RestAssured.filters(...)` set up.

saying these in an interview costs you the question

  • Claiming REST Assured ships a retry or polling filter
  • Saying ErrorLoggingFilter prints the request as well as the response
  • Creating a new CookieFilter in every test and expecting cookies to persist
  • Thinking StatusCodeBasedLoggingFilter is a class you instantiate directly
  • Assuming every shipped filter is registered by default