Reputation: 16969
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?
Upvotes: 0
Views: 1583
Reputation: 16969
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());
}
}
}
Upvotes: 1
Reputation: 39241
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("application/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());
}
}
}
Upvotes: 1