jersey 2: How to create custom HTTP param binding

后端 未结 4 2062
醉梦人生
醉梦人生 2020-12-05 15:08

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(Medi         


        
相关标签:
4条回答
  • 2020-12-05 15:16

    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);
       }
    }
    
    0 讨论(0)
  • 2020-12-05 15:22

    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.

    0 讨论(0)
  • 2020-12-05 15:36

    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");
                    }
    
                };
            }
    
    0 讨论(0)
  • 2020-12-05 15:39

    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!

    0 讨论(0)
提交回复
热议问题