1. 程式人生 > >【Spring-Controller 整理研究】@RequestMapping略解

【Spring-Controller 整理研究】@RequestMapping略解

本文以純後端的角度,去研究Spring Controller在各種情況的行為,及各種屬性的作用。

實驗準備

利用https://start.spring.io/快速生成一個開箱即用的小巧spring boot專案,無需進行復雜配置,非常適合進行研究實驗使用。

若以下例子未說明,結果為下述程式碼所示。

@Controller
@RequestMapping(value = "/terra")
public class TestController {
    // 放置實驗的Controller  
}

埠號在application.properties設定為9000

server.port=9000
server.contextPath
=/ server.tomcat.uri-encoding=UTF-8

 

@RequestMapping

@RequestMapping是一個用來處理請求地址對映的註解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。(抄)

一. value

value不設定和設定為""的情況下會產生衝突,編譯會報錯。

    @RequestMapping()
    public String voidController1() {
        return "voidController1";
    }

    @RequestMapping(value 
= "") public String voidController2() { return "voidController2"; }

兩者可以看做等價,但是又存在區別。

value不設定的情況下,認為是預設控制器,此時不能令返回值=void,否則會在請求時報錯

// TODO 需要翻原始碼瞭解為何報錯

Circular view path [second]: would dispatch back to the current handler URL [/second] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)

但是可以跳轉到其他類下的路徑,而value = "" 時則不會有這些問題.

@Controller
@RequestMapping(value = "/second")
public class SecondPageController {

    @RequestMapping()
    public String defaultController() {
        // 報錯
        // return "/terra";
        return "/terra/test";
    }
}

於是乎可以做到類似以下的雜技般有趣但又沒多大意義的跳轉,需要注意的是預設控制器無法直接使用本類下的子路徑,原因不明。

@Controller
@RequestMapping(value = "/terra")
public class TestController {


    @RequestMapping(value = "test")
    public String testController() {
        System.out.println("測試請求");
        return "/terra";
    }

    @RequestMapping()
    public String defaultController() {
        System.out.println("請求跳轉到了預設請求控制器");
        // 無法跳轉
        // return "/jump";
        return "/terra/jump";
    }

    @RequestMapping(value = "jump")
    public String jumpController() {
        System.out.println("請求跳轉到了jump");
        return "void";
    }

    @RequestMapping(value = "void")
    public void voidController() {
        System.out.println("請求跳轉到了void,終止");
    }

}
localhost:9000/terra/test 或者localhost:9000/second 請求控制檯輸出:
測試請求
請求跳轉到了預設請求控制器
請求跳轉到了jump
請求跳轉到了void,終止

順帶一提value支援中文,猜想支援的範圍=String.equal,但是一般不會有猿們這麼用吧

@Controller
@RequestMapping(value = "/中文")
public class ZnTestController {

    @RequestMapping(value = "測試")
    public void testController() {
        System.out.println("測試請求");
    }
}

 

想寫下一個內容的過程發現 value值中帶不帶“/”是等價的,下面兩個test會報錯,且用無斜槓與有斜槓的路徑沒有區別

@Controller
@RequestMapping("value")
public class ValueBindController {

    @RequestMapping(value = "/test")
    public void hasSlash() {
        System.out.println("有斜槓");
    }

    @RequestMapping(value = "test")
    public void noneSlash() {
        System.out.println("無斜槓");
    }
}

路徑貌似不支援其他特殊字元,如#value或/#value時無法與localhost:/#value/test進行匹配

......好吧正題

 

value支援使用佔位符對url進行值繫結到引數上,如下列形式,這種即是REST風格的入參形式

@Controller
@RequestMapping("value")
public class ValueBindController {

    @RequestMapping(value = "printValue/{value}")
    public void printValue(@PathVariable String value) {
        // localhost:9000/value/printValue/1
        System.out.println(value);
    }

    @RequestMapping(value = "printPrintValue/{value}")
    public void printPrintValue(@PathVariable("value") String printValue) {
        // localhost:9000/value/printPrintValue/1
        System.out.println(printValue);
    }

    @RequestMapping(value = "printValues/{value1}/{value2}")
    public void printValues(@PathVariable String value1, @PathVariable Integer value2) {
        // localhost:9000/value/printValues/1/2
        System.out.println(value1 + "/" + value2);
    }

    @RequestMapping(value = "printMap/{value1}/{value2}")
    public void printValues(@PathVariable Map<String, String> map) {
        // localhost:9000/value/printValues/1/2
        System.out.println(map.get("value1") + "/" + map.get("value2"));
    }

    @RequestMapping(value = "printList/{list}")
    public void printValues(@PathVariable List<String> list) {
        // localhost:9000/value/printList/1,2,3
        list.stream().forEach(k -> System.out.print(k + " "));
        System.out.println();
    }
}

部落格上有人提到過使用@PathVariable會產生截斷問題,即value/{value},若輸入為1.jpg,value =1。但是測試時發現非常非常的正常,版本問題麼?

 

value支援多路徑,如下,這種形式還可以解決當引數引數非必傳時的問題

@Controller
@RequestMapping(value = "mul")
public class MulValueController {

    @RequestMapping(value = {"one", "two", "three"})
    public void test() {
        // localhost:9000/mul/one
        // localhost:9000/mul/two
        // localhost:9000/mul/three
        System.out.println("多路徑匹配");
    }

    @RequestMapping(value = {"value/{value1}", "value/{value1}/{value2}"})
    public void printValues(@PathVariable String value1, @PathVariable(required = false) String value2) {
        // localhost:9000/value/1
        // localhost:9000/value/1/2
        System.out.println(value1 + "/" + value2);
    }
}

 

value亦支援正則表示式

@Controller
@RequestMapping(value = "regex")
public class RegexController {

    @RequestMapping(value = "tel/{number:^18[0-9]\\d{8}$}")
    public void telPhone(@PathVariable String number) {
        // 匹配以18開頭的手機號碼
        // localhost:9000/regex/tel/18123456890
        System.out.println(number);
    }
}

但是複雜一些的正則會出一些莫名其妙的錯誤,原因不明,比如

^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$

會報下面這個異常。

The number of capturing groups in the pattern segment (^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\d{8}$) does not match the number of URI template variables it defines, which can occur if capturing groups are used in a URI template regex. Use non-capturing groups instead.

 

 

二. method

method用於指定請求型別,如GET,POST

 

路徑相同型別不同的方法可以共存(value =value , method != method) ,

如下GET請求會到getController,POST請求會執行postController,其餘型別的請求會執行allController

    @RequestMapping(method = RequestMethod.GET)
    public String getController() {
        return "Get請求";
    }

    @RequestMapping(method = RequestMethod.POST)
    public String postController() {
        return "Post請求";
    }

    @RequestMapping()
    public String allController() {
        return "任意請求";
    }

method支援多引數,這裡用GET還是用POST都會呼叫這個方法

   @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST})
    public String getPostController() {
        return "GetOrPost請求";
    }