JAX-RSをやってみる (9) - Container Filters -

2014-06-16T00:00:00+00:00 Java JAX-RS

公式ドキュメント: https://jersey.java.net/documentation/latest/user-guide.html#d0e8119

単純にリクエストなりレスポンスをフィルターする仕組みなAPIな模様。でリクエストからレスポンスまでのライフサイクルについては9.4. Filter and interceptor execution orderにかかれているのだけど、@PreMatchingを使う事によりリクエストからマッチするリソースが特定される前に実行される物なのだろうかと

XHRRequestFilter.java

X-Requested-Withヘッダーをチェックして無い(もしくはXMLHttpRequestではない)ならHTTP/400(Bad Request)を流すようなContainerRequestFilterを作る

package sample.filter;

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

@PreMatching
@Provider
public class XHRRequestFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        String header = requestContext.getHeaderString("X-Requested-With");

        if (!("XMLHttpRequest".equals(header))) {
            requestContext.abortWith(Response.status(Response.Status.BAD_REQUEST).build());
        }
    }
}

っていうような感じだけど、これだと@Providerされてる時点で全リクエストに対してこのフィルターが作用するようになる。で例えば

package sample.filter;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface XHRFilter {
}

っていうような@XHRFilterアノテーションを作って、このアノテーションがついてる場合にのみこのフィルターが作動するような場合であれば

package sample.filter;

import java.io.IOException;

import javax.inject.Inject;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

import org.glassfish.jersey.server.ExtendedUriInfo;

@Provider
public class XHRRequestFilter implements ContainerRequestFilter {

    @Inject
    private ExtendedUriInfo uriInfo;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {

        // @PreMatchingだとExtendedUriInfo.getMatchedResourceMethodがnullを返す
        XHRFilter filterAnnotation = uriInfo.getMatchedResourceMethod()
            .getInvocable()
            .getHandlingMethod()
            .getAnnotation(XHRFilter.class);

        if (filterAnnotation != null) {
            String header = requestContext.getHeaderString("X-Requested-With");

            if (!("XMLHttpRequest".equals(header))) {
                requestContext.abortWith(Response.status(Response.Status.BAD_REQUEST).build());
            }
        }
    }
}

っていうように出来る。@PreMatchingを使っているとリソースマッチを処理していない段階でのフィルタリング処理が行われるのでgetMatchedResourceMethodを使ってもnullしか返ってこない

っていう感じ。又は前回の「DynamicFeature」を使って特定のアノテーションがついてる場合にのみコンポーネントをregisterするような仕組みを使っても出来る

っていう事でコントローラーが

package sample.controller;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

import sample.filter.XHRFilter;

@Path("/sample")
public class Home {

    @GET
    @Path("index")
    @XHRFilter
    public String index() {
        return "hoge";
    }

    @GET
    @Path("say")
    public String say() {
        return "hoge";
    }
}

みたいになってて

package sample.controller;

import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;

import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

import sample.SampleApplication;

//import static javax.ws.rs.core.MediaType.*;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;

public class HomeTest extends JerseyTest {

    @Override
    protected Application configure() {
        return new SampleApplication();
    }

    @Test
    public void test_index() {
        Response response = target("/sample/index")
            .request()
            .header("X-Requested-With", "XMLHttpRequest")
            .get();

        assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
    }

    // ヘッダーが指定されてないのでHTTP/400
    @Test
    public void test_indexNoHeader() {
        Response response = target("/sample/index")
            .request()
            .get();

        assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST.getStatusCode()));
    }

    // X-Requested-With = XMLHttpRequestじゃないのでHTTP/400
    @Test
    public void test_indexNotXHR() {
        Response response = target("/sample/index")
            .request()
            .header("X-Requested-With", "Java")
            .get();

        assertThat(response.getStatus(), is(Response.Status.BAD_REQUEST.getStatusCode()));
    }

    // sayリソース自体は@XHRFilterがついてないのでヘッダーを指定しなくてもHTTP/200になる
    @Test
    public void test_say() {
        Response response = target("/sample/say")
            .request()
            .get();

        assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
    }
}

のテストが通れば良いんじゃないかと

JAX-RSをやってみる (10) - AsyncResponse - JAX-RSをやってみる (8) - DynamicFeature -