先後端分離開發與跨域問題

先後端分離

傳統開發方式

曾幾什麼時候,JSP和Servlet爲Java帶來了無限風光,一時間大紅大紫,但隨着互聯網的不斷髮展,這樣的開發方式逐漸顯露其弊端,在移動互聯網煊赫一時的今天,應用程序對於後臺服務的要求發生了巨大的變化;html

傳統的項目開發與交互流程:

image-20191222014829931

image-20191222131131302

在傳統的web開發中,頁面展現的內容以及頁面之間的跳轉邏輯,全都由後臺來控制,這致使了先後端耦合度很是高,耦合度高則意味着,擴展性差,維護性差,等等問題前端

傳統開發的問題以下:java

  • 耦合度高
  • 調試麻煩,出現問題時每每須要先後臺一塊兒檢查node

  • 開發效率低,先後端相互依賴,溝通成本,維護成本高
  • 擴展性差,沒法兼容其餘終端
  • 交互邏輯混亂(還記得分頁顯示嗎),最終形成代碼腐爛jquery

爲了適應快速發展的移動互聯網,加快開發速度,就必須找到一種新的項目開發方式,來解決以上問題;web

先後端分離開發方式

上述問題產生的根本緣由在於先後端沒有明顯的分界,嚴重耦合在一塊兒,想要解決這些問題的核心也就是將先後端代碼徹底分開ajax

先後端分離開發與交互流程:

image-20191222132027794

image-20191222133331746

有啥不同嗎?

對比咱們以前編寫的整個項目結構和部署環境而言,有如下區別數據庫

特性/方式 傳統 先後端分離
服務器環境 所有部署到tomca便可 增長一個靜態資源服務器
MVC職責 後臺負責MVC所有 前臺負責VC,後臺負責M

簡單的說:json

先後端分離在傳統開發上增長了一個服務器處理靜態資源,將View層和Controller層放到了前端,後臺僅需處理數據存取相關以及業務邏輯相關後端

前端:負責View和Controller層。

後端:只負責Model層,業務處理/數據等。

先後端分離的優缺點

優勢:

  • 關注點分離,視圖層和控制層邏輯移到了前端,後端更注重業務邏輯和系統構架

  • 耦合大大下降,開發效率和維護效率都獲得提升

  • 錯誤友好,後臺錯誤不影響前臺界面展現

  • 對於開發者,先後端再也不須要過多的涉及彼此的開發語言

缺點:

  • 前端開發者壓力更大,須要關注Controller層
  • 增長靜態服務器後,系統結構更復雜
  • 更多的HTTP請求,在移動端運行效率差
  • 邏輯靠近前端,不一樣平臺需針對性重複實現,(安卓iOS+web)
  • SEO優化無力,爬蟲大多不支持ajax

先後端的從新定義:

在此以前區分先後端是根據硬件環境

分離後根據職責劃分:如圖

image-20191222145832786

先後端分離頁面執行流程(針對瀏覽器)

Controller層中會使用流程控制來完成數據校驗,數據解析,頁面的跳轉等動做,那麼如何完成呢,這就須要使用到JavaScript了

那麼一個先後端分離的項目,前端是如何完成最終的數據展現呢?

未命名文件-2

若是前端是其餘的例如iOS,安卓,則無需請求靜態頁面,頁面的繪製是由系統原始語言實現的,只須要向後臺請求json數據便可

what is json?

JSON全稱(JavaScript Object Notation),js對象表示法,是一種輕量級數據交換格式

特色:

  • 格式簡單
  • 解析方便,跨平臺
  • 輕量級
  • 內容爲字符串

最初產生於web項目中,後來由於太優秀,被各類CS結構項目使用

案例:

{
                "uniquekey":"a56e67162bd84ee9c480e22a1170c14b",
                "title":"人均負債17萬從「全民儲蓄」到「全民負債」 中國人的錢去哪兒了?",
                "date":"2019-12-22 13:43",
                "category":"頭條",
                "author_name":"鶴川傾海",
                "url":"http://mini.eastday.com/mobile/191222134359221.html",
                "thumbnail_pic_s":"http://05imgmini.eastday.com/mobile/20191222/20191222134359_b6ded6df388f5c6747e67bacfc32d125_4_mwpm_03200403.jpg"
}

你能夠將其看作Map數據結構,以鍵值對形式存儲,可是一些java中的數據結構json中是沒有的例如,集合

不知足現狀的前端小夥伴們-node.js

如今你已經知道了先後端分離的概念以及運行流程,做爲後端開發的你不由暗自竊喜,好了之後Controller不歸我管了,你正要開始葛優躺時前端小夥伴炸鍋了:

尼瑪,各類頁面跳轉邏輯,用戶驗證邏輯,前端的表單驗證………..,難道要在學學java?習慣了js的他們,確定不肯意

