SpringMVC 教程 – Handler Method

微信扫一扫,分享到朋友圈

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 访问请求头和请求体
Map
, Model
, ModelMap
访问会在渲染模板时使用的变量。
RedirectAttributes 重定向时使用的属性
@ModelAttribute 访问model中的属性,同时进行数据绑定和校验
Errors, BindingResult 访问在数据绑定和校验是出现的错误。

类级别的 @SessionAttributes
,SessionStatus | 在不同的请求中存储session UriComponentsBuilder
| 相对于当前请求的host,port,scheme等 @SessionAttribute
| 访问session中的属性 @RequestAttribute
| 访问请求的属性。 其他类型
| 如果参数非上述类型,那么将当成 @RequestParam
来处理

返回值

下列表格列出了支持的返回类型

返回值类型 解释
@ResponseBody 返回值由 HttpMessageConverters
转换,直接写到响应体
HttpEntity
, ResponseEntity
返回值包括,http header和body
HttpHeaders 只返回HTTP header
String ViewResolver
解析出具体的模板渲染。
View 返回具体的视图
Map
, Model
model包含的属性,视图由 RequestToViewNameTranslator
解析
@ModelAttribute 返回添加到Model的属性,视图由 RequestToViewNameTranslator
解析.
ModelAndView 返回具体视图和添加的model
void 返回void,则Spring MVC会认为Controller内部已经处理好响应内容了。
DeferredResult 异步返回结果,可以由任意线程处理
Callback 异步返回,现成由Spring MVC管理
ListenableFuture
, CompletionStage
, CompletableFuture
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 matrixVars,
        @MatrixVariable(pathVar="petId") MultiValueMap petMatrixVars) {

    // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
    // petMatrixVars: ["q" : 22, "s" : 23]
}

默认情况下Spring MVC是不启用Matrix变量的,如果是用Java配置,可以通过配置 UrlPathHelper
removeSemicolonContent=false
启用,如果是使用XML配置,可以使用
启用。

@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
或者 MultiValueMap
那么就会获取所有的请求参数。

@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
, MultiValueMap
或者 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
转换。下面这个例子就是 @ModelAttribute
修饰的值和路径匹配,通过 Converter
进行类型转换。

@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 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 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");
    }
}

微信扫一扫,分享到朋友圈

SpringMVC 教程 – Handler Method

How do I filter the list by selecting combinations of unique characters in the elements &lp...

上一篇

Giving up on ‘diversity and inclusion’

下一篇

你也可能喜欢

SpringMVC 教程 – Handler Method

长按储存图像,分享给朋友