首頁 > 軟體

Spring MVC中接收請求引數&&資料轉發

2020-06-16 16:40:05

### 1. 接收請求引數

#### 1.1. 【不推薦】 通過HttpServletRequest獲取請求引數

假設存在:

    <form action="handle_login.do" method="POST">
        <div>請輸入使用者名稱</div>
        <div><input name="username" /></div>
        <div>請輸入密碼</div>
        <div><input name="password" /></div>
        <div><input type="submit" value="登入" /></div>
    </form>

則在控制器中:

@RequestMapping("handle_login.do")
    public String handleLogin() {
        
        // 暫不關心後續的頁面
        return null;
    }

當需要處理請求時,可以在方法的引數中新增`HttpServletRequest`,然後,在方法體中,通過該引數獲取請求引數:

    @RequestMapping("handle_login.do")
    public String handleLogin(
            HttpServletRequest request) {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        
        System.out.println("username=" + username);
        System.out.println("password=" + password);
        
        // 暫不關心後續的頁面
        return null;
    }

注意:Spring MVC框架預設使用的編碼是ISO-8859-1,是不支援中文的,解決方案再議。

#### 1.2. 【推薦】 直接使用同名引數

在Spring MVC中,也可以直接將請求引數宣告為處理請求的方法的引數,例如:

    @RequestMapping("handle_reg.do")
    public String handleReg(
            String username, String password,
            Integer age, String phone,
            String email) {
        System.out.println("username=" + username);
        System.out.println("password=" + password);
        System.out.println("age=" + age);
        System.out.println("phone=" + phone);
        System.out.println("email=" + email);
        
        // 暫不關心後續的頁面
        return null;
    }

使用這種做法時,需要保證請求引數的名稱與方法引數名稱是一致的!如果不一致,則無法獲取到對應的引數,且伺服器端會視為“用戶端並沒有提交名為xxx的引數,則值為null。”

這種做法雖然簡便,但是,不適合處理請求引數過多的請求,如果某個請求中有10個或更多引數,則處理請求的方法也需要新增這麼多引數,是不合適的!

#### 1.3. 【推薦】 通過物件接收請求引數

如果請求引數過多,可以將請求引數封裝在某個型別中:

    public class User {
    
        private String username;
        private String password;
        private Integer age;
        private String phone;
        private String email;
        // SET/GET
    }

然後,在處理請求時,將該型別作為方法的引數即可:

    @RequestMapping("handle_reg.do")
    public String handleReg(User user) {
        System.out.println(user);
        
        // 暫不關心後續的頁面
        return null;
    }

在使用這種做法時,也需要保證名稱的統一!

#### 1.4. 小結

以上3種做法,除了第1種比較麻煩以外,另2種做法,請根據具體情況選擇性的使用,甚至這2種做法可以混合使用,處理請求時,引數不區分先後順序。

當然,第1種做法也不是完全沒有用武之地,在除了控制器以外的元件中依然可能需要使用。

### 2. 轉發資料

#### 2.1. 通過HttpServletRequest物件轉發

Spring MVC處理請求時,預設的返回即表示“轉發”,所以,返回值應該理解為:處理完請求之後轉發到的JSP檔案的名稱。

當需要轉發資料時,直接將資料封閉在`HttpServletRequest`物件中即可:

request.setAttribute("msg", message);

後續,並不需要獲取轉發器執行轉發!在SpringMVC中返回時,就會將資料進行轉發的操作!

#### 2.2. 【不推薦】 使用ModelAndView轉發

在ModelAndView中,model表示的就是轉發的資料,而view表示的就是轉發的目標JSP頁面,在使用時:

    @RequestMapping("handle_login.do")
    public ModelAndView handleLogin(
            String username, String password) {
        String viewName = null;
        String message = null;
        Map<String, Object> model
            = new HashMap<String, Object>();
        if ("root".equals(username)) {
            if ("1234".equals(password)) {
                // ...
            } else {
                viewName = "error";
                message = "[2] 密碼錯誤!";
                model.put("msg", message);
            }
        } else {
            viewName = "error";
            message = "[2] 使用者名稱不存在!";
            model.put("msg", message);
        }
        
        ModelAndView mav 
            = new ModelAndView(viewName, model);
        
        return mav;
    }

#### 2.3. 使用ModelMap

`ModelMap`的使用方式與`HttpServletRequest`幾乎相同:

    @RequestMapping("handle_login.do")
    public String handleLogin(
            String username, String password,
            ModelMap modelMap) {
        String message = null;
        if ("root".equals(username)) {
            if ("1234".equals(password)) {
                // ...
            } else {
                message = "[3] 密碼錯誤!";
                modelMap.addAttribute("msg", message);
                return "error";
            }
        } else {
            message = "[3] 使用者名稱不存在!";
            modelMap.addAttribute("msg", message);
            return "error";
        }
        return null;
    }


### 3. 重定向

在控制器中處理請求時,如果需要重定向,方法的返回值應該是`String`,則值應該是`redirect:目標資源`,關於**目標資源**的表示,可以使用相對路徑,也可以使用絕對路徑,例如使用相對路徑時可以返回`"redirect:index.do"`。

### 4. 關於@RequestMapping

`@RequestMapping`註解的作用主要是設定對映的路徑。

該注解既可以新增在類之前,也可以新增在方法之前!例如:

    @Controller
    @RequestMapping("user")
    public class UserController {
        
        @RequestMapping("reg.do")
        public String showReg() {
            return "reg";
        }

    }

