5

How can/should I pass an object from a ContainerRequestFilter to a (post-matching) resource in (JAX-RS) Resteasy version 3.0.11 that has undertow embedded and uses Guice?

Ari
  • 4,121
  • 8
  • 40
  • 56

1 Answers1

12

The method ContainerRequestContext#setProperty stores values which are synced with the HttpServletRequest. So with plain JAX-RS you can store an attribute like this:

@Provider
public class SomeFilter implements ContainerRequestFilter {

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        requestContext.setProperty("someProperty", "someValue");
    }

}

And afterwards you can obtain it in your resource class:

@GET
public Response someMethod(@Context org.jboss.resteasy.spi.HttpRequest request) {
    return Response.ok(request.getAttribute("someProperty")).build();
}

With CDI you also can inject any bean in the filter and resource class:

@Provider
public class SomeFilter implements ContainerRequestFilter {

    @Inject
    private SomeBean someBean;

    @Override
    public void filter(ContainerRequestContext requestContext) throws IOException {
        someBean.setFoo("bar");
    }

}

In your resource class:

@Inject
private SomeBean someBean;

@GET
public Response someMethod() {
    return Response.ok(someBean.getFoo()).build();
}

I'd expect the same to be working with Guice.

Update: As @bakil pointed out correctly you should use a @RequestScoped bean if the object you want to pass should only be associated with the current request.

lefloh
  • 10,653
  • 3
  • 28
  • 50
  • In the CDI example, I guess the requestContext/request parameters are just distractions, and I guess SomeBean needs to be annotated `@RequestScoped` or similar? – Brett Kail Aug 07 '15 at 13:54
  • Thanks for the hint. I removed the senseless parameters. SomeBean needs to be a CDI bean which means it can have any CDI scope. `@RequestScoped` might make sense in most of the cases. – lefloh Aug 07 '15 at 14:31
  • If the default `@Dependent` scope is used, then the filter and the resource class will receive distinct SomeBean instances, so the data won't be transferred. I think you need `@RequestScoped` to ensure that the same instance is shared by both. – Brett Kail Aug 07 '15 at 16:16
  • Oh sure, `@Dependent` would only work if the filter and the resource class would have the same scope which is not the case. But I tried and it's working with a `@ApplicationScoped`, `@Singleton` and `@SessionScoped` SomeBean (if this ever makes sense). – lefloh Aug 07 '15 at 16:34
  • No, those only appear to work, but they are actually broken. If you have multiple threads simultaneously making a request, then you risk the setFoo/getFoo getting mixed up. – Brett Kail Aug 07 '15 at 16:39
  • But this is the general question when to use which scope. If you want to load user related data on one place to make it available for all resource classes a `@RequestScoped` bean is the right choice. If you want a cache where you store e.g. parsed UserAgents you could choose a wider scope. I would not call this broken. Nevertheless I updated my answer. – lefloh Aug 07 '15 at 18:54
  • Of the two example you provided, I am trying to achieve the former, but unfortunately get the following error: `org.jboss.resteasy.spi.LoggableFailure: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest` Any idea how to solve this? – Ari Aug 12 '15 at 00:02
  • This is working on Wildfly so I'd expected this to work on standalone Undertow also. Could you try what `ResteasyProviderFactory.getContextDataMap()` returns inside your resource class? Maybe you can inject `@Context org.jboss.resteasy.spi.HttpRequest request` – lefloh Aug 12 '15 at 04:36