Custom annotation injection with Jersey 1.x

2019-05-10 19:45发布

问题:

I am using jersey 1.9.1. I have rest method like following where Authorization header contained encoded credentials such as username and password and it is parsed in a method and mapped local values.

@PUT
@Path(SystemConstants.REST_MESSAGE_SENDSMS)
@Consumes(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_JSON})
public Response sendSms(@HeaderParam("Authorization") String authorization, String param) {

    String[] credentials = ImosUtils.getUserCredentials(authorization);
    String username = credentials[0];
    String password = credentials[1];       
    }

I am trying to design a way to make this process automatically, without writing same parsing code in each method. I mean I would like to know if writing a special annotation such as HeaderParamExtended to this is used to parse this credentials.
I am using jersey 1.9.1 version as rest api. Where I have to edit a class in that life cycle?

@PUT
@Path(SystemConstants.REST_MESSAGE_SENDSMS)
@Consumes(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_JSON})
public Response sendSms(@HeaderParamExtended("Authorization","username") String username, @HeaderParamExtended("Authorization","password") String password, , String param) {


    }

回答1:

Normally you need an InjectableProvider to support the custom injection, and also an Injectable to provide the value.

Here's an example

@BasicAuth

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface BasicAuth {
}

InjectableProvider

@Provider
public class BasicAuthInjectionProvider
        implements InjectableProvider<BasicAuth, Parameter> {

    @Override
    public ComponentScope getScope() {
        return ComponentScope.PerRequest;
    }

    @Override
    public Injectable getInjectable(ComponentContext cc, BasicAuth a, Parameter c) {
        return new BasicAuthInjectable();
    }
}

Injectable

public class BasicAuthInjectable extends AbstractHttpContextInjectable<User>{

    @Override
    public User getValue(HttpContext hc) {
        String authHeaderValue = hc.getRequest()
                .getHeaderValue(HttpHeaders.AUTHORIZATION);
        String[] credentials = ImosUtils.getUserCredentials(authHeaderValue);

        return new User(credentials[0], credentials[1]);
    }  
}

One thing you'll notice is that I have a User class. This is to wrap the username and password, and just have one injection point. i.e.

public Response getSomething(@BasicAuth User user) {
}

I actually tried to do it your way, with

public Response getSomething(@BasicAuth("username") String username,
                             @BasicAuth("password") String password) {

And in the InjectableProvider get the annotation value from the annotation passed to the getInjectable, then pass that value onto the BasicAuthInjectable. From there check to see if the value is "username" or "password" and return the corresponding value. But for some reason the injection providers were not even called. You can play around with it to see if you can get it to work. But to me the User looks cleaner anyway, and with the two strings, the injection providers are called twice and you need to parse the headers twice. Seems unnecessary.