Sping MVC 框架学习二:Spring MVC 中 @RequestMapping、@PathVariable、@RequestParam 等常用注解用法
@RequestMapping 注解用法
在POJO类定义处标注 @Controller,再通过 <context:component-scan/> 扫描相应的类包,即可使POJO成为一个能处理 HTTP 请求的控制器。
你可以创建数量不限的控制器,分别处理不同的业务逻辑,如 LoginController、UserController、ForumController等。每个控制器可拥有多个处理请求的方法,每个方法负责不同的请求操作。如何将请求映射到对应的控制器方法中是 Spring MVC 框架最重要的任务之一,这项任务就是由 @RequestMapping 来承担的。DispatchServlet 截获请求后,就通过控制器上 @RequestMapping 提供的映射信息确定请求所对应的处理方法。
在控制器的 类定义处 及 方法定义处 都可标注 @RequestMapping,它们分别代表:
1. 类定义处:提供初步的请求映射信息。相对于 WEB 应用的根目录。
2. 方法处:提供进一步的细分映射信息。相对于类定义处的 URL。若类定义处未标注 @RequestMapping,则方法标记处的 URL 相对于 WEB 应用的根目录。
1 @Controller 2 public class HelloWorld 3 { 4 private static final String SUCCESS = "success"; 5 6 @RequestMapping("/helloworld") 7 public String hello() 8 { 9 ... ... 10 }
1 1 <!-- 类定义处未添加映射信息时,只需匹配方法处的映射信息 --> 2 2 <a href="helloworld">Hello World</a>
1 @RequestMapping("/springmvc") 2 @Controller 3 public class HelloWorld 4 { 5 private static final String SUCCESS = "success"; 6 7 @RequestMapping("/helloworld") 8 public String hello() 9 { 10 ... ...
1 <!-- 类定义处添加了映射信息时,需把类定义处与方法定义处的映射信息都写上 --> 2 <a href="springmvc/helloworld">Hello World</a>
@RequestMapping 除了可以使用请求 URL 映射之外,还可以使用请求方法、请求参数及请求头映射请求。
@RequestMapping 的 value、method、params 及 heads 属性分别表示请求 URL、请求方法、请求参数及请求头的映射条件,它们之间是与的关系,联合使用多个条件可以让请求映射更加精确化。
1. method属性
1 @RequestMapping("/springmvc") 2 @Controller 3 public class SpringTest 4 { 5 private static final String SUCCESS = "success"; 6 7 //使用 method 属性来指定请求方式 8 @RequestMapping(value="/testMethod", method=RequestMethod.POST) 9 public String testMethod() 10 { 11 System.out.println("Test Method"); 12 return SUCCESS; 13 } 14 }
<!-- 编写一个index.jsp请求页面,以post方式提交一个表单 -->
<form action="springmvc/testMethod" method="post"> <input type="submit" value="submit"/> </form>
编写一个 success.jsp 跳转结果页面
1 <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 pageEncoding="ISO-8859-1"%> 3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 4 <html> 5 <head> 6 <title>Insert title here</title> 7 </head> 8 <body> 9 <h4>Success Page</h4> 10 </body> 11 </html>
点击 “submit” 按钮可以看到 成功跳转至 success.jsp 页面,同时控制台打印出 Test Method。
注:若此时请求为 GET 请求,则程序会报错。
2. params 和 headers 属性
它们支持简单的表达式
1. param1:表示请求必须包含名为 param1 的请求参数
2. !param1:表示请求不能包含名为 param1 的请求参数
3. param1 != value1:表示请求包含名为 param1 的请求参数,但其值不能为 value1
4. {"param1=value1", "param2"}:请求必须包含名为 param1 和 param2 的两个请求参数,且param1 参数的值必须为 value1
1 @RequestMapping("/springmvc") 2 @Controller 3 public class SpringTest 4 { 5 private static final String SUCCESS = "success"; 6 //测试params属性 7 @RequestMapping(value="/testParamAndHeader", params={"username", "age!=10"}) 8 public String testParamAndHeader() 9 { 10 System.out.println("Test ParamAndHeader"); 11 return SUCCESS; 12 } 13 }
<!-- 当age值不为10时,程序能正常执行,若将age值改为10,则程序将不能正常执行 -->
<a href="springmvc/testParamAndHeader?username=a&age=1">Test ParamAndHeader</a>
3. heads属性
1 //此时的 headers 属性能使程序正常执行,若将属性值改为 Accept-Language:en-US,zh;q=0.8 ,程序将报错。请求jsp使用上面的jsp请求即可 2 @RequestMapping(value="/testParamAndHeader", params={"username", "age!=10"}, headers={"Accept-Language:zh-CN,zh;q=0.8"}) 3 public String testParamAndHeader() 4 { 5 System.out.println("Test ParamAndHeader"); 6 return SUCCESS; 7 }
@RequestMapping 不但支持标准的URL,还支持 Ant 风格的 URL
Ant 风格资源地址支持3种匹配符:
1. ?:匹配文件名中的一个字符
2. *:匹配文件名中的任意字符
3. **:匹配多层路径
@RequestMapping 支持 Ant 风格的URL:
1. /user/*/createUser:匹配如 /user/aaa/createUser、/user/bbb/createUser 等 URL
2. /user/**/createUser:匹配如 /user/createUser、/user/aaa/bbb/createUser 等 URL
3. /user/createUser??:匹配如 /user/createUseraa、/user/createUserbb 等 URL
在前面的SpringTest类中添加如下方法,在index.jsp页面增加 Test AntPath 超链接
1 @RequestMapping("/testAntPath/*/abc") 2 public String testAntPath() 3 { 4 System.out.println("Test AntPath"); 5 return SUCCESS; 6 }
<a href="springmvc/testAntPath/xyz/abc">Test AntPath</a>
启动服务器,程序可以正常运行,将超链接中xyz改为其他任何字符,程序也是可以运行的
@PathVariable 注解用法
@RequestMapping 还支持带占位符的 URL,该功能在 Spring MVC 向 REST 目标挺进的发展过程中具有里程碑的意义。
通过 @PathVariable 可以将 URL中的占位符绑定到控制器处理方法的入参中
在SpringTest中新增一个方法,相应index.jsp页面增加一个超链接
1 //可以映射URL中的占位符到目标方法的参数中 2 @RequestMapping("/testPathVariable/{id}") 3 public String testPathVariable(@PathVariable("id") Integer id) 4 { 5 System.out.println("testPathVariable:" + id); 6 return SUCCESS; 7 }
<a href="springmvc/testPathVariable/1">Test PathVariable</a>
类定义处的 @RequestMapping 的 URL 如果使用占位符的参数,也可以绑定到处理方法的入参中
1 @RequestMapping("/springmvc/{Msg}") 2 @Controller 3 public class SpringTest 4 { 5 private static final String SUCCESS = "success"; 6 7 @RequestMapping("/testPathVariable/{id}") 8 public String testPathVariable(@PathVariable String Msg, @PathVariable Integer id) 9 { 10 System.out.println("testPathVariable:" + Msg + ", " + id); 11 return SUCCESS; 12 }
<a href="springmvc/Msg/testPathVariable/1">Test PathVariable</a>
由结果可以看 URL中的占位符已经绑定到控制器处理方法的入参中了
注:为避免传递参数时可能出现的空指针异常,@PathVariable 参数接收类型最好使用对象类型(原声类型使用其包装类型)
@RequestParam 注解用法
Spring MVC 通过分析处理方法的签名,将HTTP请求信息绑定到处理方法的相应的入参中,并根据方法的返回值类型做出相应的后续出理。
前面我们讲过的 @PathVariable 是一个 REST 风格的注解,它通过占位符放入方式携带一个参数,但这个参数不是正真意义上的请求参数。
在处理方法入参处使用 @RequestParam 可以把请求参数传递给请求方法,它有三个参数
1. value:参数名
2. required:是否必须,默认为true,表示请求中必须包含对应的参数名,如果不存在将抛出异常
3. defaultValue:默认参数,表示如果请求中没有同名参数时的默认值。设置该参数时,自动将 required 设置为 false。不推荐使用
SpringTest类新增如下方法,此时请求参数的键值必须与@RequestParam中value参数相同
1 @RequestMapping(value="/testRequestParam") 2 public String testRequestParam(@RequestParam(value="username")String name, 3 @RequestParam("age")Integer age) 4 { 5 System.out.println("testRequestParam:" + name + ", " + age); 6 return SUCCESS; 7 }
index.jsp新增超链接
<a href="springmvc/testRequestParam?username=tom&age=10">Test RequestParam</a>
启动服务器,程序正常运行。此时如果我们将超链接改为
<a href="springmvc/testRequestParam?username=tom">Test RequestParam</a>
程序执行时报错,解决方法在 @RequestParam 添加属性 required=false,或者还可以使用 defaultValue 为参数赋默认值
@RequestMapping(value="/testRequestParam") public String testRequestParam(@RequestParam(value="username")String name, @RequestParam(value="age", required=false, defaultValue="0")Integer age) { System.out.println("testRequestParam:" + name + ", " + age); return SUCCESS; }
需要注意:
@RequestParam 参数类型最好都写为对象类型,不然可能会发生空指针异常。 如将上面代码中的 Integer 改为 int 类型,当传入的 age 参数为空时,在方法处的 age 值会被赋值为 null,但是 int 类型的变量不能被赋值为 null,会发生空指针异常,所以最好写为对象类型或包装类型。
如果实在没办法必须写为原生数据类型,那么就需要使用 required 和 defaultValue 来避免异常产生,比如 设置 required=false;设置一个默认值 defaultValue=默认值。
@RequestHeader
在请求头中包含了若干属性,服务器可根据此获取和客户端的信息,通过 @RequestHeader 即可将请求头中的属性值绑定到处理方法的入参中
@RequestMapping("/testRequestHeader") public String testRequestHeader(@RequestHeader(value="Accept-language")String lag) { System.out.println("testRequestHeader: " + lag); return SUCCESS; }
<a href="springmvc/testRequestHeader">Test RequestHeader</a>
@CookieValue
Spring MVC 还提供了绑定 Cookie 值的注解 @CookieValue ,它可以让方法入参绑定某个 Cookie 的值,它和 @RequestParam 拥有3个一样的参数
@RequestMapping("/testCookieValue") public String testCookieValue(@CookieValue(value="JSESSIONID")String sessionId) { System.out.println("testCookieValue: " + sessionId); return SUCCESS; }
使用 POJO 对象绑定请求参数
当我们要映射一个表单中的多个项目时,可以使用将表单中的内容放入一个 POJO 对象中,当绑定表单到处理方法入参时就不必一个一个的去写实现,而只需将 POJO 对象绑定到入参,可以提高效率。Spring MVC 会按照请求参数名和 POJO 属性名进行自动装配,自动为该对象填充属性值,并且支持级联属性。
新增两个 POJO 对象
package com.bupt.springmvc.entity; public class User { private String username; private Address address;
//生成 get 和 setter 方法,重写 toString 方法
}
package com.bupt.springmvc.entity; public class Address { private String city;
//生成 get 和 setter方法,重写 toString 方法
}
SpringTest类新增处理方法
@RequestMapping("/testPojo") public String testPojo(User user) { System.out.println("testPojo: " + user); return SUCCESS; }
index.jsp新增表单
<form action="springmvc/testPojo" method="post">
username: <input type="text" name="username"/><br>
city: <input type="text" name="address.city"/><br>
<input type="submit" value="submit"/>
</form>
使用 Servlet API 作为入参
在Spring MVC 中,控制器类可以不依赖任何 Servlet API 对象,但是 Spring MC 并不阻止我们使用 Servlet API 的类作为处理方法的入参。
Spring MVC 的 Handler 方法接受的 Servlet API 包括:HttpServletRequest、HttpServletResponse、HttpSession、java.security.Principal、Locale、InputStream、OutputStream、Reader、Writer。
继续添加处理方法和超链接
1 @RequestMapping("/testServletAPI") 2 public void testServletAPI(HttpServletRequest request, HttpServletResponse response, Writer out) throws IOException 3 { 4 System.out.println("testServlet: " + request + ", " + response); 5 out.write("Hello"); 6 }
REST 风格的请求
REST :即 Representational State Transfer,一般翻译为(资源)表现层状态转化。
表现层:把资源具体呈现出来的形式,叫做它的表现层。比如,文本可以使用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式。
状态转化:没法送一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,就是一个无状态协议,即所有的状态都保存在在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”。而这种转化是建立在表现层之上的,所以就是“表现层状态转化”。具体说,就是 HTTP 协议里,四个表示操作的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。
浏览器表单只支持 GET 与 POST 请求,而 DELETE、PUT 等并不支持。所以Spring 3.0 提供了一个 HiddenHttpMethodFilter,允许通过 “_method” 的表单参数指定这些特殊的HTTP方法(实际上还是通过POST提交表单)。服务器端配置了 HiddenHttpMethodFilter 后,Spring 会根据 _method 参数指定的值模拟出相应的 HTTP 方法,这样就可以使用这些HTTP 方法对处理方法进行映射了。
示例:
- /order/1 HTTP GET:得到 id = 1 的 order
- /order/1 HTTP DELETE:删除 id = 1 的 order
- /order/1 HTTP PUT:更新 id = 1 的 order
- /order HTTP POST:新增 id = 1 的 order
在SpringTest里新增如下方法
1 /* 2 * 如何发出 PUT 请求和 DELETE 请求呢 3 * 1. 配置 HiddenHttpMethodFilter 4 * 2. 发出POST请求 5 * 3. 发出POST请求时携带一个name="_method"的隐藏域,值为 DELETE 或 PUT 6 */ 7 @RequestMapping(value="/testRest/{id}", method=RequestMethod.GET) 8 public String testRestGet(@PathVariable Integer id) 9 { 10 System.out.println("testRest GET:" + id); 11 return SUCCESS; 12 } 13 14 @RequestMapping(value="/testRest", method=RequestMethod.POST) 15 public String testRestPost() 16 { 17 System.out.println("testRest POST"); 18 return SUCCESS; 19 } 20 21 @RequestMapping(value="/testRest/{id}", method=RequestMethod.DELETE) 22 public String testRestDelete(@PathVariable Integer id) 23 { 24 System.out.println("testRest DELETE:" + id); 25 return SUCCESS; 26 } 27 28 @RequestMapping(value="/testRest/{id}", method=RequestMethod.PUT) 29 public String testRestPut(@PathVariable Integer id) 30 { 31 System.out.println("testRest PUT:" + id); 32 return SUCCESS; 33 }
index.jsp中新增如下超链接
1 <form action="springmvc/testRest/1" method="post"> 2 <input type="hidden" name="_method" value="PUT"> 3 <input type="submit" value="Test REST PUT"> 4 </form> 5 <br><br> 6 <form action="springmvc/testRest/1" method="post"> 7 <input type="hidden" name="_method" value="DELETE"> 8 <input type="submit" value="Test REST DELETE"> 9 </form> 10 <br><br> 11 <form action="springmvc/testRest" method="post"> 12 <input type="submit" value="Test REST POST"> 13 </form> 14 <br><br> 15 <a href="springmvc/testRest/1">Test REST GET</a>
启动服务器,运行程序,如果你运行环境是在 tomcat 8 下,当运行到 DELETE 和 PUT 请求时,程序报错
出现的原因,根据 stackoverflow 上的解释说是因为,JSP 2.3 要求只能响应 GET、HEAD 和 POST,对于其他的HTTP方法不支持。Tomcat选择不支持PUT、DELETE方法来防止出现篡改HTTP方法的攻击。
直接从网上找了三种解决方法:
1. 将Tomcat 8 改为 Tomcat 7,在 Tomcat 7 环境下运行是正常的
2. 将请求转发(forward) 改为请求重定向 (redirect)
3. 自己手动写一个 Filter 来包装 HttpRequest 中的 getMethod()方法
下面介绍第三种做法,也就是自己写一个 Filter 来包装从服务器发送过来的 HttpRequest 请求
大致流程:
1. 客户端向服务器端发送请求,若发送的是 POST 请求且带有以 _method 为名的参数会被 Spring 的 HanddenHttpMethodFilter给拦截
2. HiddenHttpMethodFilter 内有一个静态内部类通过继承 HttpServletRequestWrapper 类重写 getMethod()方法,将该方法返回值设为 _method 隐藏域的值。
3. HiddenHttpMethodFilter 在包装好 Request 后,将请求发往服务器中对应的方法处理器,这时请求变成了3中的 WrapperRequest by SpringFilter
4. 服务器处理完请求后,产生一个 forward 请求,产生相应的请求信息发往客户端,注意这时的 request 的 getMethod() 方法仍然是 HiddenHttpMethodFilter 包装过的
5. 我们需要在请求到达客户端前拦截它,通过自定义的滤波器 MyHttpMethodFilter进一步包装请求,将getMethod() 方法返回值改成 POST 或 GET 即可。
6. 在 web.xml 中配置该filter 注意 dispatcher节点值必须为FORWARD
<filter-mapping> <filter-name>myFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>FORWARD</dispatcher> </filter-mapping>
<dispatcher>元素的作用:
这个元素有四个可能值:REQUEST、FORWARD、INCLUDE 和 ERROR,可以在一个 <filter-mapping> 元素中加入任意数目的 <dispatcher>,使得filter将会作用于直接从客户端过来的request,通过forward过来的request,通过include过来的request和通过 <error-page> 过来的request。若没有指定任何 <dispatcher>元素,默认值是REQUEST。
posted on 2016-07-03 15:28 Traveling_Light_CC 阅读(808) 评论(0) 编辑 收藏 举报