14、springboot中的WebMvcConfigurer接口作用

本章目标

了解WebMvcConfigurer作用

IDEA实现接口方法

利用WebMvcConfigurer配置项目的CORS等

 

WebMvcConfigurer的作用

WebMvcConfigurer: 直接点就是web的配置都可以在这类里面干

可以查看spring的文档, 其定义了很多供重构的方法

 

利用WebMvcConfigurer配置项目的CORS等

在上节的项目中新建一个MyConfiguration实现WebMvcConfigurer

public class MyConfiguration implements WebMvcConfigurer {}

IDEA实现接口方法

快捷键 CTRL+O , 会提示所有需要实现的接口

 

一、springboot内容协商配置(configureContentNegotiation)

    内容协商:在 HTTP 协议中,内容协商是这样一种机制,通过为同一 URI 指向的资源提供不同的展现形式,可以使用户代理选择与用户需求相适应的最佳匹配(例如,文档使用的自然语言,图片的格式,或者内容编码形式)。

 

覆盖内容协商方法:

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    /* 是否通过请求Url的扩展名来决定media type */
    configurer.favorPathExtension(true)
            /* 不检查Accept请求头 */
            .ignoreAcceptHeader(true)
            .parameterName("mediaType")
            /* 设置默认的media yype */
            .defaultContentType(MediaType.TEXT_HTML)
            /* 请求以.html结尾的会被当成MediaType.TEXT_HTML*/
            .mediaType("html", MediaType.TEXT_HTML)
            /* 请求以.json结尾的会被当成MediaType.APPLICATION_JSON*/
            .mediaType("json", MediaType.APPLICATION_JSON);
}

上面代码说白了就是告诉系统什么类型用什么来标识

 

二、spring boot配置视图解析(configureViewResolvers)

@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
    registry.jsp("/WEB-INF/jsp/", ".jsp");
    registry.enableContentNegotiation(new MappingJackson2JsonView());
}

看上去是不是感觉很熟悉 ,没错,在application.properties里面的spring.mvc.view.prefix以及spring.mvc.view.suffix, 如果配置了视图解析可以不需要在application.properties中配置prefix,suffix这两项了 ,测试结果和之前是一样的 http://127.0.0.1:8083/index

 

三、CORS配置跨域

第一篇解决办法:

1,JavaScript由于安全性方面的考虑,不允许页面跨域调用其他页面的对象,那么问题来了,什么是跨域问题?

答:这是由于浏览器同源策略的限制,现在所有支持JavaScript的浏览器都使用了这个策略。那么什么是同源呢?所谓的同源是指三个方面“相同”:

1,域名相同

2,协议相同

3,端口相同

2,下面就举几个例子来帮助更好的理解同源策略。

URL 说明 是否允许通信

http://www.a.com/a.js

http://www.a.com/b.js 同一域名 允许

 

http://www.a.com/a.js

http://www.b.com/a.js 不同域名 不允许

 

http://www.a.com:8000/a.js

http://www.a.com/b.js 同一域名不同端口 不允许

 

https://www.a.com/a.js

http://www.a.com/b.js 同一域名不同协议 不允许

 

3,在JAVA中处理跨域问题,通常有以下两种常用的解决方法。

第一种解决方法后台代码在被请求的Servlet中添加Header设置:

response.setHeader("Access-Control-Allow-Origin", "*");
PrintWriter out =null;

try
{
    out = response.getWriter();
} catch (IOException e)
{
    // TODO Auto-generated catch block
    e.printStackTrace();
}

out.print("{'status':'ok'}");
out.flush();
out.close();

Access-Control-Allow-Origin这个Header在W3C标准里用来检查该跨域请求是否可以被通过,如果值为*则表明当前页面可以跨域访问。默认的情况下是不允许的

 

第二种解决方法通过jsonp跨域请求的方式。JSONP和JSON虽然只有一个字母的区别,但是他们完全就是两回事,很多人很容易把他们搞混。JSON是一种数据交换的格式,而JSONP则是一种非官方跨域数据交互协议

首先来说一下前端JS是怎么发送请求。代码如下所示:

$.ajax({
           url:"your url",
           type:"get or post",
           async:false,
           dataType : "jsonp",
           //服务端用于接收callback调用的function名的参数
           jsonp:"callbackparam",
           //callback的function名称
           jsonpCallback:"success_jsonpCallback",
           success:function(data){
                    console.log(data);
           },
           error:function(data){
                    console.log(data);
           }

});

