### 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("女士,您好!"); }