SpringMVC 教程 - Handler Method
原文链接:https://www.codemore.top/cates/Backend/post/2018-04-21/spring-mvc-handler-methods
由注解@RequestMapping
注解修饰的处理请求的函数的签名非常的灵活,可以使用controller函数支持的一系列参数和返回值。
函数参数
下列表格列出了controller方法可以接受的参数,稍后会对其进行详细的解释。 对于 JDK 8的java.util.Optional
可以在包含required
属性的注解中使用,例如:@RequestParam
,@RequestHeader
等,相当于required=false
函数参数 | 解释 |
---|---|
WebRequest ,NativeWebRequest
|
无需直接使用Servlet API来访问请求参数,请求属性和session的属性。 |
javax.servlet.ServletRequest ,javax.servlet.ServletResponse
|
可以是指定的请求和响应类型例如ServletRequest ,HttpServletRequest ,也可以是Spring的MultipartRequest ,MultipartHttpServletRequest
|
HttpSession |
参数永不为null,访问session非线程安全,如果多个请求访问一个session,需要设置RequestMappingHandlerAdapter 的synchronizeOnSession 为true。 |
PushBuilder |
Servlet 4.0 支持HTTP/2 push的API,如果客户端不支持则为null |
Principal |
当前授权用户。 |
HttpMethod |
请求的HTTP方法 |
Locale |
当前请求的区域,由LocaleResolver 解析 |
TimeZone,ZoneId |
由LocaleContextResolver 解析的当前请求的时区 |
InputStream ,Reader
|
访问由Servlet API暴露的请求体 |
OutputStream ,Writer
|
访问由Servlet API 暴露的响应体 |
@PathVairable |
访问URI变量 |
@MatrixVariable |
访问URI中的name-value值。例如 pets/42;q=11 @MatrixVariable int q |
@Requestparam |
访问请求中参数 |
@RequestHeader |
访问请求头 |
@CookieValue |
访问cookie值 |
@RequestBody |
访问请求体,将请求体转换为相应类型 |
HttpEntity<B> |
访问请求头和请求体 |
Map ,Model ,ModelMap
|
访问会在渲染模板时使用的变量。 |
RedirectAttributes |
重定向时使用的属性 |
@ModelAttribute |
访问model中的属性,同时进行数据绑定和校验 |
Errors, BindingResult | 访问在数据绑定和校验是出现的错误。 |
类级别的 @SessionAttributes
,SessionStatus | 在不同的请求中存储session UriComponentsBuilder
| 相对于当前请求的host,port,scheme等 @SessionAttribute
| 访问session中的属性 @RequestAttribute
| 访问请求的属性。 其他类型
| 如果参数非上述类型,那么将当成@RequestParam
来处理
返回值
下列表格列出了支持的返回类型
返回值类型 | 解释 |
---|---|
@ResponseBody |
返回值由HttpMessageConverters 转换,直接写到响应体 |
HttpEntity<B> , ResponseEntity<B>
|
返回值包括,http header和body |
HttpHeaders |
只返回HTTP header |
String |
由ViewResolver 解析出具体的模板渲染。 |
View |
返回具体的视图 |
Map ,Model
|
model包含的属性,视图由RequestToViewNameTranslator 解析 |
@ModelAttribute |
返回添加到Model的属性,视图由RequestToViewNameTranslator 解析. |
ModelAndView |
返回具体视图和添加的model |
void |
返回void,则Spring MVC会认为Controller内部已经处理好响应内容了。 |
DeferredResult<V> |
异步返回结果,可以由任意线程处理 |
Callback<V> |
异步返回,现成由Spring MVC管理 |
ListenableFuture<V> ,CompletionStage<V> ,CompletableFuture<V>
|
同DefferedResult
|
ResponseBodyEmitter ,SseEmitter
|
使用HttpMessageConverter 异步将对象以流的方式发到响应。 |
StreamingResponseBody |
异步将响应发送的输出流 |
Reactor, RxJava, 等Reactive类型 | 同DeferredResult
|
其他类型 | 如果不返回以上类型,默认当作视图名称处理。 |
类型转换
一些需要参数的注解,例如@RequestParam
,@RequestHeader
,@PathVariabl
,@MatrixVariable
和@CookieValue
,如果他么的参数并非String,那么久需要进行类型转换。 类型转换自动由Spring MVC中注册的转换器来进行转换,默认情况下支持,int,long,Date等简单类型。对于不支持的类型可以通过WebDataBinder
或者由FormattingConversionService
注册的Formatter
来进行转换。
Matrix 变量
RFC 3986规定了在路径中添加name-value对。在Spring MVC中,将其定义为matrix变量。 Matrix变量可以出现在任意的路径中,每个变量由分号隔开,多个值由逗号隔开,例如:/cars;color=red,green;year=2012
。多个值同样可也可是通过分离名字来指定,例如:color=red;color=green
。 如果想要在路径中添加Matrix变量,那么就必须保证相应的controller方法包含接收matrix变量,并且请求映射不收到Matrix变量的影响。例如:
// GET /pets/42;q=11;r=22 @GetMapping("/pets/{petId}") public void findPet(@PathVariable String petId, @MatrixVariable int q) { // petId == 42 // q == 11 }
因为所有的路径都有可能包办Matrix变量,可以通过指定路径的形式分辨某个Matrix变量属于哪个路径例如:
@GetMapping("/owners/{ownerId}/pets/{petId}") public void findPet( @MatrixVariable(name="q", pathVar="ownerId") int q1, @MatrixVariable(name="q", pathVar="petId") int q2) { // q1 == 11 // q2 == 22 }
Matrix变量可以是可选的,指定默认值.例如:
// GET /pets/42 @GetMapping("/pets/{petId}") public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) { // q == 1 }
可以使用MultiValueMap
获取所有的Matrix变量,例如:
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23 @GetMapping("/owners/{ownerId}/pets/{petId}") public void findPet( @MatrixVariable MultiValueMap<String, String> matrixVars, @MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) { // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23] // petMatrixVars: ["q" : 22, "s" : 23] }
默认情况下Spring MVC是不启用Matrix变量的,如果是用Java配置,可以通过配置UrlPathHelper
的removeSemicolonContent=false
启用,如果是使用XML配置,可以使用<mvc:annotation-driven enable-matrix=variable="true"/>
启用。
@RequestParam
@RequestParam
可以将Servlet请求参数绑定到controller函数中的变量.例如:
@Controller @RequestMapping("/pets") public class EditPetForm { // ... @GetMapping public String setupForm(@RequestParam("petId") int petId, Model model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; } // ... }
@RequestParam
的变量required
默认情况下是true
,如果不希望必须指定某个参数可以设置required=false
或者如果使用Java 8 可以使用java.util.Optional
。 如果函数的参数非String类型,那么将会进行自动类型转换。 如果@RequsetParam
修饰的是Map<String,String>
或者MultiValueMap<String,String>
那么就会获取所有的请求参数。
@RequestHeader
@RequestHeader
将header的值绑定到controller的方法参数中。 例如一下作为请求header:
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
下列代码就可以获取Accept-Encoding
和Keep-Alive
header
@GetMapping("/demo") public void handle( @RequestHeader("Accept-Encoding") String encoding, @RequestHeader("Keep-Alive") long keepAlive) { //... }
同样,如果参数非String类型,也会自动进行类型转换,如果修饰的是Map<String,String>
,MultiValueMap<String,String>
或者HttpHeaders
,也是获取所有的header值
@CookieValue
使用@CookieValue
将cookie值绑定到controller的方法参数中 例如以下cookie:
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
下列代码即可获取:
@GetMapping("/demo") public void handle(@CookieValue("JSESSIONID") String cookie) { //... }
同样的,如果参数类型非String,会自动进行类型转换。
@ModelAttribute
使用@ModelAttribute
修饰的函数参数可以访问Model中的属性,或者其未初始化是初始化。方法参数名和请求参数名相同,model 属性同样也可以覆盖其请求参数,这样就不需要自己再从请求参数中解析了。例如:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") public String processSubmit(@ModelAttribute Pet pet) { }
Pet 示例的获取:
- 如果Model中存在,则从Model中解析
- 通过
@SessionAttributes
获取 - 从URI的路径变量中获取
- 通过默认的构造函数获取
- 通过和Servlet请求参数相匹配的带参数的构造函数获取。参数名由
@ConstructorProperties
获取或者字节码获取。
当然一般都是使用Model来填充其值的,另一个选择使用URI的路径变量,其值通过注册的Converter<String,T>
转换。下面这个例子就是@ModelAttribute
修饰的值和路径匹配,通过Converter<String,Account>
进行类型转换。
@PutMapping("/accounts/{account}") public String save(@ModelAttribute("account") Account account) { // ... }
获取了属性值的实例后就可以开始进行数据绑定了。WebDataBinder
类通过匹配Servlet 的请求参数名(查询参数和form字段)来将字段名对应到对象中。当类型转换完之后填充匹配的字段。DataBinder
和Validation
将在后面章节详细描述。 数据绑定是会产生错误的,默认情况下会抛出BindException
异常,为了在controller的方法中捕获这个异常,可以在方法参数中加入BindingResult
获取异常。例如:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) { if (result.hasErrors()) { return "petForm"; } // ... }
某些情况下只想要访问属性之而不需要数据绑定。这种情况下可以将设置@ModelAttribute(binding=false)
。例如:
@ModelAttribute public AccountForm setUpForm() { return new AccountForm(); } @ModelAttribute public Account findAccount(@PathVariable String accountId) { return accountRepository.findOne(accountId); } @PostMapping("update") public String update(@Valid AccountUpdateForm form, BindingResult result, @ModelAttribute(binding=false) Account account) { // ... }
添加javax.util.validation.Valid
或者Spring的@Validated
注解,在数据绑定完成后会自动校验。例如:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit") public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) { if (result.hasErrors()) { return "petForm"; } // ... }
@SessionAttributes
@SessionAttributes
用于在不同的请求间存储Servlet session的属性值。主要是列出需要在接下来的请求访问的session的值自动的保存到session中。例如:
@Controller @SessionAttributes("pet") public class EditPetForm { // ... }
第一次请求后这个带有pet
名字的属性值将会自动的存到session中。直到另外一个带有SessionStatus
参数的方法将其清除。例如:
@Controller @SessionAttributes("pet") public class EditPetForm { // ... @PostMapping("/pets/{id}") public String handle(Pet pet, BindingResult errors, SessionStatus status) { if (errors.hasErrors) { // ... } status.setComplete(); // ... } } }
@SessionAttribute
如果想要访问一个之前存在的session的属性,可以使用@SessionAttribute
访问。例如:
@RequestMapping("/") public String handle(@SessionAttribute User user) { // ... }
请他情况下,需要添加或者删除session的时候,可以通过注入org.springframework.web.context.request.WebRequest
或者javax.servlet.http.HttpSession
实现Session的管理。
@RequestAttribute
和@SessionAttribute
相似,@RequestAttribute
可以访问请求之前(例如,Filter
,HandlerInterceptor
)创建的请求属性。例如:
@GetMapping("/") public String handle(@RequestAttribute Client client) { // ... }
重定向属性值
默认情况下,在重定向url中所有的属性值都通过URI的模版变量暴露。 例如:
@PostMapping("/files/{path}") public String upload(...) { // ... return "redirect:files/{path}"; }
Flash属性值
Flash属性值可以保存一个请求的数据使得另一个请求可以使用他的数据。最常用的场景就是重定向,例如:Post/Redirect/Get模式。在重定向之前临时将Flash属性保存(一般保存在session中)。这样在另一个请求中就可以获取保存值,之后就会被立即删除。 Spring MVC 通过FlashMap
和FlashMapManager
支持Flash属性。FlashMap
保存值,FlashMapManager
用来保存,查询,管理FlashMap
实例。 Flash属性默认开启,如果不使用则不会创建HTTP session。对于每个请求来说都有一个input的FlashMap
,包含了上一个请求传递的属性和一个output的FlashMap
包含需要传递的属性。这两个FlashMap都可以通过RequestContextUtils
中的静态方法来获取。 一般来讲controller不会直接使用FlashMap
。其方法参数RedirectAttributes
默认情况下使用flash map存储需要重定向的数据,保存到output的FlashMap
中,重定向后,自动从input的FlashMap中获取数据添加到Model中。
Multipart
启用MultipartResolver
后,如果POST请求包含了multipart/form-data
,则其将会解析请求参数,获取Multipart。下面是上次文件的示例:
@Controller public class FileUploadController { @PostMapping("/form") public String handleFormUpload(@RequestParam("name") String name, @RequestParam("file") MultipartFile file) { if (!file.isEmpty()) { byte[] bytes = file.getBytes(); // store the bytes somewhere return "redirect:uploadSuccess"; } return "redirect:uploadFailure"; } }
如果使用Servlet 3.0 则可以用javax.servlet.http.Part
代替Spring的MultipartFile
。 Multipart 的内容同样可以作为数据绑定的一部分,例如:
class MyForm { private String name; private MultipartFile file; // ... } @Controller public class FileUploadController { @PostMapping("/form") public String handleFormUpload(MyForm form, BindingResult errors) { if (!form.getFile().isEmpty()) { byte[] bytes = form.getFile().getBytes(); // store the bytes somewhere return "redirect:uploadSuccess"; } return "redirect:uploadFailure"; } }
Multipart请求同样可以通过非浏览器提交,例如:下面是一个JSON的示例:
POST /someUrl Content-Type: multipart/mixed --edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp Content-Disposition: form-data; name="meta-data" Content-Type: application/json; charset=UTF-8 Content-Transfer-Encoding: 8bit { "name": "value" } --edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp Content-Disposition: form-data; name="file-data"; filename="file.properties" Content-Type: text/xml Content-Transfer-Encoding: 8bit ... File Data ...
可以通过@RequestParam
来获取元信息,但是更好的做法是使用@RequestPart
来获取其元信息。例如:
@PostMapping("/") public String handle(@RequestPart("meta-data") MetaData metadata, @RequestPart("file-data") MultipartFile file) { // ... }
@RequestPart
可以和javax.validation.Valid
或者Spring的@Validated
注解一同使用,通过标准的bean验证来校验数据的准确性。默认情况下校验错误抛出MethodArgumentNotValidException
的异常,会直接返回404的错误。同样可以通过BindingResult
来自己处理异常情况。
@RequestBody
使用了@RequestBody
的参数通过HttpMessageConverter
来将请求体反序列化成一个对象。下面是使用@RequestBody
的示例:
@PostMapping("/accounts") public void handle(@RequestBody Account account) { // ... }
@RequestBody
同样可以和javax.validation.Valid
或者Spring的@Validated
注解一同使用。默认抛出的异常是MethodArgumentNotValidException
处理方法同@RequestPart
@PostMapping("/accounts") public void handle(@Valid @RequestBody Account account, BindingResult result) { // ... }
HttpEntity
HttpEntity
的使用和@RequestBody
相似,不过他可以同时包含header和body。使用方法如下:
@PostMapping("/accounts") public void handle(HttpEntity<Account> entity) { // ... }
@ReponseBody
在方法中使用@ResponseBody
修饰,则会自动的将返回值通过HttpMessageConverter
的转换写入到响应体中。 使用方法如下:
@GetMapping("/accounts/{id}") @ResponseBody public Account handle() { // ... }
@ResponseBody
同样支持类级别,如果修饰controller类,那么所有的方法都会继承这个注解。这个和@RestController
一样,@RestController
就是@Controller
和@RequestBody
的组合。
ResponseEntity
ResponseEntity
同@ResponseBody
相似,只是其同时包含了响应的header和body。使用如下:
@PostMapping("/something") public ResponseEntity<String> handle() { // ... URI location = ... ; return ResponseEntity.created(location).build(); }
Jackson JSON
Spring MVC 内监了对Jackson序列化视图的支持。在使用ResponseEntity
和@ResponseBody
的时候可以使用@JsonView来启动序列化的视图类。使用如下:
public class UserController { @GetMapping("/user") @JsonView(User.WithoutPasswordView.class) public User getUser() { return new User("eric", "7!jd#h23"); } } public class User { public interface WithoutPasswordView {}; public interface WithPasswordView extends WithoutPasswordView {}; private String username; private String password; public User() { } public User(String username, String password) { this.username = username; this.password = password; } @JsonView(WithoutPasswordView.class) public String getUsername() { return this.username; } @JsonView(WithPasswordView.class) public String getPassword() { return this.password; } }
如果controller的方法返回的是一个字符串的视图,可以将其放到model中启用:
@Controller public class UserController extends AbstractController { @GetMapping("/user") public String getUser(Model model) { model.addAttribute("user", new User("eric", "7!jd#h23")); model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class); return "userView"; } }
Jackson JSONP
为了开启@ResponseBody
和ResonseEntity
的JSONP的支持,可以通过定义一个@ControllerAdvice
的bean继承AbstractJsonpResponseBodyAdvice
,其默认构造参数就是JSONP的查询参数,使用如下:
@ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { public JsonpAdvice() { super("callback"); } }
推荐阅读
-
idea创建springMVC框架和配置小文件的教程图解
-
使用IDEA搭建SSM框架的详细教程(spring + springMVC +MyBatis)
-
android开发教程之Handler分析
-
SpringMVC-Missing URI template variable 'id' for method parameter of type String
-
springmvc:错误Missing URI template variable XX for method parameter of type String
-
SpringMVC 接收AJAX请求及文件上传教程(四)
-
SpringMVC框架详细教程(九)_使用 @RequestParam 将请求参数绑定至方法参数
-
SpringMVC框架详细教程(十)_配置视图解析器
-
SpringMVC 教程 - Handler Method
-
Ruby简明教程之方法(Method)介绍