以上設定後,存取時,所使用的URL應該是`http://localhost:8080/PROJECT/user/reg.do`。

在類之前使用該注解,可以簡化每個方法之前的註解,例如,在類之前沒有注解時,可能設定為: 

@RequestMapping("user_list.do")
@RequestMapping("user_info.do")
@RequestMapping("news_list.do")
@RequestMapping("news_info.do")


如果在類和方法之前都加註解,就可以:

@RequestMapping("user")
@RequestMapping("list.do")
@RequestMapping("info.do")

@RequestMapping("news")
@RequestMapping("list.do")
@RequestMapping("info.do")

所以,一般,推薦每個控制器只處理相關資料,例如`UserController`控制器只處理與`User`相關的請求,而`NewsController`控制器只處理與`News`相關的請求,並且,每個類之前都新增`@RequestMapping`註解。

在設定路徑時,並沒有明確要求類的註解和方法的注解中是否使用`/`路徑分隔符,例如以下4種設定是完全等效的:

@RequestMapping("user") @RequestMapping("list.do")
@RequestMapping("/user") @RequestMapping("/list.do")
@RequestMapping("/user") @RequestMapping("list.do")
@RequestMapping("user") @RequestMapping("/list.do")

使用`@RequestMapping`註解還可以限制請求方式,例如:

@RequestMapping(value="路徑", method=RequestMethod.POST)

對於以上對映路徑,如果嘗試進行GET請求,則會出現405錯誤:

HTTP Status 405 - Request method 'GET' not supported

**小結**

`@RequestMapping`主要用於設定請求路徑,在實際應用時,首先,每個控制器類之前都應該新增該注解,用於設定路徑中間層,然後,每個控制器類只處理1種資料相關的請求,每個處理請求的方法之前必須再使用該注解設定具體路徑,可根據實際情況選擇設定該註解的`method`屬性,以限定請求方式。

關於`@RequestMapping`的`value`屬性和`method`屬性的值,都可以是陣列。

從Spring 4.3起,另有`@GetMapping`和`@PostMapping`,等效於限制了請求方式的`@RequestMapping`,即`@GetMapping("路徑") = @RequestMapping(value="路徑", method=RequestMethod.GET)`。使用這些注解時,需要在Spring的組態檔中新增註解驅動`<mvc:annotation-driven />`。

### 5. 關於@RequestParam註解

`@RequestParam`註解是新增在處理請求的方法的引數之前的註解!

使用`@RequestParam`可以解決用戶端提交的引數名與伺服器端處理請求時方法的引數名不一致的問題:

    @PostMapping("handle_login.do")
    public String handleLogin(
            String username,
            @RequestParam("pwd") String password) {
        System.out.println("username=" + username);
        System.out.println("password=" + password);
        return null;
    }


當使用了`@RequestParam`註解後,預設情況下,引數是必須提交的,如果用戶端提交的請求中並不包含該名稱的引數,則會報告400錯誤:

HTTP Status 400 - Required String parameter 'pwd' is not present

如果並不強制要求用戶端提交某引數,可以:

@RequestParam(value="pwd", required=false)

通過該注解,還可以通過`defaultValue`屬性來設定**預設值**,即用戶端沒有提交引數值時,伺服器端視為提交了預設的某個值:

@RequestParam(value="pwd", required=false, defaultValue="111111")

注意:當使用`defaultValue`時,必須顯式的將`required`屬性設定為`false`,否則,如果沒有設定,預設是必須提交引數值的,那麼,預設值就沒有意義了!

**小結**

關於`@RequestParam`註解的應用場景,可以是:

- 用戶端提交的引數名與伺服器端使用的方法的引數名不一致時;
- 要求用戶端必須提交某些引數時;
- 為某些引數設定預設值時。

轉發與重定向的核心區別在於用戶端請求了幾次!在轉發的處理過程中,用戶端其實只發出了1次請求,而在重定向中,用戶端發出了2次請求!

轉發是發生在伺服器內部的!所以,轉發時的URL並不會發生變化!並且,JSP檔案可以存放在WEB-INF目錄下(該目錄是不允許通過http協定存取的)。由於轉發是在伺服器內部完成的,所以,元件之間(控制器與JSP)可以直接傳遞資料。

重定向的本質是第1次請求時,伺服器端可能無法完全全部的處理,所以,伺服器向用戶端響應了重定向(通常響應碼是302),用戶端得到這第1次的響應結果時,由於響應碼表示的是重定向,所以,會再次發生第2次請求,以嘗試得到最終的響應結果。由於用戶端發出了第2次請求,所以,在重定向時,URL是會發生變化的!並且,兩次請求之間的資料預設是無法共用或傳遞的!

如果希望URL發生變化,必須使用重定向!

如果有大量的資料需要傳遞,可以考慮使用轉發!

控制器處理好的資料,不便於在JAVA中編寫如何顯示,則應該轉發給JSP頁面!

列舉

    gender; // 性別

    String gender = "男"; // "女","帥哥"/"美女","先生"/"女士"

    int gender = 1;




    public enum Gender {
        MALE, FEMALE
    }

    if (gender == Gender.MALE) {
        System.out.println("先生,您好!");
    } else {
        System.out.println("女士,您好!");
    }

IT145.com E-mail:sddin#qq.com