1. 程式人生 > >angularjs跨域請求,html5封裝進android與ios

angularjs跨域請求,html5封裝進android與ios

前言

第一次正兒八經的寫部落格實在有點不知道怎麼開頭好,所學的東西也不夠系統,我相信我寫的東西瞄準了一個點去寫,無論從哪裡開始,都會讓人覺得有點突然,但是,我也沒辦法從所談主題的起源開始談,所以不糾結這個次序關係了,有關主題的前後我就稍微介紹一些,主要圍繞我所遇到的問題和如何去解決來談吧。

我使用的框架是springboot+angularjs ,內容主要包括:
- 為什麼需要跨域
- js跨域請求
- 使用localStorage替代本地Cookies
- 使用token替代跨域傳送Cookies

下面主要針對以上幾點來分別說說

為什麼需要跨域

我們這個俄專案是使用springboot+angularjs開發的,web端順利開發結束,接下來是手機端,手機端直接使用html在電腦端執行也開發的比較順利,因為是直接使用的PC端瀏覽器,所以和web端開發是沒有區別的,當然,手機端的瀏覽器也同樣不會有什麼問題。但是我們公司專案能打包成android和ios的app,android和ios都支援webview,按理直接把url告訴webview然後去請求伺服器訪問也沒什麼問題,畢竟通過webview的形式,理論上就相當於使用了android和ios提供的一個瀏覽器而已,只是它隱藏了url。
考慮到手機上的切換效果,app開發的同事會使用多個webview來切換渲染資料,像詳情之類的頁面,需要開啟新的webview,這樣問題就來了,體驗就非常的差了,非常的卡!非常的慢!為什麼?

我簡單的做了個比較.

Web端開啟新頁面過程

這裡寫圖片描述

App端開啟新頁面過程

這裡寫圖片描述

過程比較:

過程 web app
第一次載入
切換頁面

由此可見,使用android或ios巢狀webview來達到一般原生的切換效果和訪問伺服器的速度,是很難的!

此時我們想到三個方案:

  1. app端只開啟一個webview。

    • 優點:可以減少切換頁面時重新開啟(特定的導航頁面在切換時沒有被關閉掉,但不重新整理)webview的時間。
    • 缺點:b)頁面效果可能較差,並且ios打包後有可能影響appstore稽核。
  2. 伺服器端後臺檔案和前端檔案分開部署。

    • 優點:稍微加快靜態頁面訪問速度。
    • 缺點:提升速度不明顯。
  3. html檔案本地化,打包進app檔案中。

    • 優點:不需要從伺服器端請求html再返回到webview中載入,節省了載入html的時間,速度將有較大提升。
    • 缺點:需要對當前專案做很多修改(檔案訪問路徑方面,後續webview對接也給我們帶來了許多困難)

雖然是想到了三個可行方案,但第一種方案直接被pass,第二種方案速度提升非常不明顯,那隻能考慮採用第三種了,將原先html檔案與java檔案共同打包成一個war包,然後現在需要將html檔案直接打包進app檔案,必然就需要js進行跨域請求了。

JS跨域請求

s跨域請求需要在js和伺服器端都有申明跨域,具體如下:

  • 原生ajax 請求可以這樣寫:
$.ajax({
            type: 'POST',
            url: "/user",
            data: {
                'phone': 'xxxxxxxx',
                'password':'xxxxxxx'
            },
            withCredentials: true,   // 跨域
            dataType: 'json'
        }).success(function(){

        });
  • angularjs
$http.get(url, { 
            withCredentials : true    //跨域
});
  • java過濾器程式碼

@Component
public class CorsFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));   //允許跨域
        response.setHeader("Access-Control-Allow-Credentials", "true");
        if ("OPTIONS".equals(request.getMethod())) {
            response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT,OPTIONS, DELETE");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
        }
        filterChain.doFilter(request, response);
    }
}
response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));

這行程式碼是有點意思的,我這裡沒有將值直接設為“*”號,將值設為星號無法跨域傳送cookies,需要傳送cookies的可以設定成

request.getHeader("origin");

這樣跨域傳送請求就沒有問題了。

使用localStorage替代本地Cookies

跨域後有個問題真的讓我頭疼了很久,雖然成功跨域了,資料也都已經成功返回並渲染頁面了。但依然有幾個問題。

  • 登入問題

