springboot自定義404,415錯誤處理(親測可用,易懂)
阿新 • • 發佈:2019-02-05
- 最近做一個專案,專案絕大多數請求都用的是ajax請求,但是,如果是
4XX
錯誤的話,springboot返回它自己的一套json,(全域性異常處理是捕獲不到這種錯誤的)如下:
{
"timestamp": 1538032849685,
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/x-www-form-urlencoded' not supported",
"path": "/account/login"
}
- 我們知道,做rest api,我們的資料格式是統一的,而springboot給的我們如此格式,肯定不是我們想要的,我查了好多資料,發現說法雲的霧的,好多純屬誤導,我不知道他們寫的時候,親測過沒有,真實放在專案中沒有!
- 所以決定自己動手造輪子,追尋原始碼,我們會發現,上述json串是springboot裡面一個
BasicErrorController
中的error
方法返回的,而分析它的兩個方法,一個error
,一個errorHtml
已經覆蓋了所有的請求型別,因為你的請求要麼是瀏覽器請求,要麼不是,你想繼承BasicErrorController
複寫方法?人家已經定義好返回型別,你怎麼複寫?然而,查資料我們會發現,如果有其他的ErrorController
的實現,則預設的BasicErrorController
將失去效果,如此,springboot就將這個錯誤處理的權利完全交給了程式猿
,我們可以根據我們的具體需求,實現我們自己的ErrorController,其實BasicErrorController從某種意義上來說,是springboot提供給使用者的一個例子,告訴使用者,如果你想處理錯誤,應該怎麼做!所以就自己處理了一個ErrorController,親測完全可用,程式碼如下:
package com.rjhcsoft.credit.controller; import com.rjhcsoft.credit.exceptions.BaseResponse; import com.rjhcsoft.credit.exceptions.Status; import org.springframework.boot.autoconfigure.web.ErrorProperties; import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver; import org.springframework.boot.web.servlet.error.DefaultErrorAttributes; import org.springframework.boot.web.servlet.error.ErrorAttributes; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.Assert; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Collections; import java.util.List; import java.util.Map; @Controller @RequestMapping("${server.error.path:${error.path:/error}}") public class ErrorController extends AbstractErrorController { private final ErrorProperties errorProperties; public ErrorController(){ this(new DefaultErrorAttributes(),new ErrorProperties()); } /** * Create a new {@link ErrorController} instance. * @param errorAttributes the error attributes * @param errorProperties configuration properties */ public ErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties) { this(errorAttributes, errorProperties, Collections.emptyList()); } /** * Create a new {@link ErrorController} instance. * @param errorAttributes the error attributes * @param errorProperties configuration properties * @param errorViewResolvers error view resolvers */ public ErrorController(ErrorAttributes errorAttributes, ErrorProperties errorProperties, List<ErrorViewResolver> errorViewResolvers) { super(errorAttributes, errorViewResolvers); Assert.notNull(errorProperties, "ErrorProperties must not be null"); this.errorProperties = errorProperties; } @Override public String getErrorPath() { return this.errorProperties.getPath(); } @RequestMapping(produces = "text/html") public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes( request, isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView != null ? modelAndView : new ModelAndView("error", model)); } @RequestMapping @ResponseBody public BaseResponse error(HttpServletRequest request) { Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); HttpStatus status = getStatus(request); Status sta = null; if (status.is1xxInformational()){ sta=Status.INFORMATION_EXCEPTION; }else if (status.is3xxRedirection()){ sta=Status.REDIRECT_ERROR; }else if (status.is4xxClientError()){ sta=Status.CLIENT_ERROR; }else if (status.is5xxServerError()){ sta=Status.SERVER_ERROR; }else sta=Status.UNKNKOWN_REQUEST; return BaseResponse.error(sta,body); } /** * 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 */ protected 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 */ protected ErrorProperties getErrorProperties() { return this.errorProperties; } }
- 注意:其中的error方法的返回值
BaseResponse
是我自己定義的一套ajax返回格式,具體情況具體對待,其中Status
是我們自己定義的一套狀態碼,是列舉,這個大家也是具體情況具體對待 - 最後的返回結果如下:
{
"code": "1005",
"data": {
"timestamp": 1538033789395,
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/x-www-form-urlencoded' not supported",
"path": "/account/login"
},
"msg": "客戶端請求錯誤,4XX錯誤"
}
- 大家發現,最後的返回結果已經是我們自己定義的格式了,至於全域性異常處理網上有很多,這裡我也貼一份我定義的
package com.rjhcsoft.credit.exceptions.resolve;
import com.alibaba.fastjson.JSON;
import com.rjhcsoft.credit.exceptions.BaseException;
import com.rjhcsoft.credit.exceptions.BaseResponse;
import com.rjhcsoft.credit.exceptions.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class GlobalExceptionResolve implements HandlerExceptionResolver {
private static final Logger EXCEPTION = LoggerFactory.getLogger("exception");
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
response.setStatus(200);
BaseResponse baseResponse = parseException(ex);
if (isAjax(request)) return returnJson(baseResponse,response);
else return returnView(baseResponse,response);
}
/**
* 解析異常,這裡做簡單解析
* @param ex
* @return
*/
private BaseResponse parseException(Exception ex){
EXCEPTION.error(ex.getMessage());
if (ex instanceof BaseException){
Exception e = ((BaseException) ex).getE();
EXCEPTION.error(e==null?"":e.getMessage()==null?"":e.getMessage());
return BaseResponse.error(((BaseException) ex).getStatus());
}else return BaseResponse.error(Status.UNKNKOWN);
}
/**
* 返回json串
* @param baseResponse
* @param response
* @return
*/
public static ModelAndView returnJson(BaseResponse baseResponse,HttpServletResponse response){
response.setContentType("application/json;charset=utf-8");
response.setCharacterEncoding("utf-8");
try(PrintWriter writer = response.getWriter()) {
writer.print(JSON.toJSONString(baseResponse));
}catch (IOException e){
e.printStackTrace();
}
return new ModelAndView();
}
/**
* 返回頁面
* @param baseResponse
* @param response
* @return
*/
public static ModelAndView returnView(BaseResponse baseResponse,HttpServletResponse response){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("/error");
modelAndView.addObject("response",baseResponse);
return modelAndView;
}
/**
* 判定師傅是ajax請求
* @param request
* @return
*/
public static boolean isAjax(HttpServletRequest request){
String requestType = request.getHeader("X-Requested-With");
if("XMLHttpRequest".equals(requestType)){
return true;
}else{
return false;
}
}
}
- BaseException
package com.rjhcsoft.credit.exceptions;
public class BaseException extends RuntimeException{
private Status status;
private Exception e;
protected BaseException(Status status){
super(status.json());
this.status=status;
}
protected BaseException(Status status,Exception e){
this(status);
this.e = e;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public Exception getE() {
return e;
}
public void setE(Exception e) {
this.e = e;
}
}