I am starting to use the new client API library in JAX-RS and really loving it so far. I have found one thing I cannot figure out however. The API I am using has a custom error message format that looks like this for example:

{
    "code": 400,
    "message": "This is a message which describes why there was a code 400."
} 

它返回400作为状态代码,但也包括一条描述性错误消息,告诉您做错了什么.

However the JAX-RS 2.0 client is re-mapping the 400 status into something generic and I lose the good error message. It correctly maps it to a BadRequestException, but with a generic "HTTP 400 Bad Request" message.

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)

是否有某种拦截器或自定义错误处理程序可以被注入,以便我能够访问真正的错误消息.我一直在查看文档,但找不到任何方法.

I am using Jersey right now, but I tried this using CXF and got the same result. Here is what the code looks like.

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);

UPDATE:

I implemented the solution listed in the comment below. It is slightly different since the classes have changed a bit in the JAX-RS 2.0 client API. I still think it is wrong that the default behavior is to give a generic error message and discard the real one. I understand why it wouldn't parse my error object, but the un-parsed version should have been returned. I end up having the replicate exception mapping that the library already does.

谢谢你的帮助.

下面是我的过滤类:

@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;
            }
        }
    }
}

推荐答案

I believe you want to do something like this:

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 );

Another thing you can try (since I don't know where this API puts stuff -- i.e. in the header or entity or what) is:

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 );

If you would like to generally map REST response codes to Java exception you can add a client filter to do that:

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() );
        }

        ...

In the above filter you can create specific exceptions for each code or create one generic exception type that wraps the Response code and entity.

Json相关问答推荐

try 将文本标记放置在条形图上的同一高度

JOLT拉平数组

JSON:将项';S键/名称移动到属性中,并使用JQ将其转换为数组

Terraform迭代JSON文件以获取键值对

在MongoDB中检索某个月份和年份的JSON文档

褐煤面积图中的分选问题

使用JQ从jsonl文件中删除具有匹配键/值的行

使用JQ将对象数组转换为平面数组

将 REST API - json 输出转换为表 Power BI

(已回答)JSON 读取函数返回未定义而不是预期值 - Typescript

Python 将 struct 化文本转换和筛选为对象

转换为Json时忽略内部对象中的数组

将具有多个级别的 json 读入 DataFrame [python]

如何从 JSON 中获取数据并通过 vb6 在网格中显示

将错误消息作为 JSON 对象发送

使用 API 搜索维基百科

使用 JSON.Net 的 C# 到 JSON 序列化

IE10/11 Ajax XHR 错误 - SCRIPT7002:XMLHttpRequest:网络错误 0x2ef3

从调试器获取 IntelliJ Idea 中的 JSON 对象

有没有一种快速的方法可以在文本编辑器中将 JavaScript 对象转换为有效的 JSON?