Setting response header using interceptor? - java

I'm writing jax-rs end points. For some set of end points (existing code), I want to set an extra response header which was actually generated in #AroundInvoke interceptor and set to HttpServletRequest attribute. In #AroundInvoke I'm able to access HttpServletRequest using #Inject. But it seems I cannot access HttpServletResponse in the same interceptor itself.
It seems I can do with PostProcessorInterceptor but again I'm confused with the following doc.
The org.jboss.resteasy.spi.interception.PostProcessInterceptor runs after the JAX-RS method was invoked but before MessageBodyWriters are invoked. They can only be used on the server side. Use them if you need to set a response header when there might not be any MessageBodyWriter invoked.
I'm using resteasy, jackson. If I use PostProcessorInterceptor can I inject HttpServletResponse? Or Can I set new http header there some how?
Any code example/direction would be appreciated.

With JaxRS 2 (which comes with javaEE 7) you can use a ContainerResponseFilter see also
public class PoweredByResponseFilter implements ContainerResponseFilter {
#Inject
HttpServletRequest request;
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
String name = "X-My-Header";
String value = "";// some data from request
responseContext.getHeaders().add(name, value);
}
}

Related

Using javax.ws.rs.core.Context with fabric3

I am trying to implement a ContainerRequestFilter for a RESTful web service with fabric3v2.5.3. I have the filter working, but I need to grab the ip address in the request. I have tried using
#Context
HttpServletRequest servletRequest;
But it throws a null pointer exception when trying access the HttpServletRequest. We are using the built in jersey 2.13 that comes with the JAX-RS fabric3 extension. Is there a way to get the #Context to work within fabric3?
#Context injection support will be added in Fabric 3.0 and is not available in previous versions.
For versions earlier than Fabric3 3.0, you can retrieve the HTTP servlet request and response objects via an injected Fabric3RequestContext:
public class SomeFilter implements .... {
#Context
protected F3RequestContext context;
public void filter(ContainerRequestContext requestContext) {
HttpServletRequest req =
context.getHeader(HttpServletRequest.class, "fabric3.httpRequest");
}
}

How to use Jersey's internal routing mechanism to extract a class/method reference?

I have a Jersey 1.8 application running. Jersey is running as a Servlet.
I need to write a servlet filter that given a plain request/response, is able to figure out which REST resource/method will respond to the request and extract values from annotations.
For example, imagine I have the following resource:
#Path("/foo")
#MyAnnotation("hello")
public class FooResource {
#GET
#Path("/bar")
#MyOtherAnnotation("world")
public Response bar(){
...
}
}
When a request GET /foo/bar comes in, I need my servlet filter to be able to extract the values "hello" and "world" from MyAnnotation and MyOtherAnnotation before Jersey's own servlet processes the request.
This filter logic should be able to work for all requests and all resources registered.
Is there a way to access Jersey's internal routing mechanism to obtain a class/method reference where Jersey will dispatch the request?
I'm open to other suggestions as well, but ideally nothing like trying to hack my own routing mechanism by reading the #Path annotations myself.
#Provider
#Priority(Priorities.AUTHORIZATION)
public class MyFilter implements ContainerRequestFilter
#Context // request scoped proxy
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (resourceInfo.getResourceClass().isAnnotationPresent(MyAnnotationion.class) ||
resourceInfo.getResourceMethod().isAnnotationPresent(MyOtherAnnotation.class)) {
to register the filter use
bind(AuthFilter.class).to(ContainerRequestFilter.class).in(Singleton.class);

Set JAX-RS response headers in implementation without exposing HttpServletResponse in interface

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"
}

How can you access the Response object inside ContainerResponseFilter?

In Jersey 1 you can create a container response filter and get access to the Response:
public ContainerResponse filter(ContainerRequest request, ContainerResponse response)
{
Response r = response.getResponse();
// Now I have access to Reponse.getMetadata(), etc.
}
But in Jersey 2, the ContainerResponseFilter only gives me the response context:
public void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException
{
// responseContext gives me the entity, but I want the JAX-RS Response object that my resources created
}
The implementation of ContainerResponseContext is ContainerResponse, which requires a JAX-RS Response object to build. So why can't I get access to it from within the filter? Am I missing something?
The problem I have is that my resources all build Response objects and attach meta data to them, and I'd like a response filter that can examine this meta data. Without access to the raw Response, this doesn't seem possible.
According to the Javadocs API the Response.getMetadata() is considered deprecated, even though it's not marked as such as of 2.12. The preferred alternative is to use HTTP headers.
you can get access to every response object by implementing a custom ResourceMethodInvocationHandler. Look at my answer # Registering a custom ResourceMethodInvocationHandler in Jersey

How can I extract request attributes from Jersey's ContainerRequest?

HttpServletRequest has a method setAttribute(String, Object).
How can I extract this attribute from ContainterRequest?
I didn't find: getAttribute method!
Code
public class AuthenticationFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpReq = (HttpServletRequest) servletRequest;
// .... ....
httpReq.setAttribute("businessId", businessId);
}
}
In Jersey Filter:
private class Filter implements ResourceFilter, ContainerRequestFilter {
public ContainerRequest filter(ContainerRequest request) {
// ..extract the attribute from the httpReq
}
}
You can't. They're not exposed through the Jersey API in any way. If you search the Jersey codebase, you'll find that there are no uses of HttpServletRequest.getAttributeNames(), which you'd expect to be used if they were being copied en masse. You'll also find that there are only a handful of uses of HttpServletRequest.getAttribute(), and it's strictly for internal bookkeeping.
Note, however, that when deployed in a Servlet Context, JAX-RS allows you to inject the original HttpServletRequest using the #Context annotation. I'm not certain whether you can do this in a Jersey filter, but it works in MessageBodyReaders/Writers and in resource classes.
Update: I've checked, and you can, in fact, inject the HttpServletRequest into a Jersey ContainerRequestFilter by simply including:
#Context private HttpServletRequest httpRequest;
If you're using Jersey 2, which implements JAX-RS 2.0, you can implement a ContainerRequestFilter which defines a filter method as follows:
public void filter(ContainerRequestContext requestContext) throws IOException;
ContainerRequestContext has getProperty(String) and setProperty(String, Object) methods, which in a Servlet environment (ServletPropertiesDelegate), map to the servlet request's getAttribute(String) and setAttribute(String, Object) methods.
See: Jersey on GitHub
I got the #Context working, but have the problem is that my ContainerRequestFilter is singleton.
I had to implement a custom javax.servlet.Filter and use a ThreadLocal to store the HttpServletRequest.
I wanted to add to previous answers my solution, in addition to adding context:
#Context
private HttpServletRequest httpRequest;
You should set and get attributes from the session.
Set:
httpRequest.getSession().setAttribute("businessId", "yourId");
Get:
Object attribute = httpRequest.getSession().getAttribute("businessId");

Categories