我有一个如下的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) {
....
}
我有一个过滤器,我想在调用上述 Web 服务方法之前拦截请求。
@Provider
@SessionChecker
public class CheckSessionFilter implements ContainerRequestFilter {
@Context
private HttpServletRequest servletRequest;
@Override
public void filter(ContainerRequestContext requestContext) throws WebApplicationException {
....
}
}
在 filter() 方法中,如何将 JSON 消息正文获取到参数类型的 POJO 对象中?我只需要从 JSON 消息中获取一个属性。筛选器完成后,JSON 消息应传递到 Web 服务方法,而无需更改。
提前谢谢。
这就是问题所在。命中筛选器时,尚未读取请求流 ( InputStream
)。因此,如果您尝试读取它,那么泽西岛将无法读取它,因为流只能读取一次,因此它将是空的。
泽西岛实际上为此提供了一个解决方案。ContainerRequestContext
,其实是泽西岛特有的ContainerRequest
的实例。如果您查看链接的 API,您会发现一个bufferEntity()
方法。这使我们能够读取实体,泽西岛将能够再次读取它。所以你的第一步是打这个电话
@Override
public void filter(ContainerRequestContext requestContext)
ContainerRequest cr = (ContainerRequest) requestContext;
cr.bufferEntity();
}
现在,您可以获取实体。如果你看一下 API for ContainerRequest
,也有方法可以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 API,而不是特定于 Jersey 的 API,则上述操作不可行。相反,您需要读取流以获取 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 反序列化程序来执行此操作。我只是在例子中使用了杰克逊。
它不像第一个示例那样优雅,但是如果您严格遵循 JAX-RS API,则没有太多选择。如果可以的话,我建议按照提供的方式将 Jersey 依赖项添加到您的项目中(编译时),以便您可以使用 API,因为无论如何您都在将 Jersey 与 WebLogic 一起使用。