1. 程式人生 > >SpringMVC接受JSON引數詳解及常見錯誤總結(@RequestBody轉載)

SpringMVC接受JSON引數詳解及常見錯誤總結(@RequestBody轉載)

最近一段時間不想使用Session了,想感受一下Token這樣比較安全,穩健的方式,順便寫一個統一的介面給瀏覽器還有APP。所以把一個練手專案的前臺全部改成Ajax了,跳轉再使用SpringMVC控制轉發。對於傳輸JSON資料這邊有了更深的一些理解,分享出來,請大家指正。

在SpringMVC中我們可以選擇數種接受JSON的方式,在說SpringMVC如何接受JSON之前,我們先聊聊什麼是JSON。具體的定義我也不贅述了,在JavaScript中我們經常這樣定義JSON 物件

var jsonObject = {
    "username":"admin",
    "password"
:123 }

這種形式的我們叫它JSON物件,同時還有一個概念叫做JSON字串,字串呢,顧名思義,是由’ ‘或者” “包裹起來的一個整體,我們稱之為字串。我們知道字串是可以直接輸出的,而物件不能直接輸出。所以在JavaScript中,我們可以

//定義一個物件 jsonObject
var jsonObject = {
    "username":"admin",
    "password":123
};
alert(jsonObject);

此時,會顯示[object Object]而不會輸出JSON物件的內容,JavaScript向我們提供了兩個工具

JSON.parse() 
用於將一個 JSON
字串轉換為 JavaScript 物件。 JSON.stringify() 用於將 JavaScript 值轉換為 JSON 字串。

所以當我們輸入

alert(JSON.stringify(jsonObject));

就會顯示 {“username”:”admin”,”password”:123};

好了 對於JSON的講解就到這裡了 下面我們說一說SpringMVC

既然JSON有著上述兩種存在方式,那我們通過ajax向SpringMVC傳值的時候,我們該傳哪一種呢?
我們首先嚐試直接傳送JSON物件

//定義json物件
            var username = $("#username"
).val(); var password = $("#password").val(); var json = { "username" : username, "password" : password }; // Jquery Ajax請求 $.ajax({ url : "jsontest", type : "POST", async : true, data : json, dataType : 'json', success : function(data) { if (data.userstatus === "success") { $("#errorMsg").remove(); } else { if ($("#errorMsg").length <= 0) { $("form[name=loginForm]").append(errorMsg); } } } });

我們首先想想SpringMVC提供了什麼給我們,有一個@RequestParam的註解,對於這個註解,它的作用和我們Servlet中的request.getParameter是基本相同的。我們首先使用這個註解來獲取

@RequestMapping("/jsontest")
    public void test(@RequestParam(value="username",required=true) String username,
    @RequestParam(value="password",required=true) String password){
        System.out.println("username: " + username);
        System.out.println("password: " + password);
    }

後臺成功輸出的我們的引數,成功接受!
SpringMVC如此智慧,如果我們去除@RequestParam註解,直接將兩個值放入會有什麼後果?

@RequestMapping("/jsontest")
    public void test(String username,String password){
        System.out.println("username: " + username);
        System.out.println("password: " + password);
    }

竟然同樣成功了,原理我這裡就不多贅述了,有興趣的朋友們可以打斷點看看。

SpringMVC提供了一個@RequestBody,它是用來處理前臺定義發來的資料Content-Type: 不是application/x-www-form-urlencoded編碼的內容,例如application/json, application/xml等;
細心的朋友們或許發現了,在之前的Ajax中,我們沒有定義Content-type的型別,Jquery預設使用application/x-www-form-urlencoded型別。那麼意思就是SpringMVC的@RequestParam註解,Servlet的request.getParameter是可以接受到以這種格式傳輸的JSON物件的。

為什麼呢!?GET請求想必大家都不陌生,它將引數以url?username=”admin”&password=123這種方式傳送到伺服器,並且request.getParameter可以接收到這種引數,我們在瀏覽器位址列上也可以看到這一點。而我們Ajax使用的POST,並且傳送的是JSON物件,那麼後臺是如何獲取到的呢?答案就在於這個Content-Type x-www-form-urlencoded的編碼方式把JSON資料轉換成一個字串,(username=”admin”&password=123)然後把這個字串新增到url後面,用?分割,(是不是和GET方法很像),提交方式為POST時候,瀏覽器把資料封裝到HTTP BODY中,然後傳送到伺服器。所以並不會顯示在URL上。(這段可能有點繞口,希望大家用心理解一下。)
終於說完了,長吐一口氣。所以說我們使用@RequestBody註解的時候,前臺的Content-Type必須要改為application/json,如果沒有更改,前臺會報錯415(Unsupported Media Type)。後臺日誌就會報錯Content type ‘application/x-www-form-urlencoded;charset=UTF-8’ not supported,這些錯誤Eclipse下Tomcat是不會顯示錯誤資訊的,只有使用了日誌才會顯示,如何配置日誌大家可以看我上一篇文章。接下來我們正確配置一下,上面說到了 Content-Type需要更改,同時我們的data也要更改了,這種註解方式只接受JSON字串而不是JSON物件

$.ajax({
                url : "jsontest",
                type : "POST",
                async : true,
                contentType : "application/json",
                data : JSON.stringify(json),
                dataType : 'json',
                success : function(data) {
                    if (data.userstatus === "success") {
                        $("#errorMsg").remove();
                    } else {
                        if ($("#errorMsg").length <= 0) {
                            $("form[name=loginForm]").append(errorMsg);
                        }
                    }
                }
            });

後臺也更改一下,json其實可以理解為鍵值對嘛,所以我們用Map接收,然後對字串或者其他資料型別進行進一步處理。

@RequestMapping("/jsontest")
    public void test(@RequestBody(required=true) Map<String,Object> map  ){
        String username = map.get("username").toString();
        String password = map.get("password").toString();
        System.out.println("username: " + username);
        System.out.println("password: " + password);
    }

同時,我又想起了神奇的SpringMVC,所以我決定去掉註解試試,好的,果斷被爆了一個空指標錯誤…嘗試就此打住。
SpringMVC還提供了引數直接和POJO繫結的方法,我們來嘗試一下。前臺一樣,就不貼出來了。

@RequestMapping("/jsontest")
    public void test(@RequestBody User user  ){
        String username = user.getUsername();
        String password = user.getPassword();
        System.out.println("username: " + username);
        System.out.println("password: " + password);
    }

OK,這次是可以取到值的,我個人對於登入這類小資料量的上傳來說不太喜歡這種方法,User裡面的變數很多,我只用了其中兩個,沒有必要去建立一個User物件,一般資料量小的時候我還是比較喜歡使用單獨取值出來的。我們再想一想,如果是在上傳JSON物件的情況下,我們可不可以繫結POJO呢,答案是可以的,不要使用@RequestParam註解,否則會報Required User parameter ‘user’ is not present錯誤。到此講解基本結束了,下面來總結一下。

注意:
(1)當Ajax以application/x-www-form-urlencoded格式上傳即使用JSON物件,後臺需要使用@RequestParam 或者Servlet獲取。
(2) 當Ajax以application/json格式上傳即使用JSON字串,後臺需要使用@RquestBody獲取。