springboot統(tǒng)一接口返回數(shù)據(jù)的實(shí)現(xiàn)
一,沒有異常的情況,正常返回數(shù)據(jù)
希望接口統(tǒng)一返回的數(shù)據(jù)格式如下:
{ "status": 0, "msg": "成功", "data": null }
和接口數(shù)據(jù)對應(yīng)的bean
/** * 統(tǒng)一返回結(jié)果的實(shí)體 * @param <T> */ public class Result<T> implements Serializable { private static final long serialVersionUID = 1L; /** * 錯誤碼 */ private int status; /** * 提示消息 */ private String msg; /** * 返回的數(shù)據(jù)體 */ 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實(shí)體的工具類
/** * 生成result的工具類,避免重復(fù)代碼 */ public class ResultUtils { /** * 成功時生成result的方法,有返回數(shù)據(jù) */ public static <T> Result<T> success(T t){ Result<T> result = new Result<>(); result.setStatus(ResultEnum.SUCCESS.getCode()); result.setMsg(ResultEnum.SUCCESS.getMsg()); result.setData(t); return result; } /** * 成功時生成result的方法,無返回數(shù)據(jù) */ public static <T> Result<T> success(){ return success(null); } /** * 失敗時生成result的方法 */ public static <T> Result<T> error(int status, String msg){ Result<T> result = new Result<>(); result.setStatus(status); result.setMsg(msg); return result; } }
封裝錯誤碼和錯誤消息的枚舉類
/** * 所有返回結(jié)果的枚舉 */ public enum ResultEnum { UNKNOWN_ERROR(-1, "未知錯誤"), SUCCESS(0, "成功"), BASIC_INFO_ID_IS_EMPTY(600, "基本信息中BasicInfoId為空"), BASIC_INFO_ADD_TO_DATABASE_FAILURE(601, "向數(shù)據(jù)庫添加基本信息失敗"), DETAILS_DATA_BASIC_INFO_ID_IS_EMPTY(602, "測試數(shù)據(jù)中BasicInfoId為空"), DETAILS_DATA_ADD_TO_DATABASE_FAILURE(603, "向數(shù)據(jù)庫添加測試數(shù)據(jù)失敗"); 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 + '\'' + '}'; } }
統(tǒng)一封裝返回結(jié)果的切面
之所以需要這個切面,是為了避免每個Controller方法中都要調(diào)用ResultUtils.success()。有了這個切面,Controller可以和原來一樣正常返回對象,字符串,void,在切面里面將結(jié)果封裝成Result實(shí)體,而不需要每個Controller方法都返回Result實(shí)體。
/** * 統(tǒng)一處理返回結(jié)果的切面,避免每個controller方法里面都要調(diào)用ResultUtils.success()這句話 * 統(tǒng)一在這個切面里面調(diào)用 */ @ControllerAdvice public class MyResponseAdvice implements ResponseBodyAdvice<Object> { @Autowired private ObjectMapper objectMapper; /** * Whether this component supports the given controller method return type * and the selected {@code HttpMessageConverter} type. * * @param returnType the return type * @param converterType the selected converter 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){ //發(fā)生異常之后,異常處理器里面返回的已經(jīng)是Result了 return body; }else if(body instanceof String){ //String屬于特殊情況,需要單獨(dú)處理,否則會報錯 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層為了自動回滾事務(wù),會拋出一些自定義的RuntimeException。默認(rèn)情況下,只有RuntimeException才會回滾事務(wù)。如果Controller里面直接處理service層拋出的異常,則Controller里面到處都是try catch塊,代碼會很難看。將異常集中在一個地方處理會好很多。
springboot中是通過@ControllerAdvice和@ExceptionHandler來完成統(tǒng)一異常處理的。這2個注解只能處理Controller和攔截器中拋出的異常,其他地方拋出的異常(比如Filter中拋出的異常),無法捕獲。其他地方拋出的異常會轉(zhuǎn)到/error的Controller方法來處理,默認(rèn)是BasicErrorController來處理,為了能處理其他地方拋出的異常,我們會自定義ErrorController。
統(tǒng)一的異常處理類,處理Controller和攔截器拋出的異常
/** * 統(tǒng)一的異常處理類 */ @ControllerAdvice public class MyExceptionHandler { /** * 轉(zhuǎn)發(fā)到/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"; }*/ /** * 處理基本信息相關(guān)的異常 */ @ExceptionHandler(BasicInfoException.class) @ResponseBody public Result handleBasicInfoException(BasicInfoException ex){ return ResultUtils.error(ex.getCode(), ex.getMessage()); } /** * 處理測試數(shù)據(jù)相關(guān)的異常 */ @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<Object> error(HttpServletRequest request) throws Throwable { logger.debug("myPath = " + myPath); //發(fā)生錯誤之后直接將異常拋出去,異常會到統(tǒng)一異常處理器中處理 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<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); 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中錯誤處理的方法中,也可以直接將異常拋出,這樣異常就會交給統(tǒng)一異常處理器進(jìn)行處理。
//@RequestMapping(value = "/error") @RequestMapping("${server.error.path}") //從properties文件中獲取 @ResponseBody public Result<Object> error(HttpServletRequest request) throws Throwable { logger.debug("myPath = " + myPath); //發(fā)生錯誤之后直接將異常拋出去,異常會到統(tǒng)一異常處理器中處理 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<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); return ResultUtils.error((Integer) body.get("status"), (String)body.get("message"));*/ }
到此這篇關(guān)于springboot統(tǒng)一接口返回數(shù)據(jù)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)springboot統(tǒng)一接口返回數(shù)據(jù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot使用CommandLineRunner解決項(xiàng)目啟動時初始化資源的操作
這篇文章主要介紹了springboot使用CommandLineRunner解決項(xiàng)目啟動時初始化資源的操作,幫助大家更好的理解和學(xué)習(xí)使用springboot框架,感興趣的朋友可以了解下2021-02-02springboot中縮短一個url鏈接的實(shí)現(xiàn)
縮短 URL 是現(xiàn)代應(yīng)用程序中常見的需求,通常用于減少長 URL 的長度,使其更易于分享,URL 縮短服務(wù)的核心思路是將長 URL 映射到一個唯一的短代碼,本文主要介紹了springboot中縮短一個url鏈接的實(shí)現(xiàn),感興趣的可以了解一下2024-09-09FileUtils擴(kuò)展readURLtoString讀取url內(nèi)容
這篇文章主要介紹了FileUtils擴(kuò)展readURLtoString使用其支持讀取URL內(nèi)容為String,支持帶POST傳大量參數(shù),大家參考使用吧2014-01-01Java Socket編程(三) 服務(wù)器Sockets
Java Socket編程(三) 服務(wù)器Sockets...2006-12-12spring AOP實(shí)現(xiàn)@Around輸出請求參數(shù)和返回參數(shù)
這篇文章主要介紹了spring AOP實(shí)現(xiàn)@Around輸出請求參數(shù)和返回參數(shù),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02Java中zip文件壓縮與解壓之ZipInputStream和ZipOutputStream
這篇文章主要給大家介紹了關(guān)于Java中zip文件壓縮與解壓之ZipInputStream和ZipOutputStream的相關(guān)資料,ZipInputStream 和 ZipOutputStream 可以用于處理 ZIP文件格式,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10