这里的callbackparam和success_jsonpCallback可以理解为发送的data数据的键值对,可以自定义,但是callbackparam需要和后台约定好参数名称,因为后台需要获取到这个参数里面的值(即success_jsonpCallback)

 

后台怎么样获取和返回数据:

PrintWriter out =null;
String callback=req.getParameter("callbackparam");
String json=callback+"({'status':'ok'})";

try
{
    out = resp.getWriter();
} catch (IOException e)
{
    // TODO Auto-generated catch block
    e.printStackTrace();
}

out.print(json);
out.flush();
out.close();

    首先需要获取参数名为callbackparam的值,这里获取到的值就是“success_jsonpCallback”。然后将这个值加上一对小括号小括号里放入你需要返回的数据内容,比如这里我返回一个JSON对象。当然你也可以返回其他对象,比如只返回一个字符串类型数据也可以。最后前端JS返回的数据就是这样的

success_jsonpCallback({'status':'ok'})

浏览器会自动解析为json对象,这时候你只需要在success回调函数中直接用data.status就可以了。

 

四、问题

    要知道跨域请求就要先了解同源策略,那么什么是同源?什么是不同源?简单来说就是,如果两个资源,包括HTML页面、JavaScript脚本、css样式,对应的协议、域名和端口完全相同,那么这两个资源就是同源的Same-origin policy解释得很清楚。那么同源策略的意思就是一个源中的资源访问另外一个源中的资源,在在这一点上JavaScript的跨站资源访问表现的更加明显。在HTML5之前Ajax是不允许发起跨站请求的,如果有需求的话,可以使用JSONP等方法,但是缺点就是:

  • 1、只支持Get不支持Post;

  • 2、本质上是脚本注入的方式,存在安全隐患;

    还有JSONP的优缺点,但是自从HTML5出现之后,提出了CORS(跨站资源共享)这种方式,极大地方便了日常的开发。如果要理解CORS的工作原理,首先要知道跨域访问是怎么被禁止的,之前本屌丝一直以为是前台的跨域访问请求不能发出去,是实现同源策略的浏览器拦截了该请求,但是后来才知道浏览器并没有拦截请求,而是拦截了服务器端返回的响应 所以如果要支持跨域访问,需要浏览器和后台服务器程序同时支持,如果这两个条件不能同时满足,则还是不能支持跨域访问。

用于CORS中的Http的首部有如下几个:

  • 响应头

    • Access-Control-Allow-Origin: 允许跨域访问的域,可以是一个域的列表,也可以是通配符”*”;

    • Access-Control-Allow-Methods: 允许使用的请求方法,以逗号隔开;

    • Access-Control-Allow-Headers: 允许自定义的头部,以逗号隔开,大小写不敏感;

    • Access-Control-Expose-Headers: 允许脚本访问的返回头,请求成功后,脚本可以在XMLHttpRequest中访问这些头的信息

    • Access-Control-Allow-Credentials: 是否允许请求带有验证信息,XMLHttpRequest请求的withCredentials标志设置为true时,认证通过,浏览器才将数据给脚本程序。

    • Access-Control-Max-Age: 缓存此次请求的秒数。在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据,非常有用,大幅优化请求次数;

  • 请求头

    • Origin: 普通的HTTP请求也会带有,在CORS中专门作为Origin信息供后端比对,表明来源域,要与响应头中的Access-Control-Allow-Origin相匹配才能进行跨域访问;

    • Access-Control-Request-Method: 将要进行跨域访问的请求方法,要与响应头中的Access-Control-Allow-Methods相匹配才能进行跨域访问;

    • Access-Control-Request-Headers: 自定义的头部,所有用setRequestHeader方法设置的头部都将会以逗号隔开的形式包含在这个头中,要与响应头中的Access-Control-Allow-Headers相匹配才能进行跨域访问。

从支持跨域访问的范围说,可以有整个服务器、单个应用程序、单个接口。

所以还有如下几种方法解决跨域问题:

 

第三种解决方法在服务器上可以部署多个应用程序,如果在整个服务器的范围上支持跨域访问,那么在所有应用程序上都不用单独配置了,直接使用服务器的配置即可,这里通过tomcat来进行举例。在Tomcat7之后包括tomcat7才开始支持CORS,之前的版本是不支持的。配置CORS,首先配置Tomcat中的conf\web.xml,在其中添加一个Filter声明。

<filter>
    <filter-name>CORS</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>CORS</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

 

