1.JSON
1.1. 轉發或重定向的不足
對於發出的請求,最終需要給出“成功/失敗”的結果的話,轉發的方式來處理,給用戶的體驗就非常不好!即使用戶填錯了一項數據不達標,都需要回退至此前的頁面重新填寫,而原本填寫的其它數據可能需要再填寫一次!
並且,當用戶發出請求后,如果是轉發或者重定向,一定會發生頁面的跳轉!無法在頁面中局部來提示信息。
從服務端的角度出來,其實,還存在浪費流量的問題!如果只是為了表達“成功/失敗”,也許只需要1個“1/0”就可以了,根本就不需要使用整個網頁來表達!
所以,當服務器需要向客戶端響應某些數據,而不是響應整個頁面時,可以使用JSON來組織數據!
1.2. JSON簡介
早期,會使用XML來組織服務器響應的數據,例如:
<result>
<state>-1</state>
<message>修改密碼失敗!原密碼錯誤!</message>
</result>
不過,這種做法導致的問題就是:解析比較麻煩,傳輸的數據量略大!后來,改用了JSON格式:
{
"state": -1,
"message":"修改密碼失敗!原密碼錯誤!"
}
JSON是一種組織數據的格式,JavaScript天生就支持解析JSON數據,並且,這種格式相對於XML更加簡潔,在傳輸過程中產生的流量將更小!
1.3. 語法格式
JSON是在JavaScript中的一種對象型數據,其數據的組成方式大致是:使用大括號框住所有內容,每項數據的名稱都是字符串,需要使用引號(單/雙均可)框住數據的名稱,然后使用冒號與值分隔,值可以是各種數據類型,例如:數值型的值可以直接寫,而字符串型的值需要引號,各個數據之間使用逗號進行分隔:
<script type="text/javascript">
var obj = { "state": -1, "message": "error" };
</script>
在JavaScript中,可以直接訪問JSON對象中的任意數據,語法為:
var state = obj.state;
JSON中的數組
在JSON中的數據的值也可以是數組,例如:
var obj = { "state": -1, "message": "error", "skill": ["Java", "MySQL", "AJAX"] };
即使用[]
表示數組,各數組元素之間使用逗號分隔。
則通過obj.skill
可以訪問到整個數組,通過obj.skill[下標]
可以訪問到數組中的某個元素,和其它語言一樣,數組的下標是從0開始編號的。
JSON中的對象
在JSON中的某個數據的值也可以是對象型的,例如:
var obj = { "state": -1, "message": "error", "skill": ["Java", "MySQL", "AJAX"], "user": { "username": "Jack", "age": 18 } };
則使用obj.user
就可以訪問到整個對象,使用obj.user.username
就可以訪問到"Jack"
這個值!
其實,在JSON中,可以無限的嵌套下去,即:JSON對象中包括某個子級對象,子級對象中還可以繼續包含另一個子級對象,一直包含下去……
JSON對象與JSON字符串
在實際應用中,JSON對象往往並不是直接編寫出來的,而是通過服務器端返回的!而服務器端返回的一定是字符串,而不會是一個JSON對象!即服務端返回的會是:
'{ "state": -1, "message": "error" }'
即:內容本身是JSON語法所組織的,但是,數據類型卻是字符串,而不是JSON對象!
在JavaScript中,可以通過JSON.parse(str)
函數將參數str
進行轉換,得到JSON對象!
1.4. 小結
JSON是一種組織數據結構的對象,支持在對象中存放若干個屬性,屬性的類型可以是各種類型,包括常用數據類型、數組和其它對象。
學習JSON重點掌握如何通過JSON組織數據。
JavaScript天生支持直接解析JSON對象,獲取其中的數據。
當獲取到的是JSON格式的字符串時,可以通過JSON.parse(str)
函數將字符串轉換為JSON對象!
2. 服務器向客戶端響應數據
當服務器向客戶端響應的需要是某些數據,而不是轉發或者重定向時,需要在處理請求的方法之前添加@ResponseBody
,而該注解默認是不識別的,在使用該注解之前,需要在Spring的配置文件中添加注解驅動:
<mvc:annotation-driven />
例如處理請求時:
@RequestMapping("/login.do")
@ResponseBody
public String showLogin() {
return "hello, login"; // /WEB-INF/login.jsp
}
則在瀏覽器中得到的將是"hello, login"
字符串,而不是網頁的源代碼!
注意:在沒有進一步的配置之前,返回的內容中並不支持中文!
以“處理登錄”為例,可以為處理請求的方法添加@ResponseBody
注解,然后,調整各種情況下的返回值,無論是哪種情況(成功或失敗),都使用JSON語法組織數據:
@RequestMapping(value="/handle_login.do",
method=RequestMethod.POST)
@ResponseBody
public String handleLogin(
@RequestParam("username") String username,
@RequestParam("password") String password,
HttpSession session) {
try {
User user
= userService.login(username, password);
session.setAttribute("uid", user.getId());
session.setAttribute("username", user.getUsername());
return "{ \"state\": 1 }";
} catch (UserNotFoundException e) {
return "{ \"state\": -1, \"message\": \"" + e.getMessage() + "\" }";
} catch (PasswordNotMatchException e) {
return "{ \"state\": -2, \"message\": \"" + e.getMessage() +"\" }";
}
}
3. Jackson框架
使用Jackson框架,可以解決響應的數據可能亂碼的問題(本質上是設置了響應頭中的響應字符編碼),並且,當響應某個對象時,會自動裝響應頭中的響應類型設置為json類型。
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.7</version>
</dependency>
除此以外,Jackson框架還可以將響應的對象自動轉換為JSON字符串格式!通常,可以在項目中添加ResponseResult
類:
public class ResponseResult {
private Integer state;
private String message;
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
SpringMVC框架對Jackson框架的支持性非常好,添加了Jackson依賴后,無須添加任何配置,也不需要在項目的任何位置顯式的使用它。
4. AJAX
4.1. 簡介
AJAX是一種異步提交請求並處理響應結果的做法,它本身是基於JavaScript實現的!
通常使用基於jQuery的AJAX處理方式,可以解決不同瀏覽器的版本兼容問題,並且使得代碼的可讀性變得更高,所以,通常需要:
<script type="text/javascript" src="${pageContext.request.contextPath }/jquery-3.3.1.min.js"></script>
然后調用jQuery中的ajax()函數來處理AJAX。
在ajax()函數的參數中,至少配置5個屬性:
- url:將請求提交到哪個URL
- data:提交的參數
- type:請求的類型
- dataType:服務端響應的數據類型,可以是text、xml、json
- success:當服務器正確響應(200)時如何處理,值是匿名函數,函數的參數就是服務端響應的JSON字符串轉換得到的JSON對象
4.2. 小結
使用這種模式開發時,關於控制器端,應該:
1 處理請求的方法需要添加@ResponseBody
注解;
2 處理請求的方法的返回值應該是ResponseResult
;
3 在處理請求的方法內部,無論是成功還是失敗,當決定執行響應時,都應該在ResponseResult
對象中封裝必要的數據,然后返回ResponseResult
對象。
關於前端,應該:
1 由於需要使用jQuery,所以,必須引入jQuery框架;
2 前端頁面不強制需要<form>
,並且,用戶的提交操作觸發的不是傳統的提交表單,而應該觸發JS事件,例如:可以將提交按鈕的類型調整為type="button"
,並配置onclick="doReg()"
,表示當點擊這個按鈕時,執行JavaScript中的doReg()
方法;
3 自定義JavaScript函數,且函數名與以上步驟中點擊事件對應一致:
<script type="text/javascript">
function doReg() {
}
</script>
4 然后編寫函數體,主要任務是調用jQuery框架中的ajax()函數來實現提交請求並獲取響應結果:
function doReg() {
// 獲取用戶輸入的數據
var u = $("#username").val();
var p = $("#password").val();
// 處理請求的URL
var url = "handle_reg.do";
// 提交到服務端的參數
var data = "username=" + u + "&password=" + p;
// 發出請求,並獲取響應結果
$.ajax({
"url": url,
"data": data,
"type": "post",
"dataType": "json",
"success": function(json) {
// 由於服務端使用了Jackson
// 所以響應頭中由Jackson自動設置了響應正文是json
// 所以dataType的值就是"json"
// 且當前成功時的處理函數的參數就是服務端響應的json對象
if (json.state == 1) {
alert("注冊成功!");
} else {
alert("注冊失敗!" + json.message);
}
}
});
}