在 JAX-RS Web 服务过滤器中获取 JSON 消息

Get JSON message in JAX-RS web service filter

我有如下的 Web 服务方法(部署在 WebLogic 12.2.1 上),我可以在 POJO 对象 "requestParameters":

中接收 JSON 请求主体
@POST
@SessionChecker
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("LogIn")
public Response logIn(@Context HttpServletRequest request, Parameters requestParameters) {
    ....
}

我有一个过滤器,我想在调用上述网络服务方法之前拦截请求。

@Provider
@SessionChecker
public class CheckSessionFilter implements ContainerRequestFilter {

    @Context
    private HttpServletRequest servletRequest;

    @Override
    public void filter(ContainerRequestContext requestContext) throws WebApplicationException {
        ....
    }

}

在filter()方法中,如何将JSON消息体放入Parameters类型的POJO对象中?我只需要从 JSON 消息中获取一个属性。过滤完成后,JSON 消息应原封不动地传递到 Web 服务方法。

提前致谢。

问题来了。当您的过滤器被命中时,请求流 (InputStream) 尚未被读取。因此,如果您尝试读取它,那么 Jersey 将无法读取它,因为流只能读取一次,因此它将是空的。

泽西实际上提供了解决方案。 ContainerRequestContext 实际上是 Jersey 特定 ContainerRequest 的一个实例。如果您查看链接的 API,您会发现一个 bufferEntity() 方法。这允许我们读取实体,并且 Jersey 将能够再次读取它。所以你的第一步是打电话

@Override
public void filter(ContainerRequestContext requestContext)
    ContainerRequest cr = (ContainerRequest) requestContext;
    cr.bufferEntity();
}

现在你可以得到实体了。如果您查看 ContainerRequest 的 API,还有 readEntity(..) 的方法。如果您熟悉 JAX-RS 客户端 API,您可能以前使用过 Response#readEntity(...class) 来读取响应实体。 ContainerRequest#readEntity(..) 的工作方式几乎相同。

所以如果你知道 JSON 格式应该是什么,并且你有 POJO,你就可以做到

POJO pojo = cr.readEntity(POJO.class);

否则,如果格式会因请求而异,您可以将数据提取为地图

Map<String, Object> json = cr.readEntity(new GenericType<Map<String, Object>>(){});

更新

如果您使用的是一个 JAX-RS APIs,而不是 Jersey 特定的 APIs,则以上内容不可行。相反,您需要读取流以获取 JSON,然后将流设置回去,以便 Jersey 可以读取它。如果看起来像

InputStream entityIn = requestContext.getEntityStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// write `entityIn` to `baos`
byte[] bytes = baos.toByteArray();
POJO pojo = new ObjectMapper().readValue(bytes, POJO.class);
// do something with POJO
requestContext.setEntityStream(new ByteArrayInputStream(bytes));

当然,您需要一些 JSON 反序列化器来执行此操作。我只是在示例中使用了 Jackson。

它不像第一个示例那样优雅,但如果您严格遵守 JAX-RS APIs,您将没有太多选择。如果可以的话,我建议只按照提供的方式(编译时)将 Jersey 依赖项添加到您的项目中,这样您就可以使用 APIs,因为无论如何您都在将 Jersey 与 WebLogic 结合使用。