Spring MVC 请求处理方法签名
@RequestMapping 像一个迎宾,将 HTTP 请求正确地迎接到主人的面前(负责处理该请求的方法头主人起身相迎,进而与 HTTP 请求交杯换盏、把酒言欢;最后在欢声笑语中送走 HTTP 请求(返回响应)。Spring MVC 通过分析处理方法的签名,将 HTTP 请求信息绑定到处理方法的相应入参中,然后再调用处理方法得到返回值,最后对返回值进行处理并返回响应。
Spring MVC 对控制器处理方法签名的限制是很宽松的,用户几乎可以按自己喜欢的方式进行方法签名。在必要时对方法及方法入参标注相应的注解(如@PathVariable、@RequestParam、@RequestHeader等)即可,Spring MVC 会优雅地完成剩下的工作:将 HTTP 请求的信息绑定到相应的方法入参中,并根据方法返回值类型做出相应的后续处理。
一般情况下,处理方法的返回值类型为 ModelAndView 或 String,前者包含模型和逻辑视图名,而后者仅代表一个逻辑视图名。下面来看几个典型的方法签名,如下代码所示。
// ①请求参数按名称匹配的方式绑定到方法入参中,方法返回对应的字符串代表逻辑视图名 @RequestMapping(value = "/handle1") public String handle1(@RequestParam("userName") String userName, @RequestParam("password") String password, @RequestParam("realName") String realName) { return "success"; } // ②将Cooke值及报文头属性绑定到入参中、方法返回ModelAndView @RequestMapping(value = "/handle2") public ModelAndView handle2(@CookieValue("JSESSIONID") String sessionId, @RequestHeader("Accept-Language") String accpetLanguage) { ModelAndView mav = new ModelAndView(); mav.setViewName("success"); mav.addObject("user", new User()); return mav; } // ③请求参数按名称匹配的方式绑定到user的属性中、方法返回对应的字符串代表逻辑视图名 @RequestMapping(value = "/handle3") public String handle3(User user) { return "success"; } // ④直接将HTTP请求对象传递给处理方法、方法返回对应的字符串代表逻辑视图名 @RequestMapping(value = "/handle4") public String handle4(HttpServletRequest request) { return "success"; }
从上面代码中可以发现,Spring MVC 会结合方法入参类型、入参的注解、返回值的类型,按照“契约”进行相应的处理。
1.使用 @RequestParam 绑定请求参数值
Java 类反射对象默认不记录方法入参的名称,因此需要在方法入参处使用 @RequestParam 注解指定其对应的请求参数。@RequestParam 注解有以下3个参数。
1)value:参数名。
2)required:是否必需,默认为true,表示请求中必须包含对应的参数名,如果不存在则抛出异常。
3)defaultvalue:默认参数名,在设置该参数时,自动将 required 设为 false。极少情况需要使用该参数,也不推荐使用该参数。
下面的实例将 userName 和 age 请求参数分别绑定到 handlel11() 方法的 userName 和 age 中,并自动完成类型转换。如果不存在 age 请求参数,则将抛出异常。
@RequestMapping(value = "/handle11") public String handle11( @RequestParam(value = "userName", required = false) String userName, @RequestParam("age") int age) { ... }
2.使用 @CookieValue 绑定请求中的 Cookie 值
使用 @CookieValue 可让处理方法入参绑定某个 Cookie 的值,它和 @RequestParam 拥有3个一样的参数。来看一个使用 @CookieValue 的实例,代码如下:
@RequestMapping(value = "/handle12") public String handle12( @CookieValue(value = "sessionId", required = false) String sessionId, @RequestParam("age") int age) { ... }
3.使用 @RequestHeader 绑定请求报文头的属性值
请求报文包含了若干个报文头属性,服务器可据此获知客户端的信息,通过 @RequestHeader 即可将报文头属性值绑定到处理方法的入参中。
@RequestMapping(value = "/handle13") public String handle13(@RequestHeader("Accept-Encoding") String encoding, @RequestHeader("Keep-Alive") long keepAlive) { ... }
4.使用命令/表单对象绑定请求参数值
所谓命令/表单对象并不需要实现任何接口,仅是一个拥有若干属性的POJO。Spring MVC 会按请求参数名和命令/表单对象属性名匹配的方式,自动为该对象填充属性值。支持级联的属性名,如 dept.deptld、dept.address.tel 等。
@RequestMapping(value = "/handle14") public String handle14(User user) { ... }
假设 User 类的结构如下图所示,则如下的 URL 请求,其请求参数将正确地填充到 User 对象中。
/handle14.html?userName=tom&dept.deptId=1&dept.address.tel=102
5.使用Servlet API 对象作为入参
在 Spring MVC 中,控制器类可以不依赖任何 Servlet API 对象,但是 Spring MVC 并不能阻止我们使用 Servlet API 的类作为处理方法的入参。以下处理方法都可以正确地工作,如下面代码所示。
@RequestMapping(value = "/handle21") public void handle21(HttpServletRequest request, HttpServletResponse response) { String userName = request.getParameter("userName"); response.addCookie(new Cookie("userName", userName)); } @RequestMapping(value = "/handle22") public ModelAndView handle22(HttpServletRequest request) { String userName = WebUtils.findParameterValue(request, "userName"); ModelAndView mav = new ModelAndView(); mav.setViewName("success"); mav.addObject("userName", userName); return mav; } @RequestMapping(value = "/handle23") public String handle23(HttpSession session) { session.setAttribute("sessionId", 1234); return "success"; } @RequestMapping(value = "/handle24") public String handle24(HttpServletRequest request, @RequestParam("userName") String userName) { return "success"; }
在使用 Servlet API 的类作为入参时,Spring MVC 会自动将 web 层对应的 Servlet 对象传递给处理方法的入参。处理方法入参可以同时使用 Servlet API 类的入参和其他符合要求的入参,它们之间的位置顺序没有特殊要求。
值得注意的是,如果处理方法自行使用 HttpServletResponse 返回响应,则处理方法的返回值设置成 void 即可,如 handle21 所示。
Spring MVC 在 org.springframework.web.context.request 包中定义了若干个可代理 Servlet 原生 API 类的接口,如 WebRequest 和 NativeWebRequest,它们也允许作为处理类的入参,通过这些代理类可访问请求对象的任何信息,如下:
@RequestMapping(value = "/handle25") public String handle25(WebRequest request) { String userName = request.getParameter("userName"); return "success"; }
6.使用 I/O 对象作为入参
Servlet 的 ServletRequest 拥有 getInputStream() 和 getReader() 方法,可以通过它们读取请求的信息。相应的,Servlet 的 ServletResponse 拥有 getOutputStream() 和 getWriter() 方法,可以通过它们输出响应信息。
Spring MVC 允许控制器的处理方法使用 java.io.InputStream/java.io.Reader 及
java.io.OutputStream/java.io.Writer 作为方法的入参,Spring MVC 将获取 ServletRequest 的 InputStream/Reader 或 ServIetResponse 的 OutputStream/Writer,然后传递给控制器的处理方法,如下代码所示。
@RequestMapping(value = "/handle31") public void handle31(OutputStream os) throws IOException { Resource res = new ClassPathResource("/image.jpg"); FileCopyUtils.copy(res.getInputStream(), os); }
7.其他类型的参数
控制器处理方法的入参除支持以上类型的参数外,还支持 java.util.Locale、java.security.Principal,可以通过Servlet 的 HttpServletRequest 的 getLocale() 和 getUserPrincipal() 方法得到相应的值。如果处理方法的入参类型为 Locale 或 Principal,则 Spring MVC 自动从请求对象中获取相应的对象并传递给处理方法的入参。