1. 程式人生 > >14、springboot中的WebMvcConfigurer介面作用

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 說明 是否允許通訊

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>