app开发者平台在数字化时代的重要性与发展趋势解析
911
2023-04-11
springboot统一接口返回数据的实现
一,没有异常的情况,正常返回数据
希望接口统一返回的数据格式如下:
{
"status": 0,
"msg": "成功",
"data": null
}
和接口数据对应的bean
/**
* 统一返回结果的实体
* @param
*/
public class Result
private static final long serialVersionUID = 1L;
/**
* 错误码
*/
private int status;
/**
* 提示消息
*/
private String msg;
/**
* 返回的数据体
*/
private T data;
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
操作Result实体的工具类
/**
* 生成result的工具类,避免重复代码
*/
public class ResultUtils {
/**
* 成功时生成result的方法,有返回数据
*/
public static
Result
result.setStatus(ResultEnum.SUCCESS.getCode());
result.setMsg(ResultEnum.SUCCESS.getMsg());
result.setData(t);
return result;
}
/**
* 成功时生成result的方法,无返回数据
*/
public static
return success(null);
}
/**
* 失败时生成result的方法
*/
public static
Result
result.setStatus(status);
result.setMsg(msg);
return result;
}
}
封装错误码和错误消息的枚举类
/**
* 所有返回结果的枚举
*/
public enum ResultEnum {
UNKNOWN_ERROR(-1, "未知错误"),
SUCCESS(0, "成功"),
BASIC_INFO_ID_IS_EMPTY(600, "基本信息中BasicInfoId为空"),
BASIC_INFO_ADD_TO_DATABASE_FAILURE(601, "向数据库添加基本信息失败"),
DETAILS_DATA_BASIC_INFO_ID_IS_EMPTY(602, "测试数据中BasicInfoId为空"),
DETAILS_DATA_ADD_TO_DATABASE_FAILURE(603, "向数据库添加测试数据失败");
ResultEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
private int code;
private String msg;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "ResultEnum{" +
"code=" + code +
", msg='" + msg + '\'' +
'}';
}
}
统一封装返回结果的切面
之所以需要这个切面,是为了避免每个Controller方法中都要调用ResultUtils.success()。有了这个切面,Controller可以和原来一样正常返回对象,字符串,void,在切面里面将结果封装成Result实体,而不需要每个Controller方法都返回Result实体。
/**
* 统一处理返回结果的切面,避免每个contrMqXaZPoller方法里面都要调用ResultUtils.success()这句话
* 统一在这个切面里面调用
*/
@ControllerAdvice
public class MyResponseAdvice implements ResponseBodyAdvice
@Autowired
private ObjectMapper objectMapper;
/**
* Whether this component supports the given controller method return type
* and the selected {@code HttpMessageConverter} type.
*
* @param returnType the reMqXaZPturn type
* @param converterType the selected cMqXaZPonverter type
* @return {@code true} if {@link #beforeBodyWrite} should be invoked;
* {@code false} otherwise
*/
@Override
public boolean supports(MethodParameter returnType, Class extends HttpMessageConverter>> converterType) {
return true;
}
/**
* Invoked after an {@code HttpMessageConverter} is selected and just before
* its write method is invoked.
*
* @param body the body to be written
* @param returnType the return type of the controller method
* @param selectedContentType the content type selected through content negotiation
* @param selectedConverterType the converter type selected to write to the response
* @param request the current request
* @param response the current response
* @return the body that was passed in or a modified (possibly new) instance
*/
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class extends HttpMessageConverter>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if(body instanceof Result){ //发生异常之后,异常处理器里面返回的已经是Result了
return body;
}else if(body instanceof String){ //String属于特殊情况,需要单独处理,否则会报错
try {
return objectMapper.writeValueAsString(ResultUtils.success(body));
} catch (jsonProcessingException e) {
e.printStackTrace();
return ResultUtils.error(ResultEnum.UNKNOWN_ERROR.getCode(), e.getMessage());
}
}
return ResultUtils.success(body);
}
}
二,有异常的情况下
service层为了自动回滚事务,会抛出一些自定义的RuntimeException。默认情况下,只有RuntimeException才会回滚事务。如果Controller里面直接处理service层抛出的异常,则Controller里面到处都是try catch块,代码会很难看。将异常集中在一个地方处理会好很多。
springboot中是通过@ControllerAdvice和@ExceptionHandler来完成统一异常处理的。这2个注解只能处理Controller和-中抛出的异常,其他地方抛出的异常(比如Filter中抛出的异常),无法捕获。其他地方抛出的异常会转到/error的Controller方法来处理,默认是BasicErrorController来处理,为了能处理其他地方抛出的异常,我们会自定义ErrorController。
统一的异常处理类,处理Controller和-抛出的异常
/**
* 统一的异常处理类
*/
@ControllerAdvice
public class MyExceptionHandler {
/**
* 转发到/error,表示由BasicErrorController处理,
* BasicErrorController是由springboot自动装配到容器中的
*/
/*@ExceptionHandler(BasicInfoException.class)
public String handleException(Exception ex, HttpServletRequest request){
request.setAttribute("javax.servlet.error.status_code", 401);
request.setAttribute("exMsg", ex.getMessage());
return "forward:/error";
}*/
/**
* 处理基本信息相关的异常
*/
@ExceptionHandler(BasicInfoException.class)
@ResponseBody
public Result handleBasicInfoException(BasicInfoException ex){
return ResultUtils.error(ex.getCode(), ex.getMessage());
}
/**
* 处理测试数据相关的异常
*/
@ExceptionHandler(DetailsDataException.class)
@ResponseBody
public Result handleDetailsDataException(DetailsDataException ex){
return ResultUtils.error(ex.getCode(), ex.getMessage());
}
/**
* 处理未知异常
*/
@ExceptionHandler(Exception.class)
@ResponseBody
public Result handleUnKnowException(Exception ex){
return ResultUtils.error(ResultEnum.UNKNOWN_ERROR.getCode(), ex.getMessage());
}
}
自定义的异常类示例
public class BasicInfoException extends RuntimeException {
private int code;
public BasicInfoException(int code, String msg){
super(msg);
this.code = code;
}
public int getCode() {
return code;
}
}
处理其他地方抛出的异常(不是Controller和-抛出的异常),自定义ErrorController
/**
* 自定义ErrorController,处理其他地方抛出的异常(不是Controller和-抛出的异常)
*/
@Controller
public class MyBasicErrorController extends AbstractErrorController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 可以通过@Value获取到
*/
@Value("${server.error.path}")
private String myPath;
private final ErrorProperties errorProperties;
private ErrorAttributes mErrorAttributes;
public MyBasicErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties) {
super(errorAttributes);
this.errorProperties = serverProperties.getError();
this.mErrorAttributes = errorAttributes;
}
//@RequestMapping(value = "/error")
@RequestMapping("${server.error.path}") //从properties文件中获取
@ResponseBody
public Result
logger.debug("myPath = " + myPath);
//发生错误之后直接将异常抛出去,异常会到统一异常处理器中处理
WebRequest webRequest = new ServletWebRequest(request);
Throwable throwable = this.mErrorAttributes.getError(webRequest).getCause();
throw throwable;
/*UserException ex;
if(throwable instanceof UserException){
ex = (UserException) throwable;
throw ex;
}else{
throw throwable;
}*/
/*HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return ResultUtils.error(status.value(), status.name());
}
Map
return ResultUtils.error((Integer) body.get("status"), (String)body.get("message"));*/
}
/**
* Determine if the stacktrace attribute should be included.
* @param request the source request
* @param produces the media type produced (or {@code MediaType.ALL})
* @return if the stacktrace attribute should be included
*/
private boolean isIncludeStackTrace(HttpServletRequest request, MediaType produces) {
ErrorProperties.IncludeStacktrace include = getErrorProperties().getIncludeStacktrace();
if (include == ErrorProperties.IncludeStacktrace.ALWAYS) {
return true;
}
if (include == ErrorProperties.IncludeStacktrace.ON_TRACE_PARAM) {
return getTraceParameter(request);
}
return false;
}
/**
* Provide access to the error properties.
* @return the error properties
*/
private ErrorProperties getErrorProperties() {
return this.errorProperties;
}
/**
* Returns the path of the error page.
*
* @return the error path
*/
@Override
public String getErrorPath() {
return this.errorProperties.getPath();
}
}
自定义ErrorController中错误处理的方法中,也可以直接将异常抛出,这样异常就会交给统一异常处理器进行处理。
//@RequestMapping(value = "/error")
@RequestMapping("${server.error.path}") //从properties文件中获取
@ResponseBody
public Result
logger.debug("myPath = " + myPath);
//发生错误之后直接将异常抛出去,异常会到统一异常处理器中处理
WebRequest webRequest = new ServletWebRequest(request);
Throwable throwable = this.mErrorAttributes.getError(webRequest).getCause();
UserException ex;
if(throwable instanceof UserException){
ex = (UserException) throwable;
throw ex;
}else{
throw throwable;
}
/*HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return ResultUtils.error(status.value(), status.name());
}
Map
return ResultUtils.error((Integer) body.get("status"), (String)body.get("message"));*/
}
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~