【问题标题】:Spring REST custom error-object handling in client客户端中的 Spring REST 自定义错误对象处理
【发布时间】:2019-02-21 10:31:14
【问题描述】:

Tl;博士;

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

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

除了检查原始字符串然后手动解析之外,还有什么方法可以做到这一点?

详细说明

我有两个 Spring 服务。

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

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

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

"server-service" - 代码

MyPojo

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

    //hashCode & equals & toString
}

REST错误

以下是自定义错误对象。它有一般用户的errorMessage和开发者的developerMessage。

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

    private String errorMessage, developerMessage;

    //hashCode & equals & toString
}

@ControllerAdvice

ExceptionHandlerAdvice 类将处理所有抛出的异常并返回 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;
    }
}

“客户服务-代码”

调用“server-service”的客户端方法

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
}

MyCustomResponseErrorHandler

@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.");
        }
    }
}

问题

如何处理我的自定义 RESTError?

通过使用 RestTemplate,我只能解析一次 JSON,并且只能将其解析为一个类(MyPojo.class 或 RESTError.class)。 因此,我可以解析为字符串,检查字符串,然后将 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);
}

我不喜欢这个解决方案,如果有任何其他建议,我将不胜感激。

【问题讨论】:

  • 这是说RestErr SuperMyPojo。这是真的吗?似乎MyPojo 偶尔有一个 错误。考虑组合而不是继承。
  • Composition 仍然不能解决我的选角问题(我认为)。如果您有一种组合对象的方法,可以解决我的问题,请继续并将其作为答案发布。
  • 基于this,考虑让MyCustomResponseErrorHandler在响应有错误时抛出异常。由于有错误,响应将有一个RESTErr,可以反序列化(在错误处理程序中)并包装在异常中。
  • @Andrew S 有效。我仍然不是 100% 满意,因为我希望得到这样的答案:restTemplate.getForObject(url, ResponseObject.class, ErrorObject.class);但是,嘿,你不可能拥有一切,对吧;)

标签: java spring rest spring-boot error-handling


【解决方案1】:

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

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

@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 作为其父级才能正确处理。

【讨论】:

    【解决方案2】:

    您的 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 捕获。

    如果我正确理解了您的问题。

    【讨论】:

    • IOException 由 com.fasterxml.jackson.databind.ObjectMapper.readValue(String json, Class returnObject) 抛出。我添加了 MyPojoSuper 类
    【解决方案3】:

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

        try {
          restTemplate.getForObject(url, MyPojo.class);
         } catch (HttpStatusCodeException e) {
             if (e.getStatusCode() == 404) {
    
               String body = e.getResponseBodyAsString();
    
               // do smth with error json
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2015-04-26
      • 1970-01-01
      • 1970-01-01
      • 2021-12-17
      • 1970-01-01
      • 2014-04-28
      • 1970-01-01
      • 2012-03-30
      • 1970-01-01
      相关资源
      最近更新 更多