我在Spring-Boot中有一个REST控制器,要么返回MyPojo.class,要么返回RESTError.class。
然后,我使用RestTemplate从另一个Spring Service调用此控制器,并希望获得MyPojo对象或RESTError对象。
有没有办法做到这一点,除了检查原始字符串,然后手动解析?
我有两个春季服务。
让我们称他们为“服务器服务”和“客户服务”。 “客户端服务”必须从“服务器服务”中检索POJO。如果出现问题(无效参数,缺少参数等),将返回自定义错误对象,该错误对象通过“开发人员消息”和更广泛的“用户消息”详细说明错误。
这样,如果发生错误,我将能够在ui中显示“用户消息”。
这只是最后的手段。在我调用“服务器服务”之前,我当然应首先在我的“客户端服务”中进行验证。然而,想象有人在我不知情的情况下改变“服务器服务”......
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;
}
}
调用“服务器服务”的客户端方法
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.");
}
}
}
如何处理自定义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);
}
我不喜欢这个解决方案,并希望得到任何其他建议。
您的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中捕获。
如果我正确理解你的问题。
根据@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.class
和MyPojo.class
需要MyPojoSuper.class
作为他们的父母才能得到妥善处理。
据我所知,当你使用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
}
}