- 最近做一个项目,项目绝大多数请求都用的是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;
}
}
}
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;
}
}