这样就能应用到在该Tomcat中部署的所有的应用程序的接口上。然后在Tomcat的lib文件夹下加入两个jar包:cors-filter-2.5.jar和java-property-utils-1.9.jar,这两个jar包对应的maven依赖如下:

<dependency>
    <groupId>com.thetransactioncompany</groupId>
    <artifactId>cors-filter</artifactId>
    <version>2.5</version>
</dependency>

在Tomcat服务器上配置完成后,在自己的应用程序上就不要再配置有关跨域访问的内容了,这样会造成访问相应的接口时不支持跨域访问。 但是这种配置方式覆盖面太广,有些部署在该服务器下的应用程序根本不需要支持跨域访问,就会带来一些安全问题,所以其实不推荐使用这种配置方式。

 

第四种解决方法上述服务器中的web.xml上配置的filter,配置到自己的工程中, 然后加入maven依赖

<filter>
    <filter-name>CORS</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>CORS</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

依赖:

<dependency>
    <groupId>com.thetransactioncompany</groupId>
    <artifactId>cors-filter</artifactId>
    <version>2.5</version>
</dependency>

这样就完成了对跨域访问请求的支持,如果并不想对所有的请求都支持跨域访问,则可以在Filter的url-pattern中改变匹配到的url地址

 

第四种解决方法:    在Spring中,使用这个接口可以通过定义回调方法来进行一些Spring MVC中要用到的配置。 在里面用来支持CORS的方法是addCorsMappings(CorsRegistry registry),我们并不直接使用这个接口而是使用它的抽象实现类WebMvcConfigurerAdapter, 这个类中给我们提供了WebMvcConfigurer接口中方法的空实现,我们可以直接填上自己的业务逻辑就可以直接使用(需要实现WebMvcConfigurer,继承WebMvcConfigurationAdapter或WebMvcConfigurationSupport类 在这个回调方法中的参数是CorsRegistry,这个类可以帮助我们为相应的url地址提供CORS配置,关于这个方法可以放到Application启动类中,也可以单独放到一个类中。

在Application启动类中使用该方法,只需要在Application启动类中加入如下代码即可。

或单独类:

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
            //registry.addMapping("/*").allowedOrigins("http://localhost:8070");
        }
    };
}
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = { "com.springboot.demo" })
public class CORSConfiguration {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedHeaders("*")
                        .allowedMethods("*")
                        .allowedOrigins("*");
            }
        };
    }
}

 

与在Application启动类中使用相比多了@Configuration@EnableWebMvc注解,这是因为@SpringBootApplication已经包含上述两个注解了,所以不需要重新加入。 

请求处理:

var url="http://112.74.171.99:8080/autoticketapi/pay";                                                                     
$.ajax({         
 crossDomain:true,                                                                  
 url:url, 
 type:"GET",                                                                         
    data: {"liantuoOrderNo": $(this).attr("liantuoOrderNo"), "orderNo": $(this).attr("orderNo")},    
    dataType: "json",                                                                              
    success: function (data) 
    {                                                                       
       if (data.status != 200) 
       {                                                                    
       alert("error");                                                                  
       }else{                                                                       
         alert(data.msg)                                                          
       }                                                                               
    }                                                                                   
});

第五种解决方法:    所有的方法归根结底就是拦截对接口的访问,所以如果你不想麻烦并且想深入了解CORS到底是怎样工作的,可以通过自定义Filter来实现,其实这也没有什么难的,就是在拦截器上通过对请求和响应加上一些Headers。

实现自己的过滤器:

public class CorsFilter implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,ServletException {
        // TODO Auto-generated method stub
        HttpServletResponse res = (HttpServletResponse) response;
        res.setContentType("text/html;charset=UTF-8");
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        res.setHeader("Access-Control-Max-Age", "0");
        res.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");
        res.setHeader("Access-Control-Allow-Credentials", "true");
        res.setHeader("XDomainRequestAllowed","1");
        chain.doFilter(request, response);
    }


    @Override
    public void destroy() {
        // TODO Auto-generated method stub
    }

}

web.xml配置如下:

<filter>
    <filter-name>cors</filter-name>
    <filter-class>com.tianlong.common.base.CorsFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>cors</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

快来成为我的朋友或合作伙伴,一起交流,一起进步!
QQ群:961179337
微信:lixiang6153
邮箱:lixx2048@163.com
公众号:IT技术快餐
更多资料等你来拿!

 

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贝壳里的沙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值