問題就是動力,國外的Ryan Dahl這爲兄臺,決定改變這個現狀,因而開發了使用JS做爲開發語言的Node.js(2009/5),提供了異步IO,數據庫支持,網絡支持,等等,廣大前端開發者激動落淚,之後別整什麼java了,先後臺一套JS全搞定,夢想是美好的,java(1996)是強大的,經歷了二十幾年的努力java已是如此強大,穩定,node.js做爲晚了13年的後期之秀還有很長的路要走;

相信使用一門語言編寫全部程序的一天總會到來,也有不少人在努力實現這個夢想,例如RectNative;

Node.js在先後端分離中的做用

你們都知道淘寶網站作得很大面對成百上千的前臺頁面,想要提升總體開發效率,以及項目擴展性等,必然要採用先後端分離,大量的Controller層邏輯,以及數據校驗邏輯,致使前端開發亞歷山大,而且沒有相對固定的開發模型,很是容易出問題

淘寶目前使用了一種叫作中途島的構架,利用Node.js來完成了Controller層,並提供了一些其餘良好的特性

構架:

職責:
img

優勢:

  • 請求聚合,Node在服務器端整合多個請求響應,一次性返回,效率更高
  • SEO優化
  • JS語言,學習成本低
  • 瀏覽器與Node.js端代碼可重用
  • 服務器渲染

若是將其做爲一個前端框架,Node.js對比Vue.js等無疑是重量級的

強調:

先後端分離並非必定要加入node.js,先後端分離是責任劃分問題,與使用什麼服務器沒有關係;

嘗試一下

在頁面中使用Ajax來請求數據,服務器返回json數據,前端使用js完成頁面渲染

Ajax

Ajax是客戶端的一種請求方式,全稱(Asynchronous Javascript And XML)

用於異步的向服務器發送HTTP請求並獲取響應數據,異步的好處在於,請求期間瀏覽器不會卡死,能夠正常響應用戶操做;而常見的表單提交,和直接打開指定地址,都是同步的;

對象轉json

json數據的基本形式是鍵值對,對應着對象中的屬性,是有規律的,那就可使用代碼來完成這一轉換過程,下面列出了一些常見的json開源工具

  • Gson
  • FastJson
  • Jackson
  • Json-lib

性能對比請看這裏對比

這裏選擇fastjson

下載jar

http://repo1.maven.org/maven2/com/alibaba/fastjson/

案例:

import com.alibaba.fastjson.JSON;

import java.util.ArrayList;

public class Demo {
    public static void main(String[] args) {
        //Bean熟悉
        ArrayList<String> hobby = new ArrayList<>();
        hobby.add("girl");
        hobby.add("music");
        hobby.add("coding");

        //JavaBean
        Person p = new Person("jerry",18,true,hobby);
        p.setDog(new Dog("小花花"));

        //Bean轉json字符串
        String s = JSON.toJSONString(p);
        System.out.println(s);

        //json字符轉Bean
        Person person = JSON.parseObject(s,Person.class);
        System.out.println(person);

        //ArrAyList轉json字符串
        System.out.println(JSON.toJSONString(hobby));
    }
}

先後端分離案例

如今把本身當成是前端,要開發一個先後分離的簡單頁面,用於展現學生信息列表

第一步

編寫一個用於展現表格的靜態頁面

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>

    </head>
    <body>
        <table id="tab" border="1">
            <tr>
                <th>編號</th>
                <th>名字</th>
                <th>年齡</th>
                <th>性別</th>
            </tr>
        </table>
        <button onclick="req()">請求數據</button>
        <img id="img" />
    </body>
</html>

不啓動tomcat直接在編輯器中打開便可訪問,測試他就是一個靜態網頁,而咱們的編輯器就是一個HTTP服務器,能夠響應靜態網頁

第二步

引入jquery使得ajax編寫更方便

<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>

第三步

編寫ajax,向服務器發送請求

第四步

將數據展現到頁面上

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
        <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    </head>
    <body>
        
        <table id="tab" border="1">
            <tr>
                <th>編號</th>
                <th>名字</th>
                <th>年齡</th>
                <th>性別</th>
            </tr>
        </table>
        <button onclick="req()">請求數據</button>
        <img id="img" />
    </body>
    <script>
        function req(){
            document.getElementById("img").src = "img/timg.gif";
            $.ajax({
                url:"http://localhost:8080/MyServer/getData",
                success:function(data){
                    console.log(data);
                    document.body.insertAdjacentHTML("beforeend","<h1>%</h1>".replace("%",data));
                    document.getElementById("img").src = "";    
                },
                error:function(err){
                    console.log(err);
                    document.getElementById("img").src = "";    
                }
            });
        }
    </script>
</html>

如今身份切換回後端開發用於獲取表格數據的接口

  1. 建立web項目
  2. 建立Servlet
  3. 引入fastjson
  4. 建立一個bean類
  5. 建立一堆bean放入列表中
  6. 將列表轉爲json字符串 返回給前端

Servlet代碼

package com.kkb;

import java.io.IOException;

public class AServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
    }
    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        String s = "{\"name\":\"jack\"}";
        response.getWriter().println(s);
    }
}

