Question

I am trying to create a custom http param binding for my restful service. Please see the example below.

@POST
@Path("/user/{userId}/orders")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public MyResult foo(@PathParam("userId") String someString, @UserAuthHeaderParam String authString){

}

You can see that there is a UserAuthHeaderParam annotation in the function signature. What I want to do is have a custom http param binding other than the standard javax.ws.rs.*Param .

I have try to implement org.glassfish.hk2.api.InjectionResolver which basically extract the value from http header:

public class ProtoInjectionResolver implements InjectionResolver<UserAuthHeaderParam>{
...
@Override
public Object resolve(Injectee injectee, ServiceHandle< ? > root)
{

    return "Hello World";
}
...

}

When I call the restful service, the server get below exceptions. It indicates that the framework fails to resolve the param in the function signature:

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee(requiredType=String,parent=MyResource,qualifiers={}),position=0,optional=false,self=false,unqualified=null,2136594195), 

java.lang.IllegalArgumentException: While attempting to resolve the dependencies of rs.server.MyResource errors were found

Please help. Any advise is appreciated. I do make a lot of search on google but fails to make it work. Jersey 2.*. How to replace InjectableProvider and AbstractHttpContextInjectable of Jersey 1.* might be the similar question.

-- UPDATES: I use AbstractBinder to bind my resolver to UserAuthHeaderParam:

public class MyApplication extends ResourceConfig
{

public MyApplication()
{
    register(new AbstractBinder()
    {
        @Override
        protected void configure()
        {
            // bindFactory(UrlStringFactory.class).to(String.class);
            bind(UrlStringInjectResolver.class).to(new TypeLiteral<InjectionResolver<UrlInject>>()
            {
            }).in(Singleton.class);
        }
    });
    packages("rs");

}

}

Thank you!

Was it helpful?

Solution

If all you want is to pass value directly from the header to the method you don't need to create custom annotations. Let's say you have a header Authorization, then you can easily access it by declaring your method like this:

@GET
public String authFromHeader(@HeaderParam("Authorization") String authorization) {
    return "Header Value: " + authorization + "\n";
}

You can test it by calling curl, e.g.

$ curl --header "Authorization: 1234" http://localhost:8080/rest/resource
Header Value: 1234

Given that the answer to your question, how to create custom binding is as follows.

First you have to declare your annotation like this:

@java.lang.annotation.Target(PARAMETER)
@java.lang.annotation.Retention(RUNTIME)
@java.lang.annotation.Documented
public @interface UserAuthHeaderParam {
}

Having your annotation declared you have to define how it will be resolved. Declare the Value Factory Provider (this is where you'll have access to the header parameters - see my comment):

@Singleton
public class UserAuthHeaderParamValueFactoryProvider extends AbstractValueFactoryProvider {

    @Inject
    protected UserAuthHeaderParamValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator locator) {
        super(mpep, locator, Parameter.Source.UNKNOWN);
    }

    @Override
    protected Factory<?> createValueFactory(Parameter parameter) {
        Class<?> classType = parameter.getRawType();

        if (classType == null || (!classType.equals(String.class))) {
            return null;
        }

        return new AbstractHttpContextValueFactory<String>() {
            @Override
            protected String get(HttpContext httpContext) {
                // you can get the header value here
                return "testString";
            }
        };
    }
}

Now declare an injection resolver

public class UserAuthHeaderParamResolver extends ParamInjectionResolver<UserAuthHeaderParam> {
    public UserAuthHeaderParamResolver() {
        super(UserAuthHeaderParamValueFactoryProvider.class);
    }
}

and a Binder for your configuration

public class HeaderParamResolverBinder extends AbstractBinder {

    @Override
    protected void configure() {
        bind(UserAuthHeaderParamValueFactoryProvider.class)
                .to(ValueFactoryProvider.class)
                .in(Singleton.class);

        bind(UserAuthHeaderParamResolver.class)
                .to(new TypeLiteral<InjectionResolver<UserAuthHeaderParam>>() {})
                .in(Singleton.class);
    }
}

now the last thing, in your ResourceConfig add register(new HeaderParamResolverBinder()), like this

@ApplicationPath("rest")
public class MyApplication extends ResourceConfig {
    public MyApplication() {
        register(new HeaderParamResolverBinder());
        packages("your.packages");
    }
}

Given that, you should be now able to use the value as you wanted:

@GET
public String getResult(@UserAuthHeaderParam String param) {
    return "RESULT: " + param;
}

I hope this helps.

OTHER TIPS

I don't know how to resolve your exception. However, may I propose you a different way to do the same thing. I hope it helps.

I've faced exactly the same problem: I need extra parameters in the http header (btw, also related to authentication). Besides, I need to send them in every call, since I want to do a "typical" rest implementation, without maintaining a session.

I'm using Jersey 2.7 - but I'd say it should work in 2.0. I've followed their documentation https://jersey.java.net/documentation/2.0/filters-and-interceptors.html

It's quite clear there, but anyway I copy-paste my implementation below. It works fine. True there are some other ways to secure a rest service, for example this is a good one: http://www.objecthunter.net/tinybo/blog/articles/89

But they depend on the application server implementation and the database you use. The filter, in my opinion, is more flexible and easier to implement.

The copy-paste: I've defined a filter for authentication, which applies to every call and it is executed before the service (thanks to @PreMatching).

@PreMatching
public class AuthenticationRequestFilter implements ContainerRequestFilter {

    @Override
    public void filter(final ContainerRequestContext requestContext) throws IOException {
        final MultivaluedMap<String, String> headers = requestContext.getHeaders();
        if (headers == null) {
            throw new...
        }

        // here I get parameters from the header, via headers.get("parameter_name")
        // In particular, I get the profile, which I plan to use as a Jersey role
        // then I authenticate
        // finally, I inform the Principal and the role in the SecurityContext object, so that I can use @RolesAllowed later
        requestContext.setSecurityContext(new SecurityContext() {

            @Override
            public boolean isUserInRole(final String arg0) {
                //...
            }

            @Override
            public boolean isSecure() {
                //...
            }

            @Override
            public Principal getUserPrincipal() {
                //...
            }

            @Override
            public String getAuthenticationScheme() {
                //...
            }
        });

    }

}

You have to include this filter class in your implementation of ResourceConfig,

public class MyResourceConfig extends ResourceConfig {

    public MyResourceConfig() {

        // my init
        // my packages
        register(AuthenticationRequestFilter.class); // filtro de autenticación
        // other register

    }

}

Hope it helps!

If your need is to retrieve all the http headers binding into one object, a solution could be to use the @Context annotation to get javax.ws.rs.core.HttpHeaders; which contains the list of all request headers.

@POST
@Path("/user/{userId}/orders")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public MyResult foo(@PathParam("userId") String someString, @Context HttpHeaders headers){
 // You can list all available HTTP request headers via following code :
   for(String header : headers.getRequestHeaders().keySet()){
     System.out.println(header);
   }
}

here is my actual implementatipn of UserAuthHeaderParamValueFactoryProvider class

import javax.inject.Inject;
import javax.inject.Singleton;

import org.glassfish.hk2.api.Factory;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.server.internal.inject.AbstractValueFactoryProvider;
import org.glassfish.jersey.server.internal.inject.MultivaluedParameterExtractorProvider;

    import org.glassfish.jersey.server.model.Parameter;

    @Singleton
    public class UserAuthHeaderParamValueFactoryProvider extends AbstractValueFactoryProvider {

        @Inject
        protected UserAuthHeaderParamValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator locator) {
            super(mpep, locator, Parameter.Source.UNKNOWN);
        }

        @Override
        protected Factory<?> createValueFactory(Parameter parameter) {
            Class<?> classType = parameter.getRawType();

            if (classType == null || (!classType.equals(String.class))) {
                return null;
            }

            return new AbstractContainerRequestValueFactory<String>() {
                @Override
                public String provide() {
                    //you can use get any header value.
                    return getContainerRequest().getHeaderString("Authorization");
                }

            };
        }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top