spring-boot(四)异常配置,Whitelabel Error Page处理方式

前言我用的spring boot版本号是2.0.1
#1、srping boot几种异常类型

  • rest接口运行时抛出的异常
  • 访问视图时抛出的异常
    a、访问一个存在的url返回一个不存的视图异常
    b、访问一个不存的url异常

1.1spring boot默认处理异常的方式

spring boot访问视图接口默认处理异常的是用类BasicErrorController实现的,源码如下,这里只贴出主要方法:

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
	private final ErrorProperties errorProperties;
	public BasicErrorController(ErrorAttributes errorAttributes,
			ErrorProperties errorProperties) {
		this(errorAttributes, errorProperties, Collections.emptyList());
	}
	public BasicErrorController(ErrorAttributes errorAttributes,
			ErrorProperties errorProperties, List errorViewResolvers) {
		super(errorAttributes, errorViewResolvers);
		Assert.notNull(errorProperties, "ErrorProperties must not be null");
		this.errorProperties = errorProperties;
	}
	@RequestMapping(produces = "text/html")
	public ModelAndView errorHtml(HttpServletRequest request,
			HttpServletResponse response) {
		HttpStatus status = getStatus(request);
		Map model = Collections.unmodifiableMap(getErrorAttributes(
				request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
		response.setStatus(status.value());
		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
		//默认设置返回的是error页面
		return (modelAndView == null ? new ModelAndView("error", model) : modelAndView);
	}
	@RequestMapping
	@ResponseBody
	public ResponseEntity> error(HttpServletRequest request) {
		Map body = getErrorAttributes(request,
				isIncludeStackTrace(request, MediaType.ALL));
		HttpStatus status = getStatus(request);
		return new ResponseEntity<>(body, status);
	}
}

rest 方法:

//返回视图
@RequestMapping("/index")
public String indexHtml(Model model) {
    // Duplicated logic
    model.addAttribute("test");
    //返回视图名字
    return "index";         
}
//返回json
@RequestMapping("/errorResTest")
@ResponseBody
 public String errorResTest(){
     int i = 0;
     int j = -1;
     int x = j/i;
     return "erro";
 }

spring boot默认处理异常的方法就是errorHtml error方法。可以通过断点来看当用浏览器和client http方法访问rest url视图时分别执行的什么方法。
访问:http://127.0.0.1:8080/indexhttp://127.0.0.1:8080/errorResTest
**当用浏览器访问:**不管事执行返回视图的rest方法还是执行返回json的rest方法,异常 执行的都是errorHtml方法,并且如果templates下没有error.html页面时,会返回spring boot默认的错误面就是Whitelabel Error Page
**用httpclient 访问:**不管事执行返回视图的rest方法还是执行返回json的rest方法,异常执行的都是error方法,返回的是这样的错误 {protocol=http/1.1, code=500, message=Server Error, url=http://127.0.0.1:8080/errorResTest}
以上spring boot默认的异常返回形式,我们一般不会使用,通常都想自定义异常。
如果我们不重写这个类,用默认的类BasicErrorController来处理异常,那么我们只需要在约定的目录下新建页面就可以了。

1.2 访问一个存在的url返回一个不存的视图异常

如果我们不配置页面默认会返因spring boot的一个异常页面
spring-boot(四)异常配置,Whitelabel Error Page处理方式_第1张图片
我用的spring boot版本号是2.0.1在spring boot默认视图目录下建立一个error.html页面,就是自动跳转到这个自定义的页面下:
spring-boot(四)异常配置,Whitelabel Error Page处理方式_第2张图片
默认目录结构如下:
spring-boot(四)异常配置,Whitelabel Error Page处理方式_第3张图片

1.3 访问一个不存在的url异常

访问一个不存的url:localhost:8080/fjdasldfjoaewijf
我们希望需要自动跳转到400页面,如果不配置,那么spring boot会自动跳到一个默认的错误页面,spring boot版本2.0.1直接建立如下页面即可
spring-boot(四)异常配置,Whitelabel Error Page处理方式_第4张图片

这时再输入一个不存在的url,那么就是跳转到自定义的404.html页面

1.4http client访问rest josn 异常

用http client 访问rest join 返回错误

java.lang.RuntimeException: Unexpected code Response{protocol=http/1.1, code=500, message=Server Error, url=http://127.0.0.1:8080/errorResTest}

直接报错,也没有统一的json格式。

2、自定义异常处理方法

官网这样介绍:
spring-boot(四)异常配置,Whitelabel Error Page处理方式_第5张图片
一:你可以继承BasicErrorController来实现
二:你也可以用ControllerAdvice注解来自定义json返回。下面来具体说一下。

2.1继承BasicErrorController实现视图url异常处理

rest接口:

@RequestMapping("/index")
public String errorResTest(){
      int i = 0;
      int j = -1;
      int x = j/i;
      return "erro";
  }

定义CustomErrorController类

/**
 * Created by clock on 2018/4/13.
 * 覆盖spring boot默认的异常处理类 继承BasicErrorController
 */
@Controller
public class CustomErrorController extends BasicErrorController {
    public CustomErrorController(ServerProperties serverProperties) {
        super(new DefaultErrorAttributes(), serverProperties.getError());
    }
    //通过浏览器访问的url 如果发生异常全部会被浏览并跳转到list.html页面
    @Override
    public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
        //请求的状态
        HttpStatus status = getStatus(request);
        response.setStatus(getStatus(request).value());

        Map model = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.TEXT_HTML));
        ModelAndView modelAndView = resolveErrorView(request, response, status, model);
        //指定自定义的视图
        return(modelAndView == null ? new ModelAndView("list", model) : modelAndView);
    }
    //通过http client访问的接口如果发生异常会调用这个方法
    @Override
    public ResponseEntity> error(HttpServletRequest request) {
        Map body = getErrorAttributes(request,
                isIncludeStackTrace(request, MediaType.ALL));
        HttpStatus status = getStatus(request);

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

        return new ResponseEntity>(map, status);
    }
}
  • 1、用浏览器访问视图:运行errorHtml方法,返回 templates目录下的list.html页面
  • 2、用httpclient访问视图:运行error方法,返回错误 。
