简体   繁体   中英

ContainerRequestFilter returns wrong content type for entity

I want to make a global check for every request. Therefore, I use a ContainerRequestFilter (without @PreMatching ) and throw a WebApplicationException with a response containing an error entity, if the check is not passed.

My problem is, that the content type of the response is not matching the Accept header of the request. But if I throw the same exception in my resource, the response contains the right content type.

Code:

My entity:

@XmlRootElement(namespace = "http://www.mycompany.com/test")
@XmlAccessorType(value = XmlAccessType.FIELD)
public class TestEntity {

    public TestEntity() {
        this.key = "error";
    }

    @XmlElement
    private String key;
}

My filter:

@Named
public class TestFilter implements ContainerRequestFilter {
    private boolean globalError = true;

    public void filter(final ContainerRequestContext requestContext) throws IOException {
        if (globalError) {
            throw new WebApplicationException(Response.status(422).entity(new TestEntity()).build());
        }
    } 
}

My resource:

@Named
public class TestResource {

    @GET
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public void find() {
        throw new WebApplicationException(Response.status(422).entity(new TestEntity()).build());
    }
}

My CXF configuration:

<jaxrs:server address="/rest/v1" id="test">
    <jaxrs:serviceBeans>
        <ref bean="testResource" />
    </jaxrs:serviceBeans>
    <jaxrs:providers>
        <bean class="org.apache.cxf.jaxrs.provider.JAXBElementProvider" />
        <bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider" />
        <ref bean="testFilter" />
    </jaxrs:providers>
</jaxrs:server>

Test:

Request:

GET http://localhost:8080/test-webapp/services/rest/v1/ HTTP/1.1
Accept-Encoding: gzip,deflate
Accept: application/json
Host: localhost:8080
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

Response with globalError=true :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:testEntity xmlns:ns2="http://www.mycompany.com/test">
      <key>error</key>
</ns2:testEntity>

Response with globalError=false :

{"key":"error"}

Questions:

Why is the response different? How can I fix it?

Seems default response builder of ContainerRequestFilter does not use accept header to override the response content type. Its needed to add the content type to the Response

throw new WebApplicationException(Response.status(422).type("applicati‌​on/json").entity(new TestEntity()).build()

One option to get the desired behaviour is inspecting the header before set .type in exception response (use requestContext.abortWith() instead of raise an exception)

This is an example to set application/json if it is found in the header or return the default type

public class TestFilter implements ContainerRequestFilter {
    private boolean globalError = true;

    private String typeFromHeaders(ContainerRequestContext requestContext){
        List<String> acceptHeaders = requestContext.getHeaders().get("Accept");
        if (acceptHeaders != null){         
            for (String acceptHeader: acceptHeaders){
                if (acceptHeader.indexOf(MediaType.APPLICATION_JSON)>=0){
                    return MediaType.APPLICATION_JSON;
                }   
            }
        }
        return null;
    }
    public void filter(final ContainerRequestContext requestContext) throws IOException {
        if (globalError) {
            requestContext.abortWith(
                    Response.status(422).type(typeFromHeaders(requestContext)).entity(new TestEntity()).build());
        }
    } 
}

Based on @pedrofb good answer I found a easier work-around using ContainerRequestContext#getAcceptableMediaTypes :

Get a list of media types that are acceptable for the response.

Returns:
a read-only list of requested response media types sorted according to their q-value, with highest preference first.

My modified filter:

@Named
public class TestFilter implements ContainerRequestFilter {
    private boolean globalError = true;

    public void filter(final ContainerRequestContext requestContext) throws IOException {
        if (globalError) {
            MediaType mediaType = requestContext.getAcceptableMediaTypes().size() > 0 ? requestContext.getAcceptableMediaTypes().get(0) : null;
            throw new WebApplicationException(Response.status(422).type(mediaType).entity(new TestEntity()).build());
        }
    } 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM