map insert异常失败_【spring boot】spring boot 处理异常

一、继承BasicErrorController

spring boot默认有处理异常的跳转页面,成为“Whilelabel Error Page”,spring boot的异常处理全都由ErrorMvcAutoConfiguration类来调度的,这个类中定义了一个BasicErrorController类,这个类中有两个方法:

1、处理页面请求(procedure=“text/html”)

	@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
	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);
	}

2、处理费页面请求(procedure=“application/json;charset=UTF-8”)

	@RequestMapping
	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
		Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
		HttpStatus status = getStatus(request);
		return new ResponseEntity<>(body, status);
	}

我们只要继承BasicErrorController类,重写这两个方法就可以。下面是我们自己的代码

/**
 * @Classname ErrorController
 * @Description 自定义异常处理的Controller
 * @Date 2019/7/31 20:00
 * @Author by weikai
 */
@Controller
public class ErrorController extends BasicErrorController {

  private Logger logger = LoggerFactory.getLogger(this.getClass());

  @Autowired
  MyErrorAttributes myErrorAttributes;

  public ErrorController(ServerProperties serverProperties) {
    super(new DefaultErrorAttributes(), serverProperties.getError());
  }

  /**
   * 覆盖默认的Json响应
   */
  @Override
  @RequestMapping(produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  @ResponseBody
  public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    Map<String, Object> body = getErrorAttributes(request,
        isIncludeStackTrace(request, MediaType.ALL));
    HttpStatus status = getStatus(request);

    //输出自定义的Json格式
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("status", false);
    map.put("msg", body.get("message"));

    return new ResponseEntity<Map<String, Object>>(map, status);
  }

  /**
   * 覆盖默认的HTML响应
   */
  @Override
  @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
  public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
    //请求的状态
    HttpStatus status = getStatus(request);
    response.setStatus(getStatus(request).value());

    WebRequest webRequest = new ServletWebRequest(request);
    Map<String, Object> model = myErrorAttributes.getErrorAttributes(webRequest,
        isIncludeStackTrace(request, MediaType.TEXT_HTML));
    //设置流的字符集
    OutputStream outputStream = null;
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss");
    String realPageContent ;
    response.setHeader("content-type", "text/html;charset=utf-8");
    try {
      outputStream = response.getOutputStream();
      String pageContent = ErrorPathConstant.PAGE_ERROR;
      realPageContent = String.format(pageContent,model.get("timestamp"),model.get("status"),model.get("message"));
      outputStream.write(realPageContent.getBytes("UTF-8"));
    } catch (Exception e) {
      logger.error("获取响应的的流失败",e);
    }finally {
      IOUtils.closeQuietly(outputStream);
    }
    return null;
  }
}

在返回到错误页面的方法中,我们用了response.getOutputStream()获得流,然后在write()到页面的形式。

我们还需要重写DefaultErrorAttributes类的getErrorAttributes,这里是对model里的信息做的处理。先看一下源码:

	@Override
	public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
		Map<String, Object> errorAttributes = new LinkedHashMap<>();
		errorAttributes.put("timestamp", new Date());
		addStatus(errorAttributes, webRequest);
		addErrorDetails(errorAttributes, webRequest, includeStackTrace);
		addPath(errorAttributes, webRequest);
		return errorAttributes;
	}

我们的MyErrorAttributes重写了代码如下:

/**
 * @Classname MyErrorAttributes
 * @Description 自定义errorAttributes
 * @Date 2019/8/1 12:11
 * @Author by weikai
 */
@Configuration
@ConditionalOnProperty(name = "bean.valid.normal",havingValue = "true")
public class MyErrorAttributes extends DefaultErrorAttributes {

  @Override
  public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) {
    Map<String, Object> errorAttributes = super.getErrorAttributes(webRequest,includeStackTrace);
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    errorAttributes.put("timestamp", simpleDateFormat.format(new Date()));
    return errorAttributes;
  }
}
//这里就修改了timestamp的格式
这里说明一下,error这个方法被触发是在请求为ajax请求或者通过jmeter等工具触发的非页面请求

看一下效果,用页面请求得到的错误页面

4a58030f-3d2f-eb11-8da9-e4434bdf6706.png

用工具调用得到的json格式的错误信息

4b58030f-3d2f-eb11-8da9-e4434bdf6706.png

二、写通知

用@ControllerAvice("kai.personal.wei")这个注解扫描基础包,@ExceptionHandler(Exception.class)表示要捕捉的异常类型,这样就可以有好的处理程序中抛出的异常了。代码如下:

