1. 程式人生 > >前後端資料互動,跳轉

前後端資料互動,跳轉

概述

作為一枚菜鳥,前後端互動可是大問題,經常資料互動失敗,不知道怎麼跳轉。在這分享一下互動的小心得。

我們不妨先大概瞭解一下整個訪問流程:

使用者從輸入網址按下回車,互動就已經開始了。

瀏覽器會將請求按照http協議(或者其他https,ftp等)將請求資料封裝包從電腦的埠發出 -> 路由器 -> 運營商(域名解析之類的)-> 目標伺服器(可能會有代理,負載均衡等等)

最終從伺服器開放的埠,進入到伺服器(TCP三次握手和四次揮手,Tomcat之類的,會根據協議的內容進行解析)-> web專案進行具體的邏輯處理 -> 返回資料 -> 瀏覽器接收資料(根據協議內容進行解析)

整個過程,協議很重要,因為客戶端和伺服器都是通過協議來解析和傳送資訊的,最常見的http協議,協議頭部的引數常用的哪幾個,有什麼影響。

Http協議的簡介,說的很有意思:https://blog.csdn.net/u010256388/article/details/68491509/

還有快取問題(cookie、session、localstorage、cashe-controller等),不僅提高效率,而且有時候你更改了程式碼測試卻沒變化的重要原因:https://www.jianshu.com/p/9ed3e8759ce3

相應的後臺tomcat的快取處理:http://www.360doc.com/content/17/0721/17/41344223_673116604.shtml(瞭解即可)

 

具體到使用分為:前端互動,分為同步和非同步。

同步互動

常見的from表單提交(post方式帶引數),URL直接訪問(get方式,有引數在位址列可見,特殊符號需要轉義,不安全)。

非同步互動

關鍵是XMLHttpRequest物件,平時說的ajax只是一種互動模式,並不是什麼新技術,其原理就是對XMLHttpRequest的封裝。所以對XMLHttpRequest深入全面的瞭解可以幫助我們更好的掌握非同步互動。

可以看這篇精品文章瞭解:https://www.jianshu.com/p/b037f71af548,裡面涉及到了在傳送過程中各個引數的用法,會觸發的事件,返回值等等很實用的知識。(雖然能設定同步互動,但不推薦)

其中傳輸型別 contentType就非常重要,這個值設定決定了後臺怎麼去解析http協議。

 

  • 上圖所示,如果格式前後端傳送和接收對不上,那是獲取不了資料。如圖的最後一步返回,如果想頁面跳轉,則不需要@ResponseBody,改為返回ModelAndView
  • @RequestBody主要用來接收前端傳遞給後端的json字串中的資料的(請求體中的資料的);GET方式無請求體,所以使用@RequestBody接收資料時,前端不能使用GET方式提交資料,而是用POST方式進行提交。
  • @RequestParam接收的是key-value裡面的引數,所以GET方式的資料和表單提交,可以接收。
  • @RequestBody和@RequestParma的使用知識,更輕鬆的全面掌握:https://blog.csdn.net/justry_deng/article/details/80972817

前端互動類

前端我建立了兩個工具類,來負責互動。

非同步互動類