比如,登入成功後session在後臺儲存了使用者會話資訊,再次傳送請求獲取資料時,再次讀取session驗證使用者身份時,始終無法取到登入時儲存的會話資訊,因為沒有獲取到cookies,而伺服器與瀏覽器之間的會話sessionID是儲存在cookies中的,為什麼沒有獲取到cookies,是因為伺服器端允許跨域是這樣設定的:

response.setHeader("Access-Control-Allow-Origin", "*");   //允許跨域

origin設定成“*”,是不支援檔案形式的訪問所傳送cookies的,需要怎麼改呢?改成與本地檔案一樣的路徑形式就成,比如,在我的機器上如果html路徑是 file:///D:test.html,我在java端允許跨域就必須設定成

response.setHeader("Access-Control-Allow-Origin", "file://");   //允許跨域 

才能獲取到cookies,考慮到html打包進ios或android app中的路徑可能不同,則將路徑設為:

response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));

這點可讓我找了很久,“*”居然不行,我到現在也沒想通。

  • 本地cookies儲存

登入的問題解決了,傳送cookies也沒有問題了,但是本地檔案的cookies儲存又出了問題,無法儲存!也許有辦法只是我沒找到。

考慮到不同瀏覽器之間的差別和ios對cookies的可能支援不太好的問題,決定打算使用用localStorage在本地檔案儲存一些資訊,因為localStorage是html5自帶的,不存在不同瀏覽器之間的差別。
localStorage的使用也比較簡單,我在這個專案中也只使用了幾個方法。

首先申明一個全域性的storage,因為本身用他的目的就是替代cookies儲存使用者狀態,當然是全域性的啦

var storage = window.localStorage;

然後儲存值

storage.setItem('userid','深藍淺藍的天')

取值

storage.getItem('userid');

退出清空storage

storage.clear();

so easy,我很快就搜尋全部程式碼將cookies的使用全部替換掉了(注意搜尋路徑哦),但是問題到了這裡還是沒有結束,在電腦上測試完了本地檔案的訪問都沒有了問題,但是用android或ios打包html檔案又出現問題了。

post方法可正常跨域,因為它的headers中的origin是有指的,而get方法origin卻是是null,而伺服器端對於null是不接收跨域傳送cookies,所以cookies還是要忍痛替換掉。++

使用token替代跨域傳送Cookies

cookies既然有那麼多的問題,而且可能在某些地方支援不太好,之後若是需要對app進行功能擴充套件,第三方授權之類的,也還是要走token,所以索性直接換掉cookies,當然,我在js中是有區分web/ios/android的,所以手機瀏覽器端仍然延續使用cookies,ios和android打包檔案形式則用token。

雖然在伺服器程式碼中session幾乎無處不在,但要替換成token也不會特別麻煩,將原先儲存在session中的資料,使用token作為一個鍵儲存在redis中,然後在使用者請求伺服器時,攔截請求並取出token,再從redis中取出資料手動賦值到session中就可以了,只是我把原先伺服器接收請求並取出cookies中sessionID,從而取出對應session的動作替換了下而已。

簡單圖示就是:
- 伺服器處理cookies

第一次請求伺服器
這裡寫圖片描述
第二次請求伺服器
這裡寫圖片描述

大致過程如上圖,用markdown不會畫圖,下次畫個好點的~~

  • token替換cookie
    第一次請求伺服器
    這裡寫圖片描述
    第二次請求伺服器
    這裡寫圖片描述

過程幾乎是一樣的,只是我在攔截中做了一步處理,以便我不需要大幅度改變原有程式碼,如下:

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        if (session.getAttribute("userId") == null) {
            String token = request.getHeader("Authorization");
            if (token != null && !token.equals("null")) {
                String jsonToken = (String) redisTemplate.opsForValue().get(token);
                UserToken userToken = JSON.parseObject(jsonToken, UserToken.class);
                if (userToken != null) {
                    session.setAttribute("userId", userToken.getUserId());
                }
            }
        }
        return true;
    }

過程大致如此。

第一次寫這麼長的博文,歡迎指正。

歡迎關注我的個人公眾號:逍遙的心。主推程式設計師寫的生活類文章,有興趣的朋友可以共同探討下:這裡寫圖片描述