Придумайте декодер ошибок с настраиваемым ответом JSON

У меня есть API, который вернет ошибку в виде настраиваемого ответного сообщения, при этом отправляя ответ Http 200. Пример:

Status: 200
Body: {
   "code":404,
   "message":"Data not found",
   "data":{},
   "status":"ERROR"
}

Моя текущая реализация ErrorDecoder:

public class ApiFeignErrorDecoder implements ErrorDecoder {

    private final Gson gson;

    public ApiFeignErrorDecoder(Gson gson) {
        this.gson = gson;
    }

    @Override
    public Exception decode(String methodKey, Response response) {

        try {
            ApiResponse apiResponse = gson.fromJson(response.body().asReader(), BaseApiResponse.class);
            ResponseStatus status = apiResponse.getStatus();
            switch (status) {
                case SUCCESS:
                    return null;
                case FAIL:
                    return new Exception("Failure message recorded");
                case ERROR:
                    return new Exception("Error message recorded");
                default:
                    return new Exception("No suitable status found.");
            }
        } catch (IOException e) {
            e.printStackTrace();
            return new ErrorDecoder.Default().decode(methodKey, response);
        }
    }

}

У меня проблема в том, что Feign / Spring переключается на ErrorDecoder только в том случае, если HttpStatus составляет ›300. Документы JavaDoc из декодера ошибок также описывают это ограничение:

Обработка ошибок Ответы, в которых Response.status () не находится в диапазоне 2xx, классифицируются как ошибки, которые обрабатываются ErrorDecoder. Тем не менее, некоторые API RPC возвращают ошибки, определенные в Response.body (), даже при статусе 200. Например, в API DynECT условие выполнения задания возвращается со статусом 200, закодированным в json. Когда возникают подобные сценарии, вы должны вызвать исключение для конкретного приложения (которое может быть повторено).

Теперь у меня вопрос: что и как именно я могу реализовать или есть способ расширить ErrorDecoder таким образом, чтобы я мог обрабатывать эти сообщения об ошибках. Я думаю, что смогу поместить их либо в декодер, либо даже в реализацию / переопределить HttpClient, но я не уверен, какой правильный / лучший способ.


person Stephan Stahlmann    schedule 24.07.2020    source источник


Ответы (1)


Как и ErrorDecoder, существует SpringDecoder, с помощью которого вы можете написать собственную логику для обработки успешных ответов.

private class ResponseDecoder extends SpringDecoder {

    public ResponseDecoder(ObjectFactory<HttpMessageConverters> messageConverters) {
        super(messageConverters);
    }

    @Override
    public Object decode(Response response, Type type) throws IOException, FeignException {
        try {
            ApiResponse apiResponse = gson.fromJson(response.body().asReader(), BaseApiResponse.class);
            ResponseStatus status = apiResponse.getStatus();
            switch (status) {
                case SUCCESS:
                    return null;
                case FAIL:
                    throw new FeignException(FAIL, "Failure message recorded");
                case ERROR:
                    throw new FeignException(ERROR, "Error message recorded");
                default:
                    throw new FeignException(YOUR_DEFAULT_ERROR_CODE, "No suitable status found.");
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null; // return accordingly to your use-case
        }
    }

}

Имитация клиентского интерфейса:

@FeignClient(value = "foo", configuration = FooClientConfig.class)
public interface FooClient {
    //Your mappings
}

Настраиваемая конфигурация клиента Feign:

@Configuration
public class FooClientConfig {

    @Bean
    public Decoder feignDecoder() {
        // if you're using gson, add gson converter here instead
        HttpMessageConverter jacksonConverter = new MappingJackson2HttpMessageConverter(customObjectMapper());
        ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(jacksonConverter);
        return new ResponseEntityDecoder(new ResponseDecoder(objectFactory));
    }

    public ObjectMapper customObjectMapper(){
        ObjectMapper objectMapper = new ObjectMapper();
        //Customize as much as you want
        return objectMapper;
    }
}
person Drunken Daddy    schedule 24.07.2020