客户端中的Spring REST自定义错误对象处理

问题描述 投票:1回答:3

Tl;Dr;

我在Spring-Boot中有一个REST控制器,要么返回MyPojo.class,要么返回RESTError.class。

然后,我使用RestTemplate从另一个Spring Service调用此控制器,并希望获得MyPojo对象或RESTError对象。

有没有办法做到这一点,除了检查原始字符串,然后手动解析?

Detailed description

我有两个春季服务。

让我们称他们为“服务器服务”和“客户服务”。 “客户端服务”必须从“服务器服务”中检索POJO。如果出现问题(无效参数,缺少参数等),将返回自定义错误对象,该错误对象通过“开发人员消息”和更广泛的“用户消息”详细说明错误。

这样,如果发生错误,我将能够在ui中显示“用户消息”。

这只是最后的手段。在我调用“服务器服务”之前,我当然应首先在我的“客户端服务”中进行验证。然而,想象有人在我不知情的情况下改变“服务器服务”......

"server-service" - Code

MyPojo

@Getter @Setter @Accessors(chain = true)
public class MyPojo extends MyPojoSuper{
    private String someString;

    //hashCode & equals & toString
}

RESTError

以下是自定义错误对象。它具有针对普通用户的errorMessage和针对开发人员的developerMessage。

@JsonIgnoreProperties(ignoreUnknown = true)
@Getter @Setter @Accessors(chain = true)
public class RESTError extends MyPojoSuper{

    private String errorMessage, developerMessage;

    //hashCode & equals & toString
}

@ControllerAdvice

Exception Handler Advice类将处理所有抛出的异常并返回RESTError。

@ControllerAdvice
@RequestMapping("/error")
@Slf4j
public class ExceptionHandlerAdvice {

    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    @ResponseBody
    public RESTError missingServletRequestParameterException(MissingServletRequestParameterException ex) {

    log.info("A parameter['" + ex.getParameterName() +"'] is missing.", ex);
    RESTError error = new RESTError();
    error.setDeveloperMessage(ex.getMessage());
    error.setErrorMessage("The parameter['" + ex.getParameterName() +"'] is missing.");

    return error;
    }
}

"client-service - Code"

调用“服务器服务”的客户端方法

public MyPojo getMyPojo(){
    restTemplate.setErrorHandler(new MyCustomResponseErrorHandler());
    return restTemplate.getForObject(getURLWithParams(), MyPojo.class);
}

RESTError客户端

@JsonIgnoreProperties(ignoreUnknown = true)
@Getter @Setter @Accessors(chain = true)
public class RESTError extends MyPojoSuper{

    private String errorMessage, developerMessage;

    //hashCode & equals & toString
}

MyPojoSuper

@Getter @Setter @Accessors(chain = true)
public class MyPojoSuper{
    public boolean error;

    //equals & hashCode & toString
}

我的自定义ResponseErrorHandler

@Slf4j
public class MyCustomResponseErrorHandler implements ResponseErrorHandler {

    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        if (response.getStatusCode() != HttpStatus.OK) {
            log.info("There was an error.");
            return true;
        }
        return false;
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        if (response.getStatusCode() != HttpStatus.OK) {
            log.info("There was an error.");
        }
    }
}

Problem

如何处理自定义RESTError?

通过使用RestTemplate,我只能解析一次JSON,我只能将它解析为一个Class(MyPojo.class或RESTError.class)。因此,我可以解析为String,检查String,然后将JSON重新解析为所需的格式:

public MyPojoSuper getMyPojo() {
    ObjectMapper mapper = new ObjectMapper();
    restTemplate.setErrorHandler(new MyCustomResponseErrorHandler());
    String res = restTemplate.getForObject(getURL(), String.class);

    try {
        if (res.contains("\"error\":true")) {
            return mapper.readValue(res, RESTError.class);
        } else {
            return mapper.readValue(res, MyPojo.class);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return new RESTResponse().setError(true);
}

我不喜欢这个解决方案,并希望得到任何其他建议。

java spring rest spring-boot error-handling
3个回答
0
投票

您的RESTError类是MyPojoSuper类的子类

RESTError extends MyPojoSuper

在你的try-catch中,你特意捕获IOException

try {
        if (res.contains("\"error\":true")) {
            return mapper.readValue(res, RESTError.class);
        } else {
            return mapper.readValue(res, MyPojo.class);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

你能分享一下MyPojoSuper.java的类代码吗?它应该至少是IOException的子类,以便它可以在try catch中捕获。

如果我正确理解你的问题。


0
投票

根据@Andrew S的评论,这是一个解决方案:

可以通过在自定义类中创建实现ResponseErrorHandler的类并使用ResponseErrorHandler注册来注册RestTemplate

@Slf4j
public class MyCustomResponseErrorHandler implements ResponseErrorHandler {

    private ObjectMapper mapper = new ObjectMapper();

    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        if (response.getStatusCode() != HttpStatus.OK) {
            log.info("loggin stuff");
            return true;
        }
    return false;
    }

    @Override
    public void handleError(ClientHttpResponse response) throws     JsonParseException, JsonMappingException, IOException{
        if (response.getStatusCode() != HttpStatus.OK) {
            log.info("logging stuff");
            throw new RestErrorExeption(mapper.readValue(response.getBody(),     RESTError.class));
        }
    }
}

如图所示,这个类现在抛出一个自定义异常,RestErrorExeption。它包含一个RESTError对象。然后,可以通过调用mapper.readValue(response.getBody(), RESTError.class)来解析响应主体中的JSON。 (Mapper是com.fasterxml.jackson.databind.ObjectMapper的一个实例)

然后可以捕获异常并从RestErrorExeption获取解析的JSON作为POJO:

public MyPojoSuper getMyPojo() {
    restTemplate.setErrorHandler(new MyCustomResponseErrorHandler());

    try {
        return restTemplate.getForObject(getURLWithParams(), MyPojo.class);
    } catch (RestErrorExeption e) {
        log.info(e.getMessage());
        return e.getRestError();
    }
}

请注意,RESTError.classMyPojo.class需要MyPojoSuper.class作为他们的父母才能得到妥善处理。


0
投票

据我所知,当你使用restTemplate时,如果响应代码不等于HttpStatusCodeException,它会抛出2xx。这就是为什么我认为你必须抓住异常。喜欢

    try {
      restTemplate.getForObject(url, MyPojo.class);
     } catch (HttpStatusCodeException e) {
         if (e.getStatusCode() == 404) {

           String body = e.getResponseBodyAsString();

           // do smth with error json
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.