1

使用 Jersey 1.17.1,我实现了一个基本的 RESTful API,并将其作为一个 servlet 部署在 Google App Engine 1.7.5 中。我正在使用在 HTTP 请求标头参数中传输的令牌对用户进行身份验证。我成功@HeaderParam地提取了令牌并使用令牌对用户进行了身份验证,但我想创建一个自定义注释来为我处理身份验证。但是,泽西岛似乎没有选择提供者。

我定义了一个注释:

package com.kwogger.api.web.rs.ext;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthUser {
  boolean required() default true;
}

和提供者:

package com.kwogger.api.web.rs.ext;

import com.googlecode.objectify.Key;
import com.sun.jersey.api.core.HttpContext;
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider;
import com.kwogger.om.User;

@Provider
public class AuthUserProvider extends PerRequestTypeInjectableProvider<AuthUser, Key<User>> {
  public AuthUserProvider() {
    super(Key.class);
  }

  @Override
  public Injectable<Key<User>> getInjectable(ComponentContext ic, final AuthUser a) {
    return new AbstractHttpContextInjectable<Key<User>>() {
      @Override
      public Key<User> getValue(HttpContext ctxt) {
        // ... authenticate user here ...
      }
    };
  }
}

在我的资源中,我修改了端点:

  @GET
  public JsonNode getVault() {
    Key<User> user = authUser();
  }

  private String userToken;

  @HeaderParam(Constants.HEADER_USER_TOKEN)
  public void setUserToken(String token) {
    if (token != null) {
      userToken = token;
    }
  }

  @CookieParam(Constants.COOKIE_USER_TOKEN)
  public void setUserTokenCookie(String cookie) {
    if (cookie != null) {
      userToken = cookie;
    }
  }

  private Key<User> authUser() {
    // ... authenticate user here ...
  }

对此:

  @GET
  public JsonNode getItem(@AuthUser Key<User> user) {
    // do stuff...
  }

但是,当我尝试访问端点时,我在日志中收到带有以下错误消息的 HTTP 415:

com.sun.jersey.spi.container.ContainerRequest getEntity: A message body reader for Java class com.googlecode.objectify.Key, and Java type com.googlecode.objectify.Key<com.kwogger.om.User>, and MIME media type application/octet-stream was not found.
The registered message body readers compatible with the MIME media type are:
application/octet-stream ->
  com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
  com.sun.jersey.core.impl.provider.entity.FileProvider
  com.sun.jersey.core.impl.provider.entity.InputStreamProvider
  com.sun.jersey.core.impl.provider.entity.DataSourceProvider
  com.sun.jersey.core.impl.provider.entity.RenderedImageProvider
*/* ->
  com.sun.jersey.core.impl.provider.entity.FormProvider
  com.sun.jersey.core.impl.provider.entity.MimeMultipartProvider
  com.sun.jersey.json.impl.provider.entity.JSONJAXBElementProvider$General
  com.sun.jersey.json.impl.provider.entity.JSONArrayProvider$General
  com.sun.jersey.json.impl.provider.entity.JSONObjectProvider$General
  com.sun.jersey.core.impl.provider.entity.StringProvider
  com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
  com.sun.jersey.core.impl.provider.entity.FileProvider
  com.sun.jersey.core.impl.provider.entity.InputStreamProvider
  com.sun.jersey.core.impl.provider.entity.DataSourceProvider
  com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
  com.sun.jersey.core.impl.provider.entity.ReaderProvider
  com.sun.jersey.core.impl.provider.entity.DocumentProvider
  com.sun.jersey.core.impl.provider.entity.SourceProvider$StreamSourceReader
  com.sun.jersey.core.impl.provider.entity.SourceProvider$SAXSourceReader
  com.sun.jersey.core.impl.provider.entity.SourceProvider$DOMSourceReader
  com.sun.jersey.json.impl.provider.entity.JSONRootElementProvider$General
  com.sun.jersey.json.impl.provider.entity.JSONListElementProvider$General
  com.sun.jersey.json.impl.provider.entity.JacksonProviderProxy
  com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLRootObjectProvider$General
  com.sun.jersey.core.impl.provider.entity.EntityHolderReader

同一资源文件中的每个其他端点都可以正常工作。我已经com.sun.jersey.config.property.packages在 servlet 定义的 init-param 中指定了包,并且ExceptionMapper位于与自定义提供程序工作相同的包中。我没有使用 Spring 或 Guice。

我错过了什么?似乎泽西岛正在尝试Key<User> user从身体中读取,这让我认为提供者根本没有被捡起,但我似乎无法弄清楚如何让它工作。

4

1 回答 1

2

您的Key<User>类没有被拦截,因为在 Jersey 运行时注册的提供者需要一个原始类型 ( Key),正如您的提供者构造函数中指定的那样:

public AuthUserProvider() {
    super(Key.class);
}

但是您正在尝试注入参数化类型,因此您需要适当地调用超类构造函数。获得所需实际类型的最简单(可能也是唯一)方法是通过超类型标记。下面是一个使用 JacksonTypeReference类作为助手的示例:

public AuthUserProvider() {
    super(new TypeReference<Key<User>>() {
    }.getType());
}

这一行:

new TypeReference<Key<User>>() {}.getType()

基本上允许您创建一个保留其通用信息的类型(在这种情况下,它表示一个Key<User>.

除了 Jackson TypeReference 类,您可以使用 Jersey Client GenericType或 Gson TypeToken来完成同样的事情。

于 2013-04-02T21:57:39.647 回答