/**
 * @Classname AcmeControllerAdvice
 * @Description 处理错误的方法类
 * @Date 2019/7/30 16:29
 * @Author by weikai
 */
@ControllerAdvice("kai.personal.wei")
@ConditionalOnProperty(name = "bean.valid.normal",havingValue = "true")
public class AcmeControllerAdvice extends ResponseEntityExceptionHandler {

  @ExceptionHandler(Exception.class)
  @ResponseBody
  ResponseEntity<?> handleControllerException(HttpServletRequest request, Throwable throwable) {
    HttpStatus status = getStatus(request);
    return new ResponseEntity<>(new ResponseDto(status.value(), throwable.getMessage()), status);
  }

  private HttpStatus getStatus(HttpServletRequest request) {
    Integer statusCode = (Integer) request.getAttribute("javax.servlet.customerror.status_code");
    if (statusCode == null) {
      return HttpStatus.INTERNAL_SERVER_ERROR;
    }
    return HttpStatus.valueOf(statusCode);
  }
}

当我的请求报错之后,会进入到我写的这个通知里,并把异常友好的打印出来。

4c58030f-3d2f-eb11-8da9-e4434bdf6706.png
需要注意的是,reqeust.getAtrribute("javax.servlet.error.status_code")是404,500等状态码的key,
org.springframework.boot.web.servlet.error.DefaultErrorAttributes.ERROR为错误信息的key。

三、在tomcat中配置ErrorPages,修改错误信息的映射

写一个配置类,配置各种错误的映射路径,如下

/**
 * @Classname WebServerAutoConfiguration
 * @Description 这个是tomcat的错误页面的配置类
 * @Date 2019/8/1 10:58
 * @Author by weikai
 */
@Configuration
public class WebServerAutoConfiguration {
    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
//        ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/customerror/400.html");
        ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/customerror/400");
//        ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/customerror/401.html");
        ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/customerror/401");
//        ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/customerror/403.html");
        ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/customerror/403");
//        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/customerror/404.html");
        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/customerror/404");
//        ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/customerror/415.html");
        ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/customerror/415");
//        ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/customerror/500.html");
        ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/customerror/500");
        factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500);
        return factory;
    }
}

这个类是把各个错误状态码维护到TomcatServletWebServerFactory中,每个状态码都有各自的吊装路径,可以是一个controller,也可以是一个html。这里用的是controller,看一下controller的代码

/**
 * @Classname HttpErrorStatusController
 * @Description 配置error映射
 * @Date 2019/8/1 14:28
 * @Author by weikai
 */
@Controller
@RequestMapping("customerror")
public class HttpErrorStatusController {

  private Logger logger = LoggerFactory.getLogger(this.getClass());

  @RequestMapping("404")
  public void get404(HttpServletRequest request, HttpServletResponse response) {
    logger.info("==========这个是404的错误处理器");
    OutputStream outputStream = null;
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss");
    String realPageContent ;
    //设置response的字符集
    response.setHeader("content-type", "text/html;charset=utf-8");
    try {
      outputStream = response.getOutputStream();
      String pageContent = ErrorPathConstant.PAGE_404;
      String dateStr = simpleDateFormat.format(new Date());
      realPageContent = String.format(pageContent,dateStr);
      if (response.getContentType() == null) {
        response.setContentType(MediaType.TEXT_HTML_VALUE);
      }
      outputStream.write(realPageContent.getBytes("UTF-8"));
    } catch (Exception e) {
      logger.error("获取响应的的流失败",e);
    }finally {
      IOUtils.closeQuietly(outputStream);
    }
  }

  @RequestMapping("500")
  public void get500(HttpServletRequest request, HttpServletResponse response) {
    logger.info("==========这个是500的错误处理器");

    //设置流的字符集
    OutputStream outputStream = null;
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss");
    String realPageContent ;
    response.setHeader("content-type", "text/html;charset=utf-8");
    try {
      outputStream = response.getOutputStream();
      String pageContent = ErrorPathConstant.PAGE_404;
      String dateStr = simpleDateFormat.format(new Date());
      realPageContent = String.format(pageContent,dateStr);
      if (response.getContentType() == null) {
        response.setContentType(MediaType.TEXT_HTML_VALUE);
      }
      outputStream.write(realPageContent.getBytes("UTF-8"));
    } catch (Exception e) {
      logger.error("获取响应的的流失败",e);
    }finally {
      IOUtils.closeQuietly(outputStream);
    }
  }
}

请求的程序中出现了500的错,则

4e58030f-3d2f-eb11-8da9-e4434bdf6706.png

这些主要都是我的笔记,有什么不懂得地方可以留言,大家一起探讨一下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值