js跨域訪問及html文件形式打包進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;
    }

過程大致如此。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM