我现在开始在JAX-RS中使用新的客户端API库,并且到目前为止真的很喜欢它。但是我发现一件事我不知道。我使用的API具有自定义错误消息格式,例如:
{ "code": 400, "message": "This is a message which describes why there was a code 400." }
它返回400作为状态代码,但还包含描述性错误消息,以告诉您您做错了什么。
但是,JAX-RS 2.0客户端将400状态重新映射为通用状态,因此我丢失了良好的错误消息。它正确地将其映射到BadRequestException,但带有通用的“ HTTP 400 Bad Request”消息。
javax.ws.rs.BadRequestException: HTTP 400 Bad Request at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:908) at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:770) at org.glassfish.jersey.client.JerseyInvocation.access$500(JerseyInvocation.java:90) at org.glassfish.jersey.client.JerseyInvocation$2.call(JerseyInvocation.java:671) at org.glassfish.jersey.internal.Errors.process(Errors.java:315) at org.glassfish.jersey.internal.Errors.process(Errors.java:297) at org.glassfish.jersey.internal.Errors.process(Errors.java:228) at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:424) at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:667) at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:396) at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:296)
是否可以注入某种拦截器或自定义错误处理程序,以便我可以访问真正的错误消息。我一直在浏览文档,但看不到任何方法。
我现在正在使用Jersey,但是我使用CXF进行了尝试,并得到了相同的结果。代码如下所示。
Client client = ClientBuilder.newClient().register(JacksonFeature.class).register(GzipInterceptor.class); WebTarget target = client.target("https://somesite.com").path("/api/test"); Invocation.Builder builder = target.request() .header("some_header", value) .accept(MediaType.APPLICATION_JSON_TYPE) .acceptEncoding("gzip"); MyEntity entity = builder.get(MyEntity.class);
更新:
我实现了以下评论中列出的解决方案。由于类在JAX-RS 2.0客户端API中有所变化,因此它略有不同。我仍然认为默认行为是给出通用错误消息并丢弃真实消息是错误的。我知道为什么它不能解析我的错误对象,但是应该返回未解析的版本。我最终拥有该库已经完成的复制异常映射。
谢谢您的帮助。
这是我的过滤器类:
@Provider public class ErrorResponseFilter implements ClientResponseFilter { private static ObjectMapper _MAPPER = new ObjectMapper(); @Override public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException { // for non-200 response, deal with the custom error messages if (responseContext.getStatus() != Response.Status.OK.getStatusCode()) { if (responseContext.hasEntity()) { // get the "real" error message ErrorResponse error = _MAPPER.readValue(responseContext.getEntityStream(), ErrorResponse.class); String message = error.getMessage(); Response.Status status = Response.Status.fromStatusCode(responseContext.getStatus()); WebApplicationException webAppException; switch (status) { case BAD_REQUEST: webAppException = new BadRequestException(message); break; case UNAUTHORIZED: webAppException = new NotAuthorizedException(message); break; case FORBIDDEN: webAppException = new ForbiddenException(message); break; case NOT_FOUND: webAppException = new NotFoundException(message); break; case METHOD_NOT_ALLOWED: webAppException = new NotAllowedException(message); break; case NOT_ACCEPTABLE: webAppException = new NotAcceptableException(message); break; case UNSUPPORTED_MEDIA_TYPE: webAppException = new NotSupportedException(message); break; case INTERNAL_SERVER_ERROR: webAppException = new InternalServerErrorException(message); break; case SERVICE_UNAVAILABLE: webAppException = new ServiceUnavailableException(message); break; default: webAppException = new WebApplicationException(message); } throw webAppException; } } } }
我相信您想做这样的事情:
Response response = builder.get( Response.class ); if ( response.getStatusCode() != Response.Status.OK.getStatusCode() ) { System.out.println( response.getStatusType() ); return null; } return response.readEntity( MyEntity.class );
您可以尝试做的另一件事(因为我不知道此API将内容放到哪里-即在标头或实体中还是在什么地方)是:
Response response = builder.get( Response.class ); if ( response.getStatusCode() != Response.Status.OK.getStatusCode() ) { // if they put the custom error stuff in the entity System.out.println( response.readEntity( String.class ) ); return null; } return response.readEntity( MyEntity.class );
如果您通常希望将REST响应代码映射到Java异常,则可以添加客户端过滤器来执行此操作:
class ClientResponseLoggingFilter implements ClientResponseFilter { @Override public void filter(final ClientRequestContext reqCtx, final ClientResponseContext resCtx) throws IOException { if ( resCtx.getStatus() == Response.Status.BAD_REQUEST.getStatusCode() ) { throw new MyClientException( resCtx.getStatusInfo() ); } ...
在上面的过滤器中,您可以为每个代码创建特定的异常,或者创建一种用于包装响应代码和实体的通用异常类型。