@Test
    public void tsetsett(){
        String url = "http://127.0.0.1:8080/index";
        try {
            HttpHelper httpHelper = new HttpHelper();
            String josn  = new String(httpHelper.get(url),"UTF-8");
            System.out.println(josn);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

返回错误,我们可以看到http client的异常虽然被error拦截过,但返回的错误 也不是自定义的,还需要用ControllerAdvice 注解 实现自定义的json错误返回。

1.5 总结

在配置完BasicErrorController 后,我们发现在用浏览器访问接口,发生异常时会全部跳转到error.html页面,但如果用http client访问接口,没法实现统一的错误josn串,那么需要用用ControllerAdvice注解来实现

2、用ControllerAdvice 注解处理异常

rest接口定义

//这个rest接口,会抛出一个异常,我们没有捕获,下面定义统一的异常处理
 @RequestMapping("/errorResTest")
 @ResponseBody
  public String errorResTest(){
      int i = 0;
      int j = -1;
      int x = j/i;
      return "erro";
  }
  //抛出一个自定义的异常
  @RequestMapping("/throwcustomexcepiont")
    @ResponseBody
    public String throwCustomExcepiontTest(){
        try{
            int i = 0;
            int j = -1;
            int x = j/i;
        }catch (Exception e){
            throw new CustomException("custom code",e.getMessage());
        }
        return "erro";
    }
    //抛出一个自定义的异常
  @RequestMapping("/index")
  public String throwCustomExcepiontTest(){
      try{
          int i = 0;
          int j = -1;
          int x = j/i;
      }catch (Exception e){
          throw new CustomException("custom code",e.getMessage());
      }
      return "erro";
  }

这个接口运行时肯定会抛出一个异常,并且这个异常我们没有捕获,如果是普通的mvc接口,那么这种异常我们通常会配置一个统一的拦截器,来处理异常统一返回一个错误码,那么spring boot只需要注解就可以完成

@ControllerAdvice//在异常类上添加ControllerAdvice注解
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {

    /**
    不管是访问返回视图接口,还是返回json串接口,只要抛出的excetion异常全部由这个方法拦截,并统一返回json串
     * 统一异常拦截 rest接口
     * @param request
     * @param ex
     * @return
     */
    @ExceptionHandler(Exception.class)//Exception 这个是所有异常的父类
    //定义异常统一返回json,即使是返回视图的接口
    @ResponseBody
    Map handleControllerException(HttpServletRequest request, Throwable ex) {
        HttpStatus status = getStatus(request);
        Map map = new HashMap();
        map.put("code", String.valueOf(status.value()));
        map.put("msg", ex.getMessage());
        //这时会返回我们统一的异常json
        return map;
    }
    private HttpStatus getStatus(HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        if (statusCode == null) {
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        return HttpStatus.valueOf(statusCode);
    }

    /**
     * 拦截自定义异常 CustomException rest接口
     * @param request
     * @param ex
     * @return
     */
    @ExceptionHandler(CustomException.class)
    @ResponseBody
    Map handleControllerCustomException(HttpServletRequest request, CustomException ex) {
        HttpStatus status = getStatus(request);
        Map map = new HashMap();
        map.put("code", String.valueOf(status.value()));
        map.put("msg", "customer error msg");
        return map;
    }
}
	/**
	  * 拦截抛出CustomViewException 异常的方法并返回视图
	  * @param request
	  * @param ex
	  * @return
	  */
	 @ExceptionHandler(CustomViewException.class)
	 ModelAndView handleControllerCustomExceptionView(HttpServletRequest request, CustomViewException ex) {
	     ModelAndView modelAndView = new ModelAndView();
	     modelAndView.setViewName("error");
	     HttpStatus status = getStatus(request);
	     Map map = new HashMap();
	     map.put("code", String.valueOf(status.value()));
	     map.put("msg", "customer error msg");
	     return modelAndView;
	 }
}

3、总结

以上定义完BasicErrorControllerControllerAdvice处理异常的主法后。

  • 没有抛出异常,但访问不存在的url或视图 时会由BasicErrorController 处理并返回error.html页面
  • 抛出异常,如果不是自定义异常那么统一由Exception.class定义的handle处理,并返回指定的返回形式。定义异常那么由相应的自定义异常handle处理。

你可能感兴趣的:(spring,boot)