spring boot(学习笔记第五课) 自定义错误页,CORS(跨域支持)

spring boot(学习笔记第五课)

  • 自定义错误页,CORS(跨域支持)

学习内容:

  1. 自定义错误页
  2. CORS(跨域支持)

1. 自定义错误页

  1. 简单配置错误页面。
    通常如果访问没有定义的urlspring boot会返回默认的页面。
    在这里插入图片描述
    这个页面通常不是想要的页面,需要进行自己的定制开发。
  • 首先学习下spring boot的错误页面处理。spring boot使用BasicErrorController来进行处理。在IntelliJ IDEA中双击shift键,查看BasicErrorController的源代码。
    • MediaType == 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, getErrorAttributeOptions(request, MediaType.TEXT_HTML)));
      		response.setStatus(status.value());
      		ModelAndView modelAndView = resolveErrorView(request, response, status, model);
      		return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
      	}
      
    • MediaType == text/html以外
      	@RequestMapping
      	public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
      		HttpStatus status = getStatus(request);
      		if (status == HttpStatus.NO_CONTENT) {
      			return new ResponseEntity<>(status);
      		}
      		Map<String, Object> body = getErrorAttributes(request, getErrorAttributeOptions(request, MediaType.ALL));
      		return new ResponseEntity<>(body, status);
      	}
      
  • 进一步查看resolveErrorView
    注意到返回错误页面的处理的时候,spring boot的处理是ModelAndView modelAndView = resolveErrorView(request, response, status, model);,也就是说,这里会ErrorViewResolver进行错误页面的生成处理。进一步,查看DefaultErrorViewResolver的错误页面处理。
    	static {
    	Map<Series, String> views = new EnumMap<>(Series.class);
    	views.put(Series.CLIENT_ERROR, "4xx");
    	views.put(Series.SERVER_ERROR, "5xx");
    	SERIES_VIEWS = Collections.unmodifiableMap(views);
    }
    	@Override
    	public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
    		ModelAndView modelAndView = resolve(String.valueOf(status.value()), model);
    		if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
    			modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
    		}
    		return modelAndView;
    	}
    
    	private ModelAndView resolve(String viewName, Map<String, Object> model) {
    		String errorViewName = "error/" + viewName;
    		TemplateAvailabilityProvider provider = this.templateAvailabilityProviders.getProvider(errorViewName,
    				this.applicationContext);
    		if (provider != null) {
    			return new ModelAndView(errorViewName, model);
    		}
    		return resolveResource(errorViewName, model);
    	}
    
    这里知道,默认的错误页面是从/error/4xx/error/5xx进行查找的,实际上,spring boot的规则有两种:
    • 可以使用4xx.html/5xx.html进行定制,那么这种粒度较粗,不用的error code使用同样的页面。
    • 可以使用404.html/500.html进行定制,那么这种粒度较粗,不用的error code使用同样的页面。
      <!DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org/">
      <head>
          <meta charset="utf-8">
          <title>Title</title>
      </head>
      <body>
         <div>404</div>
      </body>
      </html>
      
  • 使用静态资源来定义classpath:/static/error/404.htmlclasspath:/static/error/500.html之后访问未定义的资源。
    在这里插入图片描述
  • 静态资源表达的页面没有thymeleaf页面灵活,尝试用thymeleaf进行展示。在/resources/templates/error/4xx.html/resources/templates/error/5xx.html
    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org/">
    <head>
        <meta charset="utf-8">
        <title>Title</title>
    </head>
    <body>
    <table border="1">
        <tr>
            <td>timestamp</td>
            <td th:text="${timestamp}"></td>
        </tr>
        <tr>
            <td>status</td>
            <td th:text="${status}"></td>
        </tr>
        <tr>
            <td>error</td>
            <td th:text="${error}"></td>
        </tr>
        <tr>
            <td>message</td>
            <td th:text="${message}"></td>
        </tr>
        <tr>
            <td>path</td>
            <td th:text="${path}"></td>
        </tr>
    </table>
    </body>
    </html>
    
    加入thymeleaf依赖,点击pom.xml,并maven->重新加载项目
    	<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-thymeleaf</artifactId>
    		</dependency>
    
  • 查看404的错误页面。
    在这里插入图片描述
    还是不想期待的返回一个详细的thymeleaf页面,而是原来的静态页面/resources/static/error/404.html页面。由此得出结论,同样的有4xx.html和404.html,优先找到最匹配的html作为view
    在这里插入图片描述
  1. 复杂配置(自定义error数据)
  • 查看BasicErrorController的代码
    看到spring boot通过调用getErrorAttributes这个方法,将Error发生时候的messagestatus一起数据都放在了这里,在结果画面表示的时候,从这个ErrorAttributes中取得,这样都会最终调用DefaultErrorAttributes这个类。
    查看ErrorMvcAutoConfiguration,会看到下面的代码,如果没有定义实现ErrorAttributes接口的类,那么spring boot将生成默认的DefaultErrorAttributes
    	@Bean
    	@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)
    	public DefaultErrorAttributes errorAttributes() {
    		return new DefaultErrorAttributes();
    	}
    
  • 通过继承DefaultErrorAttributes,进行特殊的属性的追加。
     public Map<String,Object> getErrorAttributes(WebRequest webRequest,
                                                     ErrorAttributeOptions includeStackTrace){
            Map<String,Object> errorAttributes =
                    super.getErrorAttributes(webRequest,includeStackTrace);
            errorAttributes.put("custommsg","出错了!");
            errorAttributes.remove("error");
            return errorAttributes;
        }
    
  • 更改thymeleaftemplates/error/404.html的页面代码,使用custommsg来取得message
      <tr>
        <td>message</td>
        <td th:text="${custommsg}"></td>
      </tr>
    
  • 最后显示not found的页面。
    在这里插入图片描述
  1. 自定义error视图
  • 查看ErrorMvcAutoConfiguration的代码
    		@Bean
    		@ConditionalOnBean(DispatcherServlet.class)
    		@ConditionalOnMissingBean(ErrorViewResolver.class)
    		DefaultErrorViewResolver conventionErrorViewResolver() {
    			return new DefaultErrorViewResolver(this.applicationContext, this.resources);
    		}
    
    可以看到默认的,如果没有实现出ErrorViewResolver接口的类,那么就会使用默认的DefaultErrorViewResolver,来根据上面的处理来查找error的处理view
  • 实现出自己的ErrorViewResolver接口的类。
    @Component
    public class CustomErrorViewResolver implements ErrorViewResolver {
        @Override
        public ModelAndView resolveErrorView(HttpServletRequest httpServletRequest,
                                             HttpStatus httpStatus,
                                             Map<String,Object> model){
            ModelAndView mv = new ModelAndView("errorPage");
            model.put("error","错误信息!");
            mv.addAllObjects(model);
            return mv
        }
        ```
    
  • 自定义error view,生成文件/resources/templates/errorPage.html
    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org/">
    <head>
      <meta charset="utf-8">
      <title>CustomErrorViewResolver></title>
    </head>
    <body>
    <table border="1">
      <tr>
        <td>timestamp</td>
        <td th:text="${timestamp}"></td>
      </tr>
      <tr>
        <td>status</td>
        <td th:text="${status}"></td>
      </tr>
      <tr>
        <td>error</td>
        <td th:text="${error}"></td>
      </tr>
      <tr>
        <td>message</td>
        <td th:text="${custommsg}"></td>
      </tr>
      <tr>
        <td>path</td>
        <td th:text="${path}"></td>
      </tr>
    </table>
    </body>
    </html>
    
  1. 完全自定义controller
    还可以完全通过自定义BasicErrorController,完全控制错误页面的表示。
public class CustomBasicErrorController extends BasicErrorController {
    @Autowired
    public CustomBasicErrorController(ErrorAttributes errorAttributes,
                                      ServerProperties serverProperties,
                                      List<ErrorViewResolver> errorViewResolvers) {
        super(errorAttributes,
                serverProperties.getError(),
                errorViewResolvers);
    }

    @Override
    public ModelAndView errorHtml(HttpServletRequest httpServletRequest,
                                  HttpServletResponse httpServletResponse) {
        HttpStatus status = getStatus(httpServletRequest);
        Map<String, Object> model = getErrorAttributes(httpServletRequest,
                ErrorAttributeOptions.defaults());
        model.put("custommsg", "出错了!!");
        model.put("error", "采用了完全自定义错误controller!");
        return new ModelAndView("errorPage", model);
    }
    @Override
    public ResponseEntity<Map<String,Object>>error(HttpServletRequest httpServletRequest){
        Map<String,Object> model = getErrorAttributes(httpServletRequest,
                ErrorAttributeOptions.defaults());
        model.put("custommsg","出错了!!");
        HttpStatus status = getStatus(httpServletRequest);
        return new ResponseEntity<Map<String, Object>>(model,status);
    }
}

之后访问,让错误页面表示出来。
在这里插入图片描述

2. CORS(跨域支持)

  1. 了解什么是CSRF
    CSRF (Cross-site request forgery,跨站请求伪造)也被称为One Click Attack或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。
    CSRF的攻击原理:
    在这里插入图片描述

可以看出,黑客系统可以通过银行系统对于用户浏览器的信任(合法的cookie情报),当而皇之的对银行系统进行非法的get或者post请求。
因而现代浏览器的一个策略就是禁止跨域请求。

  1. CORS(Cross-Origin Resource Sharing 跨域支持)
    对于在一个域内的spring boot的应用程序,通过浏览器的ajax等手段访问其他域,是非常正常的情况,所以衍生出了CORS(跨域支持)的资源共享技术。
    JavaEE中的JSONP能够使用非官方的方法解决这个跨域共享问题,但是只能支持Get请求,这是一个巨大的缺陷
    接下来说明一下,服务器端是如何支持CORS的。

在这里插入图片描述
3. spring boot中进行配置CORS(Cross-Origin Resource Sharing 跨域支持)

  • /resources/static/中增加jquery.js。
    >>>jquery的官网
  • 在/resources/static/中增加index.html进行访问。
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>CORS(跨域支持)</title>
        <script src="jquery.js"></script>
    </head>
    <body>
    <div id="contentDiv"></div>
    <div id="deleteResult"></div>
    <input type="button" value="提交数据" onclick="getData()"><br>
    <input type="button" value="删除数据" onclick="deleteData()"><br>
    <script>
        function deleteData(){
            $.ajax({
                url:'https://localhost:8080/book/99,
                type:'delete',
                success: function(msg){
                    $("#deleteResult").html(msg);
                }
            })
        }
    </script>
    </body>
    </html>
    
  • controller里面打开CORS(跨域支持)
        @DeleteMapping("/book/{id}")
        @CrossOrigin(value = "https://localhost:8081"
                    ,maxAge = 1800,allowedHeaders = "*")
        @ResponseBody
        public String book(@PathVariable Long id){
            return String.valueOf(id);
        }
    
  • 将资源共享的server设定成8080端口,采用java -jar的方式启动。
  • 将请求资源的server使用IntelliJ IDEA进行启动,端口改修成9000。
  • 在浏览其中访问https://localhost:9000/index.html,点击删除数据按钮,成功访问了8080端口的另一个域,实现了CORS(跨域支持)
  • 在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值