我把jQuery的ajax進行了封裝,主要是獲取和計算資訊,程式碼如下:

  1 /*
  2  * 以下為程式錯誤碼
  3  */
  4 //通用的請求失敗,包括未知原因
  5 var EXPECTATION_FAILED = 417;
  6 var EXPECTATION_QUERY = 404;
  7 
  8 /**
  9  * 訪問後臺的物件,為ajax封裝
 10  * @constructor
 11  */
 12 var Query = function (url, param, callback, contentType) {
 13     this.url = url;
 14 
 15     //先確認引數存在,如果不存在則建立空map
 16     if (!param) {
 17         param = new Map();
 18     }
 19     //注意,要根據不同的傳輸格式來確定傳輸的值的型別
 20     if (contentType == Query.NOMAL_TYPE) {
 21         this.param = JSON.parse(this._convertParam(param));
 22     } else {
 23         this.param = this._convertParam(param);
 24     }
 25 
 26 
 27     this.callback = callback;
 28     this.contentType = contentType;
 29     //請求超時,預設5秒
 30     this.timeout = 5000;
 31     //是否非同步請求,預設非同步
 32     this.async = true;
 33 }
 34 
 35 Query.JSON_TYPE = 'application/json';
 36 Query.NOMAL_TYPE = 'application/x-www-form-urlencoded';
 37 
 38 /**
 39  * ajax請求的訪問
 40  * @param url 要訪問的地址
 41  * @param paramMap 傳給後臺的Map引數,key為字串型別
 42  * @param callback 回撥函式
 43  * @param contentType 傳輸資料的格式  預設傳輸application/x-www-form-urlencoded格式
 44  */
 45 Query.create = function (url, paramMap, callback) {
 46     return new Query(url, paramMap, callback, Query.NOMAL_TYPE);
 47 }
 48 
 49 Query.createJsonType = function (url, paramMap, callback) {
 50     return new Query(url, paramMap, callback, Query.JSON_TYPE);
 51 }
 52 
 53 /**
 54  * 將ParamMap轉為json格式,目前只支援Map物件,以後會擴充套件
 55  * @param paramMap
 56  * @private
 57  */
 58 Query.prototype._convertParam = function (param) {
 59 
 60     if (param instanceof Map) {
 61         return strMap2Json(param);
 62     }
 63 }
 64 
 65 /**
 66  * 對ajax回撥函式的封裝
 67  * @param callBack
 68  * @private
 69  */
 70 Query.prototype._callback = function (queryResult) {
 71 
 72     //取消載入框
 73     if (this.loadDom) {
 74         $(this.loadDom).remove("#loadingDiv");
 75     }
 76 
 77     //Query物件
 78     var self = queryResult.queryObj;
 79     var data = $.parseJSON(queryResult.responseText);
 80     //記錄請求是否有錯誤
 81     self.queryException = false;
 82     var handleError;
 83 
 84     if (queryResult.status == EXPECTATION_FAILED || queryResult.status == EXPECTATION_QUERY) {
 85         var error = queryResult.responseText;
 86         self.queryException = true;
 87     }
 88 
 89     //呼叫回撥函式,如果返回結果為true,則對於出錯不會預設錯誤處理
 90     if (self.callback instanceof Function) {
 91         handleError = self.callback(data);
 92     }
 93 
 94     //如果出現了異常並且沒有被處理,那麼將進行預設錯誤處理
 95     if (self.queryException && !handleError) {
 96         window.location.href = "/system/error/" + error.code + "/" + error.msg;
 97     }
 98 
 99     //如果需要跳轉,則進行跳轉
100     if (data.redirect_url) {
101         window.location.href = data.redirect_url;
102     }
103 }
104 
105 /**
106  * 正式傳送ajax
107  * @private
108  */
109 Query.prototype.sendMessage = function () {
110     var self = this;
111     var xhr = $.ajax(
112         {
113             type: "post",
114             url: this.url,
115             contentType: this.contentType,
116             data: this.param,
117             // ajax傳送前呼叫的方法,初始化等待動畫
118             // @param XHR  XMLHttpRequest物件
119             beforeSend: function (XHR) {
120                 //繫結本次請求的queryObj
121                 XHR.queryObj = self;
122                 if (self.beforeSendFunc instanceof Function) {
123                     self.beforeSendFunc(XHR);
124                 }
125 
126                 if (self.loadDom instanceof HTMLElement) {
127                     self.loadDom.innerText = "";
128                     $(self.loadDom).append("<div id='loadingDiv' class='loading'><img src='/image/loading.gif'/></div>");
129                 } else if (self.loadDom instanceof jQuery) {
130                     self.loadDom.empty();
131                     self.loadDom.append("<div id='loadingDiv' class='loading'><img src='/image/loading.gif'/></div>");
132                 }
133             },
134             complete: this._callback,
135             timeout:this.timeout,
136             async:this.async
137         }
138     );
139 
140 }
141 
142 /**
143  * 檢測是否有錯誤,返回ture有錯誤,或者false
144  */
145 Query.prototype.checkEception = function () {
146     return this.queryException;
147 }
148 
149 //------------------------以下為對Query的引數設定---------------------------
150 /**
151  * 在ajax傳送前設定引數,可以有載入的動畫,並且請求完成後會自動取消
152  * @param loadDom 需要顯示動畫的dom節點
153  * @param beforeSendFunc ajax傳送前的自定義函式
154  */
155 Query.prototype.setBeforeSend = function (loadDom, beforeSendFunc) {
156     this.loadDom = loadDom;
157     this.beforeSendFunc = beforeSendFunc;
158 }
159 
160 /**
161  * 設定超時時間
162  * @param timeout
163  */
164 Query.prototype.setTimeOut = function (timeout) {
165     this.timeout = timeout;
166 }
167 
168 Query.prototype.setAsync = function (async) {
169     this.async = async;
170 }

