[Jersey] RESTful service starter-kit (2)

如何設定Custom Injection

場景:
client發送get request,會在header帶一個userId,server會根據userId去資料庫查詢相關user.
例如:

1
curl -H 'userId: 89ce5a60-a88f-11e5-a837-0800200c9a66' http://localhost:8080/user

PS:正規的做法是把userId放在uri path中,這邊只是模擬從header抓取資料進行處理,之後可以透過annotation方式將結果放到resource

  • 定義annotation

    1
    2
    3
    4
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
    public @interface UserParam {
    }
  • 定義ValueFactoryProvider

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    @Singleton
    @Slf4j //lombok annotation
    public final class UserParamValueFactoryProvider extends AbstractValueFactoryProvider {

    @Singleton
    public static final class InjectionResolver extends ParamInjectionResolver<UserParam> {
    public InjectionResolver() {
    super(UserParamValueFactoryProvider.class);
    }
    }

    @Inject
    public UserParamValueFactoryProvider(MultivaluedParameterExtractorProvider mpep, ServiceLocator injector) {
    super(mpep, injector, Parameter.Source.UNKNOWN);
    }

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

    if (classType == null || (!classType.equals(User.class))) {
    log.warn("UserParam annotation was not placed on correct object type; Injection might not work correctly!");
    return null;
    }

    return new AbstractContainerRequestValueFactory<User> (){
    @Context
    private ResourceContext context;
    @Inject
    private Repo repo;

    public User provide() {
    final ContainerRequestContext context = this.context.getResource(ContainerRequestContext.class);
    final UUID userId = UUID.fromString(context.getHeaderString("userId"));
    return repo.getUser(userId);
    }
    };
    }
    }

Repo

1
2
3
public interface Repo {
User getUser(UUID uuid);
}

RepoFactory

1
2
3
4
5
6
7
8
9
10
11
public class RepoFactory implements Factory<Repo> {
@Override
public Repo provide() {
return new DummyRepo();
}

@Override
public void dispose(Repo instance) {

}
}

  • bind UserParamValueFactoryProvider

    bind(UserParamValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
    1
    2
    bind(UserParamValueFactoryProvider.InjectionResolver.class).to(new TypeLiteral<InjectionResolver<UserParam>>() {}).in(Singleton.class);
    bindFactory(RepoFactory.class).to(Repo.class).in(Singleton.class);
  • 透過annotation取得User

    1
    2
    3
    4
    5
    6
    @GET
    @ApiOperation(value = "Return user data", response = User.class)
    public Response user(@NotNull @Valid @UserParam User user) {
    return Response.ok(user, MediaType.APPLICATION_JSON)
    .build();
    }

如何設定Swagger API

因為是透過ResourceConfig去設定JAX-RS,所以只要將以下程式碼加入到ResourceConfig就完成Swagger API設定

1
2
3
4
5
6
7
8
9
10
11
12
register(ApiListingResourceJSON.class);
register(SwaggerSerializers.class);

BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.1");
beanConfig.setResourcePackage("rest"); // replace with your packages
beanConfig.setBasePath("http://localhost:8080/jersey-starterkit/");
beanConfig.setDescription("My RESTful resources");
beanConfig.setTitle("My RESTful API");
beanConfig.setFilterClass(ApiAuthorizationFilterImpl.class.getName());
beanConfig.setPrettyPrint(true);
beanConfig.setScan(true);

假如想要用apiKey限制使用者存取swagger.json,可以繼承SwaggerSpecFilter,在beanConfig.setFilterClass設定路徑即可.

呼叫swagger.json:

1
curl http://localhost:8080/swagger.json

程式碼:jersey-starterkit

參考資料: