取得 URL
中的匹配的内容,适合 REST 的风格。
A @PathVariable
argument can be of any simple type such as int, long, Date, etc. Spring automatically converts to the appropriate type or throws a TypeMismatchException if it fails to do so. You can also register support for parsing additional data types.
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ParameterController {
// {userId} 是 placeholder,里面的内容可以用 @PathVariable 取到
@RequestMapping("/user/{userId}")
@ResponseBody
public String one(@PathVariable Integer userId) {
return userId + "";
}
// 如果变量名和 {} 中的名字不一样,
// 需要用 @PathVariable("nameInPath") 来指定变量要使用路径中的哪一个变量
@RequestMapping("/category/{categoryName}/product/{productId}")
@ResponseBody
public String two(@PathVariable String categoryName,
@PathVariable("productId") Integer pId) {
return "categoryName: " + categoryName + ", productId: " + pId;
}
// 还支持正则表达式的方式
@RequestMapping("/regex/{text:[a-z]+}-{number:\\d+}")
@ResponseBody
public String three(@PathVariable String text, @PathVariable Integer number) {
return "Text: " + text + ", Number: " + number;
}
}
取得 HTTP 请求
中的参数。
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ParameterController {
// 取得名字为 id 的参数的值
@RequestMapping("/user")
@ResponseBody
public String findUser(@RequestParam Integer id) {
return "ID: " + id;
}
// required 默认是 true,参数是必要的,如果没有提供需要的参数,则报错
// required 为 false 表示参数是可选的
@RequestMapping("/product")
@ResponseBody
public String findProduct(@RequestParam(value="productId", required=true) Integer id,
@RequestParam(value="productName", required=false) String name) {
return "ID: " + id + ", Name: " + name;
}
}
把 HTTP 请求
的参数映射到对象,参数名和对象中的属性名匹配的就做映射,不匹配的就不管。
controller.ParameterController
类package controller;
import domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class ParameterController {
// 表单中的参数被映射到对象 user
@RequestMapping("/user")
@ResponseBody
public String findUser(@ModelAttribute User user,
@RequestParam(required=false) Integer age) {
System.out.println("Age: " + age);
return user.toString();
}
}
domain.User
类package domain;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
Forward
:forward:/URI
Redirect
:redirect:/URI
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ParameterController {
@RequestMapping("/forward-test")
public String forward() {
return "forward:/helloworld-springmvc";
}
@RequestMapping("/redirect-test")
public String redirect() {
return "redirect:/helloworld-springmvc";
}
}
表单提交后一般都会 redirect 到另一个页面,防止表单重复提交。RedirectAttributes
的作用就是把处理 PageA 的结果存储起来,当 redirect 到 PageB 的时候显示 PageA 的结果。
Request 中的参数不能被传递给 redirect 的页面,因为 redirect 是从浏览器端发起一个新的请求。
Normally when we generate an http redirect request, the data stored in request is lost making it impossible for next GET request to access some of the useful information from request.
Flash attributes comes handy in such cases. Flash attributes provide a way for one request to store attributes intended for use in another. Flash attributes are saved temporarily (typically in the session) before the redirect to be made available to the request after the redirect and removed immediately.
防止表单重复提交的流程
view/ftl/user-form.htm
<!DOCTYPE html>
<html>
<head>
<title>Update User</title>
</head>
<body>
<form action="/update-user" method="post">
Username: <input type="text" name="username"><br>
Password: <input type="text" name="password"><br>
<button type="submit">Update User</button>
</form>
</body>
</html>
view/ftl/result.htm
Result: ${result}
controller/ParameterController.java
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
public class ParameterController {
// 显示表单
@RequestMapping("/user-form")
public String showUserForm() {
return "user-form.htm";
}
// 更新 User,把结果保存到 RedirectAttributes
@RequestMapping("/update-user")
public String updateUser(@RequestParam String username,
@RequestParam String password,
final RedirectAttributes redirectAttributes) {
// Update user in database...
System.out.println("Username: " + username + ", Password: " + password);
// 操作结果显示给用户
redirectAttributes.addFlashAttribute("result", "The user is already successfully updated");
return "redirect:/result";
}
// 显示表单处理结果
@RequestMapping("/result")
public String result() {
return "result.htm";
}
}
访问 http://localhost/user-form,填写信息,提交表单,处理好后页面被 redirect 到 http://localhost/result 显示操作结果。
@ModelAttribute
@RequestMapping("flash")
public String flash(RedirectAttributes redirectAttributes) {
redirectAttributes.addFlashAttribute("username", "Biao");
return "redirect:flash2";
}
@RequestMapping("flash2")
@ResponseBody
public String flash2(@ModelAttribute("username") String username) {
return "username: " + username;
}
想取得 HttpServletRequest 和 HttpServletResponse 很容易,只要在方法的参数里定义后,SpringMVC 会自动的注入 它们。
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Controller
public class ParameterController {
@RequestMapping("/request-response")
@ResponseBody
public String foo(HttpServletRequest request, HttpServletResponse response) {
System.out.println(request);
return "WoW";
}
}
The @RequestHeader
annotation allows a method parameter to be bound to a request header. Here is a sample request header:
Header 直接写到 HttpServletResponse
。
Header | Value |
---|---|
Host | localhost:8080 |
Accept | text/html,application/xhtml+xml,application/xml;q=0.9 |
Accept-Language | fr,en-gb;q=0.7,en;q=0.3 |
Accept-Encoding | gzip,deflate |
Accept-Charset | ISO-8859-1,utf-8;q=0.7,*;q=0.7 |
Keep-Alive | 300 |
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
@Controller
public class ParameterController {
@RequestMapping("/read-header")
@ResponseBody
public String readHeader(@RequestHeader("Host") String host,
@RequestHeader(value="Accept-Encoding", required=false) String encoding,
@RequestHeader(value="Accept-Charset", required=false) String charset) {
return "Host: " + host + ", Encoding: " + encoding + ", Charset: " + charset;
}
@RequestMapping("/read-header-error")
@ResponseBody
public String readHeaderError(@RequestHeader("Host") String host,
@RequestHeader("Accept-Charset") String charset) {
return "Host: " + host + ", Charset: " + charset;
}
@RequestMapping("/write-header")
@ResponseBody
public String writeHeader(HttpServletResponse response) {
response.setHeader("token", "D4BFCEC2-89E6-40CB-AF9A-B5513CB30FED");
return "Header is wrote.";
}
}
访问 http://localhost/read-header
访问 http://localhost/read-header-error 提示错误,因为 charset 是 required=true的,在 header 信息里没有 charset,所以报错。但是这个错误如果不了解的话会一头雾水,因为在控制台中没有输出错误的原因
这个错误是SpringMVC报出来的,见到它意味着html/jsp页面的控件名称和 controller 里函数的参数不符。 要看到错误原因,需要把 Spring 的日志调到 Debug 级别。
The @CookieValue
annotation allows a method parameter to be bound to the value of an HTTP cookie.
Cookie 直接写到 HttpServletResponse
。
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
@Controller
public class ParameterController {
@RequestMapping("/read-cookie")
@ResponseBody
public String readCookie(@CookieValue("username") String cookie) {
return "Cookie for username: " + cookie;
}
@RequestMapping("/write-cookie")
@ResponseBody
public String writeCookie(HttpServletResponse response) {
Cookie cookie = new Cookie("username", "Don't tell you");
cookie.setMaxAge(1000);
response.addCookie(cookie); // Put cookie in response.
return "Cookie is wrote.";
}
}
@CookieValue(value="username", defaultValue="") String cookie
可以使用 HttpSession
来直接操作 session,同时 SpringMVC 也提供了操作 session 的注解 @SessionAttributes
。
HttpSession 的方式什么时候都生效,但是 @SessionAttributes 有时候就不行,如返回 AJAX 请求时设置的 session 无效。所以推荐使用注入 HttpSession 来读写 session。
@SessionAttributes
annotation indicates that in the controller’s methods can be assigned some values to arguments of the annotation. In this example I declared just one session attribute with the name “thought“. That’s mean I can put some object into modelAndView using addObject() method, and it will be added to the session if the name of the object will be the same as the name of argument in @SessionAttributes.
package controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpSession;
@Controller
@SessionAttributes({"result"})
public class SessionController {
@RequestMapping("/write-session")
@ResponseBody
public String writeSession(HttpSession session) {
session.setAttribute("username", "座山雕");
session.setAttribute("password", "天王盖地虎");
return "Session wrote...";
}
@RequestMapping("/read-session")
public String readSession() {
return "helloworld-freemarker.htm";
}
@RequestMapping("/write-session2")
public ModelAndView writeSession2() {
ModelAndView mav = new ModelAndView();
mav.addObject("result", "Save session");
mav.setViewName("user-form.htm");
return mav;
}
@RequestMapping("/read-session2")
public String readSession2() {
return "result.htm";
}
}
使用 JSR-303 Validation
进行验证,比 SpringMVC 自己提供的验证方式简单很多。
当我们在SpringMVC 中需要使用到 JSR-303 的时候就需要我们提供一个对 JSR-303 规范的实现,Hibernate Validator
是实现了这一规范的,这里我将以它作为 JSR-303 的实现来讲解 SpringMVC 对 JSR-303 的支持。
参考 http://haohaoxuexi.iteye.com/blog/1812584
Annocation | Description |
---|---|
@Null | 限制只能为null |
@NotNull | 限制必须不为null |
@AssertFalse | 限制必须为false |
@AssertTrue | 限制必须为true |
@DecimalMax(value) | 限制必须为一个不大于指定值的数字 |
@DecimalMin(value) | 限制必须为一个不小于指定值的数字 |
@Digits(integer,fraction) | 限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction |
@Future | 限制必须是一个将来的日期 |
@Max(value) | 限制必须为一个不大于指定值的数字 |
@Min(value) | 限制必须为一个不小于指定值的数字 |
@Past | 限制必须是一个过去的日期 |
@Pattern(value) | 限制必须符合指定的正则表达式 |
@Size(max,min) | 限制字符长度必须在min到max之间 |
添加依赖
<!-- For Validate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency>
controller.ValidateController
package controller;
import domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.Valid;
@Controller
public class ValidateController {
@RequestMapping("/validate-user")
@ResponseBody
public String validateUser(@ModelAttribute @Valid User user, BindingResult result) {
System.out.println(result.hasErrors());
if (result.hasErrors()) {
return result.getFieldErrors().toString();
}
return "名字: " + user.getUsername() + ", Password: " + user.getPassword();
}
}
domain.User
package domain;
import org.hibernate.validator.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public class User {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@NotBlank(message="用户名不能为空") // 进行参数验证
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@NotNull(message="密码不能为null") // 进行参数验证
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
spring-mvc.xml
</mvc:annotation-driven>