封裝的好處:

  • 可以省略很多重複的程式碼,如$.ajax傳引數那一長串(111-136行)
  • 可以規定統一的前端互動流程,並且修改這個流程也方便。
    1. 根據傳輸型別也對引數進行轉換(20-24行),並且_convertParam()方法能繼續擴充套件引數型別,直到後面可以把絕大多數引數都轉換成正確的格式(那傳送基本不用考慮引數格式了,只要確定傳輸的型別就好)              
    2. 統一回調流程(70-103行)可以對異常做統一處理(ajax)像我這樣跳轉到錯誤頁面,或者有些非同步請求返回時需要跳轉,也能統一跳轉
    3. 統一請求傳送前的處理(119-133行)可以對請求等待統一的設定等待動畫,最後再回調流程統一取消動畫

同步互動類

from表單建立類,主要是進行有引數傳輸的頁面跳轉,因為直接URL跳轉引數會暴露不安全

 1 /**
 2  * 訪問後臺的類,構造form表單來進行post請求
 3  * @param url
 4  * @param paramMap  引數map
 5  * @constructor
 6  */
 7 var QueryForm = function (url,paramMap) {
 8     //form表單的JQ物件
 9     this.form = $("<form></form>");
10     this.form.attr("action",url);
11     this.form.attr("method","post");
12     //遍歷Map
13     for(var [key,value] of paramMap){
14        var inputDom = $("<input/>") ;
15        inputDom.attr("name",key);
16        inputDom.attr("value",value);
17        this.form.append(inputDom);
18     }
19     //必須要放入body裡面,不然請求發不出去
20     var bodyDom = $("body");
21     bodyDom.append(this.form);
22     this.sendMessage();
23 
24     //傳送完後銷燬
25     this.destroy();
26 }
27 
28 QueryForm.create = function (url, paramMap) {
29     return new QueryForm(url,paramMap);
30 }
31 
32 QueryForm.prototype.sendMessage = function () {
33      this.form.submit();
34 }
35 
36 QueryForm.prototype.destroy =  function () {
37     this.form.remove();
38 }

後端互動類

普通的Controller,在@RequestMapping中填寫正確的路徑,根據前端傳輸的資料型別來獲取引數(見上文),根據邏輯來確定要不要@ResponseBody來返回資訊,還是ModelAndView來進行頁面跳轉

路徑

經常404怎麼辦,那就是路徑有問題。得先明白,java虛擬機器執行的不是java檔案而是編譯後的.calss檔案,所以最先檢查的是target資料夾下是否有你URL寫的這個檔案,是否路徑正確

平時網上看到的classpath,就是指target下的classes資料夾

順帶提一句,如果是eclipse,右鍵點選新建,你會發現有幾個資料夾可以選

folder:就是普通的資料夾,它和我們window下面使用的資料夾沒有任何區別


source folder:資料夾是一種特別的資料夾,如果你用面向物件的思想去看待這個source folder,那麼他是folder的一個子集,作為子集,肯定是有folder的所有功能,而且還有自己特別的功能,他的特別之處,就是在source folder下面的java檔案都會被編譯,編譯後的檔案會被放在我們設定的某個資料夾下面(一般我們設定成WEB-INF/classes),source folder下面的非java檔案會被copy一份放在我們的設定的資料夾下面


package:資料夾也是一種特別的資料夾,他的特別之處在於:他必須存在於source folder下面,上下級通過.來區分,他的路徑最後組成了每一個類的包路徑名

 

所以當出現404,請先檢查路徑,如果是springboot專案,則要遵守一些預設路徑規則,比如模板必須放在templates,靜態檔案必須放在static

&n