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