啓動服務,測試訪問,會發現頁面上沒有顯示服務器返回的結果….

跨越問題

image-20191222174227720

打開瀏覽器檢查頁面會發現沒有輸出服務器返回的消息而是,出現了一個錯誤信息,這就是先後端分離最多見的跨越問題

什麼是跨域

跨越爲題之因此產生是由於瀏覽器都遵循了同源策略

同源策略:

同源策略(Same origin policy)是一種約定,它是[瀏覽器]最核心也最基本的安全功能,若是缺乏了同源策略,則瀏覽器的正常功能可能都會受到影響。能夠說Web是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。

同源策略是瀏覽器的行爲,是爲了保護本地數據不被JavaScript代碼獲取回來的數據污染,瀏覽器會先發送OPTION請求進行預檢查,判斷服務器是否容許跨域,若是容許才發送真正的請求,不然拋出異常。

簡單的說:

同源策略瀏覽器的核心安全機制,其不容許在頁面中解析執行來自其餘服務器數據

如何判斷是否跨域

當一個請求url的協議、域名、端口三者之間任意一個與當前頁面url不一樣即爲跨域

同源限制:
  1. 沒法讀取非同源網頁的 Cookie、LocalStorage 和 IndexedDB

  2. 沒法向非同源地址發送 AJAX 請求

何時產生跨域問題:

瀏覽器在解析執行一個網頁時,若是頁面中的js代碼請求了另外一個非同源的資源,則會產生跨越問題

而瀏覽器直接跳轉另外一個非同源的地址時不會有跨域問題

解決跨越問題

既然禁止跨域問題時瀏覽器的行爲,那麼只須要設置瀏覽器運行解析跨域請求的數據便可,可是這個設置必須放在服務器端,由服務器端來判斷對方是否可信任

在響應頭中添加一個字段,告訴瀏覽器,某個服務器是可信的

package com.kkb;

import java.io.IOException;

public class AServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

    }

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        response.setHeader("Access-Control-Allow-Origin","*");
        String s = "{\"name\":\"jack\"}";
        response.getWriter().println(s);
    }
}

其值跨越式某個指定的域名,也能夠是*表示信任全部地址

其餘相關設置

//指定容許其餘域名訪問
'Access-Control-Allow-Origin:http://XXX.XXX.XXX'//通常用法(*,指定域,動態設置),注意*不容許攜帶認證頭和cookies
//是否容許後續請求攜帶認證信息(cookies),該值只能是true,默認爲false
//且須要前端配合進行相應設置才能讓cookie跨越
'Access-Control-Allow-Credentials:true'
//預檢查間隔時間
'Access-Control-Max-Age: 1800'
//容許的請求類型
'Access-Control-Allow-Methods:GET,POST,PUT,POST'
//列出必須攜帶的字段
'Access-Control-Allow-Headers:x-requested-with,content-type'

解決了跨越問題後再來完善上面的案例

Servlet代碼:
package com.kkb;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.io.IOException;
import java.util.ArrayList;

public class AServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

    }

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {
        //容許來自任何主機的跨越訪問
        response.setHeader("Access-Control-Allow-Origin","*");
                //設置響應類型爲json數據
        response.setContentType("application/json;charset=utf-8");

        //學生信息
        ArrayList<Student> students = new ArrayList<>();

        Student stu1 = new Student("s1","jack",20,"man");
        Student stu2 = new Student("s2","tom",22,"girl");
        Student stu3 = new Student("s3","jerry",10,"woman");
        Student stu4 = new Student("s4","scot",24,"boy");
        students.add(stu1);
        students.add(stu2);
        students.add(stu3);
        students.add(stu4);
        response.getWriter().println(JSON.toJSONString(JSON.toJSONString(students)));
    }
}
HTML代碼
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title></title>
        <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    </head>
    <body>
        <table id="tab" border="1">
            <tr>
                <th>編號</th>
                <th>名字</th>
                <th>年齡</th>
                <th>性別</th>
            </tr>
        </table>
        <button onclick="req()">請求數據</button>
        <img id="img" />
    </body>
    <script>
        function req(){
            document.getElementById("img").src = "img/timg.gif";
            $.ajax({
                url:"http://localhost:8080/MyServer/getData",
                success:function(data){
                    data = JSON.parse(data)
                    console.log(data)
                    for (var i = 0; i < data.length; i++) { 
                        a = data[i];
                        var row = "<tr><td>id</td><td>name</td><td>age</td><td>gender</td></tr>"
                            row = row.replace("id",a.id);
                            row = row.replace("name",a.name);
                            row = row.replace("age",a.age);
                            row = row.replace("gender",a.gender);   
                            document.getElementById("tab").insertAdjacentHTML("beforeend",row);
                    }
                    document.getElementById("img").src = "";    
                },
                error:function(err){
                    console.log(err);
                    document.getElementById("img").src = "";    
                }
            });
        }
    </script>
</html>

一個簡單的先後端分離的案例就搞定了

思考用戶身份身份狀態保持的問題?

相關文章
相關標籤/搜索