How can I set the charset with JAX-RS? I've tried #Produces("text/html; charset=UTF-8") but that was ignored and only text/html was send with the HTTP header. I want to set the charset within a MessageBodyWriter, but don't want to extract the media type by analysing the #Produces annotation via reflection by myself.
As Daemon pointed out in a comment, the latest versions of JAX-RS (including the stable version as of September 2012) now do support the #Produces syntax. So you can just use:
#Produces("text/html; charset=UTF-8")
Just to keep it up to date. Not sure whether this was supported in older versions of Jersey, but definitely if you decide to use ResponseBuilder.header(...) method you can use MediaType method withCharset(). Like this:
return Response.status(Status.OK)
.entity(result)
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_TYPE.withCharset("utf-8"))
.build());
It is also possible to use ResponseBuilder.header(...) method to set the content type with the charset. See below for a code sample (using JAX-RS 1.1.1, CXF 2.3.1).
final Response myResponse = Response.status(Response.Status.BAD_REQUEST)
.entity("La requĂȘte n'est pas correcte.\n ...")
.header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN+"; charset=ISO-8859-15" )
.build();
If you want to do this in a JAX-RS implementation neutral way, you may be able to reset the Content-Type in the MessageBodyWriter. Something like:
public void writeTo(Object obj,
Class<?> cls,
Type type,
Annotation[] annotations,
MediaType mt,
MultivaluedMap<String, Object> responseHttpHeaders,
OutputStream stream) throws IOException {
responseHttpHeaders.putSingle(javax.ws.rs.core.HttpHeaders.CONTENT_TYPE, mt.toString() + ";charset=UTF-8");
}
If you have different character sets besides UTF-8 per resource method, you may want to create a custom annotation and add it to each resource method. Then, try to use the annotations parameter in the writeTo() method.
Just FYI, Apache Wink supports the usage of charset and other attributes on media types. I hope that future JAX-RS spec revisions makes this easier.
First setup #Produces annotation on your resource class methods.
Then in MessageBodyWriter of your returned type, you can do this in writeTo() method:
response.setContentType(mediaType.toString);
Remark: You can inject response in your writer by:
#Context
protected HttpServletResponse response;
What I do is to get an instance of the servlet response object:
protected #Context HttpServletResponse response;
And then set the character encoding to utf-8:
response.setCharacterEncoding("utf-8");
That works for me.
If using RESTEasy you can register an Inteceptor:
import org.jboss.resteasy.annotations.interception.ServerInterceptor;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.core.ServerResponse;
import org.jboss.resteasy.spi.Failure;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.interception.PreProcessInterceptor;
import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.ext.Provider;
#Provider
#ServerInterceptor
public class ContentTypeSetter implements PreProcessInterceptor {
#Override
public ServerResponse preProcess(HttpRequest request, ResourceMethodInvoker resourceMethodInvoker) throws Failure, WebApplicationException {
request.setAttribute(InputPart.DEFAULT_CONTENT_TYPE_PROPERTY, "*/*; charset=UTF-8");
return null;
}
}
Note: If you manually set a #Produces it overrides the ContentType set by this interceptor. If you do that, set the charset in #Produces
Related
I am working on a REST API implementation using Jersey. For PATCH (partial updates), I have implemented my own custom implementation of PATCH since Jersey does not support it.
Now I am trying to figure out how to write functional tests around that implementation. I am using jersey test framework for other methods (PUT, POST, GET, DELETE) that has that support available in that framework.
Is there a way where in I can extend jersey test framework implementation to write my functional tests for PATCH?
If not, are there any other test frameworks available that I can use to test my Jersey PATCH implementation?
If anyone can provide any examples, that would be great.
Assuming your implementation consists of a custom annotation like this
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.HttpMethod;
#HttpMethod("PATCH")
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface PATCH {}
Trying to do something like this with the Client
String response = target.request().method("PATCH", Entity.text("Hello"), String.class);
by default is not supported, and will an exception like
java.net.ProtocolException: Invalid HTTP method: PATCH
This is not a problem with the Client API directly, but with the lower level Java APIs. Seems to be some security restriction.
With the Client API we can override this by setting a property
HttpUrlConnectionProvider.SET_METHOD_WORKAROUND to true
In the JerseyTest, one way to configure the Client is to override configureClient, and set the property with the ClientConfig. You could just as easily set the property on the Client itself, but staying in the spirit of the JerseyTest framework (where we don't need to explicitly access the Client, the example below will just just override the method
public class PatchTest extends JerseyTest {
#Path("patch")
public static class PatchResource {
#PATCH
#Produces(MediaType.TEXT_PLAIN)
public String getPatch(String request) {
return "Patched " + request;
}
}
#Override
protected void configureClient(final ClientConfig config) {
config.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
}
#Override
public Application configure() {
return new ResourceConfig(PatchResource.class);
}
#Test
public void doPatchTest() {
WebTarget target = target("patch");
String response = target.request().method("PATCH", Entity.text("Hello"), String.class);
Assert.assertEquals("Patched Hello", response);
System.out.println(response);
}
}
To send the HTTP PATCH via JAX RS Client API without any extra configuration:
client.target("$baseUrl$restUsersUrl/$userId")
.request("application/json")
.build("PATCH", Entity.entity(json2Update, MediaType.APPLICATION_JSON))
.invoke()
Annotation #PATCH is now available in JAX-RS 2.1. You can implement this HTTP method on the server side like:
#PATCH
public Response updateResource() { ... }
As for the client side, you can do something like:
Response r = ClientBuilder.newClient()
.target("http://localhost:8080/patch")
.request()
.build("PATCH", Entity.text("patch"))
.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true)
.invoke();
Where SET_METHOD_WORKAROUND is used to avoid the protocol exception, as indicated by #peeskillet:
java.net.ProtocolException: Invalid HTTP method: PATCH
With simple Strings this works for me. But does anyone know how to do this when the Patch method does not accept and return a simple String? See my example below.
The return type in the Response differs from the type of the passed argument. Both of which are not simple types.
Instead of a 200, I always get a 400 and/or the message that it cannot construct the ObjectPatch instance. And I understand that, since it is an interface with only an apply method. But somehow on runtime it manages to construct an AttentionPatchResource object from it anyway. Unfortunately not when using the JerseyTest framework.
#PATCH
#Path("/something")
#Produces(MediaType.APPLICATION_JSON)
#Consumes({ PatchMediaTypes.APPLICATION_MERGE_PATCH_JSON, PatchMediaTypes.APPLICATION_JSON_PATCH })
public Response updateAttention( //
#Parameter(schema = #Schema(implementation = AttentionPatchResource.class)) ObjectPatch patch) {
Attention attention = attentionService.find();
AttentionPatchResource patchResource = attentionAdapter.toPatchResource(attention);
AttentionPatchResource patchedResource = patch.apply(patchResource);
Attention patchedAttention = attentionAdapter.fromPatchResource(attention, patchedResource);
AttentionResource resource = attentionAdapter.toResource(patchedAttention);
return Response.status(Status.OK).entity(resource).build();
}
I have a RESTful server implementation as well as a library for clients to make the calls, all using JAX-RS. The server components are divided up into interface FooResource and implementation FooResourceService.
In order for the client and server libraries to share RESTful path and other definitions, I wanted to split out the FooResource interface into its own project:
#Path(value = "foo")
public interface FooResource {
#GET
public Bar getBar(#PathParam(value = "{id}") int id) {
I want to set some headers in the response. One easy way to do this is to use #Context HttpServletResponse in the method signature:
public Bar getBar(#PathParam(value = "{id}") int id, #Context HttpServletResponse servletResponse) {
But the problem is that this exposes implementation details in the interface. More specifically, it suddenly requires my REST definition project (which is shared between the client and server library) to pull in the javax.servlet-api dependency---something the client has no need up (or desire for).
How can my RESTful resource service implementation set HTTP response headers without pulling in that dependency in the resource interface?
I saw one post recommending I inject the HttpServletResponse as a class member. But how would this work if my resource service implementation is a singleton? Does it use some sort of proxy with thread locals or something that figures out the correct servlet response even though the singleton class is used simultaneously by multiple threads? Are there any other solutions?
The correct answer seems to be to inject an HttpServletResponse in the member variable of the implementation, as I noted that another post had indicated.
#Context //injected response proxy supporting multiple threads
private HttpServletResponse servletResponse;
Even though peeskillet indicated that the semi-official list for Jersey doesn't list HttpServletResponse as one of the proxy-able types, when I traced through the code at least RESTEasy seems to be creating a proxy (org.jboss.resteasy.core.ContextParameterInjector$GenericDelegatingProxy#xxxxxxxx). So as far as I can tell, thread-safe injection of a singleton member variable seems to be occurring.
See also https://stackoverflow.com/a/10076327/421049 .
So injecting HttpServletResponse seems like a no go. Only certain proxy-able types are inject-able into singletons. I believe the complete list is as follows:
HttpHeaders, Request, UriInfo, SecurityContext
This is somewhat pointed out in the JAX-RS spec, but is explained more clearly in the Jersey reference guide
The exception exists for specific request objects which can injected even into constructor or class fields. For these objects the runtime will inject proxies which are able to simultaneously server more request. These request objects are HttpHeaders, Request, UriInfo, SecurityContext. These proxies can be injected using the #Context annotation.
SecurityContext may be Jersey specific, as it's not stated in the spec, but I'm not sure.
Now those types mentioned above don't really do much for you because they are all request contexts and nothing to set the response.
One Idea though is to use a javax.ws.rs.container.ContainerResponseFilter, along with the HttpHeaders to set a temporary request header. You can access that header through the ContainerRequestContext passed to the filter method. Then just set the response header through the ContainerResponseContext, also passed to the filter method. If the the header is not specific to the context of that resource method, then it's even easier. Just set the header in the filter.
But let's say the header is dependent on the execution of the resource method. Then you could do something like
#Singleton
#Path("/singleton")
public class SingletonResource {
#Context
javax.ws.rs.core.HttpHeaders headers;
#GET
public String getHello() {
String result = resultFromSomeCondition(new Object());
headers.getRequestHeaders().putSingle("X-HELLO", result);
return "Hello World";
}
private String resultFromSomeCondition(Object condition) {
return "World";
}
}
Then the ContainerResponseFilter might look something like this
#Provider
public class SingletonContainerResponseFilter
implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext crc,
ContainerResponseContext crc1) throws IOException {
String header = crc.getHeaderString("X-HELLO");
crc1.getHeaders().putSingle("X-HELLO", "World");
}
}
And just so only the singleton classes run through this filter, we can simply use a #NameBinding annotation
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.NameBinding;
#NameBinding
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface SingletonHeader {}
...
#SingletonHeader
public class SingletonResource {
...
#SingletonHeader
public class SingletonContainerResponseFilter
implements ContainerResponseFilter {
This is the only way I can think to handle this situation.
Resources:
Filters and Interceptors
Name Binding
#Path("/foo")
public interface FooResource {
#GET
#Path("{id}")
public Response getBar(#PathParam("id") int id) {
Bar bar = new Bar();
//Do some logic on bar
return Response.ok().entity(bar).header("header-name", "header-value").build()
}
}
Returns a JSON representation of the instance of bar with a status code 200 and header header-name with value header-value. It should look something along the lines of:
{
"bar-field": "bar-field-value",
"bar-field-2": "bar-field-2"
}
I'm trying to test a resource with Resteasy using an embedded Netty instance as described in the Resteasy Docs.
Injecting path parameters and query parameters works like a charm but then I tried to test a resource that injects HttpServletRequest and HttpServletResponse from the context like this:
#GET
#Path("/")
public void example(#Context HttpServletResponse response,
#Context HttpServletRequest request) { ... }
Resteasy cannot find HttpServletRequestin the context and throws the following exception:
5105 [r #1] DEB o.j.resteasy.core.SynchronousDispatcher - PathInfo: /auth
5201 [r #1] ERR c.s.f.v.s.r.e.ApplicationExceptionMapper - Unhandled application exception: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest
org.jboss.resteasy.spi.LoggableFailure: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest
I tried putting mock versions of request and response in the context as suggested in RESTEasy Mock vs. Exception Mapper vs. Context but it does not work either as the contextual data is a ThreadLocal and Netty spawns a new thread for each request.
Any ideas on how to solve this?
What worked in my case was injecting a org.jboss.seam.mock.HttpServletRequest, since I am using seam in my application. You should try some mock framework like spring.test or mockito.
Here is how my code looks like:
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.mock.MockDispatcherFactory;
import org.jboss.resteasy.mock.MockHttpRequest;
import org.jboss.resteasy.mock.MockHttpResponse;
import org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.seam.mock.MockHttpServletRequest;
import org.jboss.seam.mock.DBUnitSeamTest;
public class Test extends DBUnitSeamTest{
#Test
public void test() throws Exception {
Dispatcher dispatcher = MockDispatcherFactory.createDispatcher();
POJOResourceFactory noDefaults = new POJOResourceFactory(ClasstoBeTested.class); dispatcher.getRegistry().addResourceFactory(noDefaults);
MockHttpRequest request = MockHttpRequest.get("/serviceToBeTested/1961");
MockHttpResponse response = new MockHttpResponse();
HttpServletRequest servletRequest = new MockHttpServletRequest(getSession());
ResteasyProviderFactory.getContextDataMap().put(HttpServletRequest.class, servletRequest);
dispatcher.invoke(request, response);
Assert.assertEquals(HttpServletResponse.SC_OK, response.getStatus());
Assert.assertTrue(response.getContentAsString().contains("1961"));
}
}
I just got hit by this again on another project and decided to investigate once more.
The issue is that in a mock request with Netty, there is no HttpServletRequest available. If you look into the sources of NettyJaxrsServerand related classes, Reasteasy uses its own abstraction for http requests that do not implement HttpServletRequest.
If I change my implementation to use these abstractions, I can access request and response in my resource.
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.HttpResponse;
#GET
#Path("/")
public void example(#Context HttpResponse response,
#Context HttpRequest request) { ... }
This is not perfect, because it makes my resources depend on Resteasy interfaces but I decided to go with it for now to support mock testing of multipart data.
I am trying to use the Jersey client API to consume a third-party REST service. I plan to use the automatic POJO deserialisation to go from JSON responses to Java objects.
Unfortunately, the third party service returns the responses using the content type "text/javascript". My Jersey client fails to understand that this should be considered as a JSON object and fails to deserialise the object.
I wrote a simple Jersey server application to verify that by changing the content type from "text/javascript" to "application/json" that the deserialisation works.
Armed with this information, I set about to use a Jersey client filter to modify the response headers. The code comes from a comment by the author of this question. In fact, the question appears to be exactly the same as mine - however the answerer mis-answered the question and shows how to modify the request headers (rather than the response headers). The original author was able to use the answer to create his solution, but, it seems his stated solution fails to work.
The filter code is:
client.addFilter(new ClientFilter() {
#Override public ClientResponse handle(ClientRequest cr)
throws ClientHandlerException {
ClientResponse response = getNext().handle(cr);
response.getHeaders().putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
return response;
}
});
When executed however, an UnsupportedOperationException is raised:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.clear(Collections.java:1035)
at com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap.putSingle(StringKeyIgnoreCaseMultivaluedMap.java:78)
at com.sun.jersey.core.util.StringKeyIgnoreCaseMultivaluedMap.putSingle(StringKeyIgnoreCaseMultivaluedMap.java:56)
at App$1.handle(App.java:49)
at com.sun.jersey.api.client.Client.handle(Client.java:648)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:680)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:507)
at App.main(App.java:63)
The returned headers appear to be wrapped in an unmodifiable collection.
I then attempted to copy all of the headers to a new collection, but there is no way that I can see to set a map of headers back into the response.
Finally, I thought perhaps I can create a new ClientResponse containing my amended headers. However, the constructor for ClientResponse has this signature:
public ClientResponse(int status,
InBoundHeaders headers,
InputStream entity,
MessageBodyWorkers workers)
It is trivial to copy the status, headers and entity variables from the original. However, I can see no way of getting a reference to the workers field.
How can I use a Jersey client filter to modify the response header from "text/javascript" to "application/json" so that my POJO deserialisation will work?
In Jersey 2, register an implementation of a ClientResponseFilter with the ClientConfig in order to manipulate the HTTP headers of incoming responses.
For example, this seems to work well with Jersey 2.3.1 for manipulating HTTP header:
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
import javax.ws.rs.client.ClientRequestContext;
import org.glassfish.jersey.client.ClientConfig;
/* Ensure that there is an "application/xml" Content-Type header on
* successful responses without a content type header. */
#Provider
public static class EnsureXmlContentTypeFilter implements ClientResponseFilter {
#Override
public void filter(ClientRequestContext requestContext,
ClientResponseContext responseContext) {
if (200 == responseContext.getStatus() &&
null == responseContext.getHeaderString(HttpHeaders.CONTENT_TYPE)) {
responseContext.getHeaders().add(
HttpHeaders.CONTENT_TYPE, "application/xml"
);
}
}
}
private final ClientConfig config = new ClientConfig()
// Registering this filter adds a "Content-Type: application/xml"
// header to each response that lacks Content-Type headers.
.register(EnsureXmlContentTypeFilter.class)
;
private final Client client = ClientBuilder.newClient(config);
The Jersey documentation on Filters and Interceptors isn't perfect, but it does have some links to the javadocs for the relevant classes: https://jersey.java.net/documentation/latest/filters-and-interceptors.html
I am getting XML responses from a service which responds with XML content, but lacks a "Content-Type: application/xml" header. Probably a better approach would be to register MessageBodyReaders, but the above approach works while I'm playing around with that service's API.
I don't have an answer to your real question, but I think I see how you can get that workers instance if you want to try to create a new response in your filter.
The "workers" object that you need appears to be a singleton. If you can get hold of your com.sun.jersey.api.client.Client instance, you can retrieve the workers object. In my case, the Jersey client code is in a unit test which subclassed JerseyTest. JerseyTest defines a method "client()" which returns the Client object. I added the following test code (well not exactly but close):
MessageBodyWorkers workers = client().getMessageBodyWorkers();
Then I set a breakpoint in the constructor of ClientResponse (this is the original ClientResponse returned by Jersey. I have not attempted to clone it because I don't need to for my test). The workers passed to the constructor was the same instance. So, even though you can not get the workers object from the response object, you should be able to get it elsewhere.
Guido's answer provides the insight required to create a new ClientResponse object and return it instead. For reasons that I've not yet bothered to track down, creating a new InboundHeaders, adding all the existing headers to it, and then modifying the single header in question still fails with an UnsupportedOperationException. As such, to re-write the headers, we iterate over the original headers and build the correct set iteratively:
final Client client = Client.create(clientConfig);
client.addFilter(new ClientFilter()
{
#Override
public ClientResponse handle(ClientRequest cr) throws ClientHandlerException
{
final ClientResponse response = getNext().handle(cr);
final InBoundHeaders headers = new InBoundHeaders();
for (String header : response.getHeaders().keySet())
{
if (header.equals(HttpHeaders.CONTENT_TYPE))
{
headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
}
else
{
headers.put(header, headers.get(header));
}
}
return new ClientResponse(response.getStatus(),
headers,
response.getEntityInputStream(),
client.getMessageBodyWorkers());
}
}
In Jersey 2, you should use a ClientResponseFilter. Then you can just call responseContext.getHeaders().putSingle(...).
Under Java 8 you can do it with a lambda:
client.register((ClientResponseFilter) (requestContext, responseContext) ->
responseContext.getHeaders().putSingle("Content-Type", "application/json"));
If you want to re-use an existing filter instance, just register it on the Client instead of on the ClientConfig.
Old way (Jersey-1.9):
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
Client client = new Client();
client.addFilter(new HTTPBasicAuthFilter(username, password));
New way (Jersey-2.3):
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import org.glassfish.jersey.client.filter.HttpBasicAuthFilter;
Client client = ClientBuilder.newClient();
client.register(new HttpBasicAuthFilter(username, password));
That's not the best solution, but it may help you to migrate.
I wrote an HttpServletRequestWrapper named HTTPRequest that reads the full HTTP POST body for further use. This one is based in the code at http://natch3z.blogspot.com/2009/01/read-request-body-in-filter.html
My Jersey server application needs to get this wrapper to read the body. Unfortunately, I don't know how to do that.
I've tried putting
#Context HTTPRequest request;
but it does not work (Missing dependency for field).
I've tried too doing:
#Context HttpServletRequest request;
then casting to HTTPRequest, it didn't work neither ($ProxyXXX cannot be cast to HTTPRequest).
I've searched for information in the Internet but I cannot find anything regarding this. Any idea? :)
Thanks!
I don't quite understand: HTTPRequest is your objects extending the HttpServletRequestWrapper, right?
So if you want Jersey to inject it via the #Context annotation, you need to implement the ContextResolver. Actually in your case it should be easy:
#Provider
HTTPRequestContextResolver implements ContextResolver<HTTPRequest> {
#Context HttpServletRequest request;
HTTPRequest getContext(Class<?> type) {
return new HTTPRequest(request);
}
}
Updated: If you already wrapped the original request in a filter, you may have a problem to get it, since Jersey wraps the original request using the dynamic proxy.