b 4.1 Spring MVC Filters过滤器详解 & request mapping注解 & Handler Methods &databinder
文章目录
- 1.2 Filters
- 1.3 注解Controllers
- 1.3.1 声明Declaration
- 1.3.2 Request Mapping 重要
- URI patterns
- Pattern Comparison 模式比价
- Suffix Macth 后缀匹配。
- Suffix Match and RFD
- Consumable Media Types(Content-Type条件)
- Producible Media Types (Accept条件)
- Parameters, headers (按参数条件匹配)
- HTTP HEAD, OPTIONS
- Custom Annotations 自定义注解
- Explicit Registrations 明确的注册
- 1.3.3 Handler Methods
- Method Arguments 方法参数
- Return Values 支持的返回值
- Type Conversion 参数代表注解
- Matrix Variables
- @RequestParam
- @RequestHeader
- @CookieValue
- @ModelAttribute
- @SessionAttributes
- @SessionAttribute
- @RequestAttribute
- Redirect Attributes 重定向是属性值得传递
- Flash Attributes
- Multipart 多片传输
- @RequestBody
- HttpEntity
- @ResponseBody
- ResponseEntity
- 重要关于Jackson JSON
- 1.3.4 Model
- 1.3.5 DataBinder重要
- 1.3.6 Exceptions 异常处理
- 1.3.7. Controller Advice
1.2 Filters
1.2.1 Form Data
浏览器可以提交form data,通过HTTP GET或者HTTP POST。但是非浏览器端也会用HTTP PUT, PATCH, and DELETE。Servlet API要求ServletRequest.getParameter*()
方法支持只允许从POST获取参数。spring-web
模块提供FormContentFilter
去拦截HTTP PUT, PATCH, and DELETE requests ,谁拥有application/x-www-form-urlencoded,从请求body里读取form data,并且包装ServletRequest去让ServletRequest.getParameter*()可以获得到。
1.2.2 Forwarded Headers
作为一个请求通过代理后,host、port和scheme也许会变,这让link能准确的链接到正确的Host、port和scheme上是一个挑战。RFC 7239
规范,定义了Forwarded HTTP header, 让代理proxy可以用来提供关于原始请求的信息。也有非标准的header,包含X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Proto, X-Forwarded-Ssl, and X-Forwarded-Prefix
。ForwardedHeaderFilter
是一个Servlet filter可以修改requestde 的host,port,scheme基于Forwarder。这个filter依赖包装request,因此他必须在其他filter前面,例如RequestContextFilter
(必须是用被修改后的request,而不是原来的)
由于一个应用不知道headers是否被代理,或者被恶意client添加或修改过。出于对forwarded的安全考虑,proxy应该配置成移除不被信任Forwarded headers。你也可以配置ForwardedHeaderFilter
为 removeOnly=true
,来移除并不使用这个headers。
异步请求
为了支持异步永清和error跳转,这个filter应该被配置DispatcherType.ASYNC
和DispatcherType.ERROR
。如果使用Spring框架的AbstractAnnotationConfigDispatcherServletInitializer
,所有的filter会被注册成拥有对应的dispatch types。
如果使用了web.xml
进行配置,或者spring boot通过FilterRegistrationBean
来包含DispatcherType.ASYNC
DispatcherType.ERROR
需要额外添加DispatcherType.REQUEST
。
1.2.3 Shallow ETag
ShallowEtagHeaderFilter
会创建一个shallow
ETAG来缓存被写入response的content和计算一个MD5的hash。下次clents同样发送时,它会比较If-None-Match
,相同就返回一个304(NOT_MODIFIED)的标记。
这种策略节省网络带宽,但不节省cpu,当全response必须被计算和比较。其他策略在conroller级别,避免计算。
filter拥有writeWeakETag
参数来配置filter去写弱ETags,如W/"02a2d595e6ed9a0b24f027f2b63b134d6
在RFC 7232的规范里。
为了去支持asynchronous requests
,filter必须被配置成DispatcherType.ASYNC
以便于在最后一步分发结束的时候生成ETag。
1.2.4 CORS配置
1.3 注解Controllers
Spring MVC支持注解驱动的@Controller
和@RestCOntroller
一个简单的示例
1.3.1 声明Declaration
@Controller是一个原注解,允许在WebApplicationContext
中自动侦测。当然@Component也行。去开启@Controller你可以注册扫描
xml版
@RestController是一个复合注解,@Controller和@ResponseBody去指明Controller的每一个方法都继承了@ResponseBody的注解。
AOP代理
某些情况下,需要声明controllerr和AOP代理。如果你有@Transactional
注解在@Controller上。这种情况下,我们推荐使用class-based的代理。这个会被controllers默认选中。然而如果controller必须实现一个接口不是一个Spring Context callback(例如InitializingBean
,*Aware
),你需要明确的配置 class-based的代理,如<tx:annotation-driven/>改成<tx:annotation-driven proxy-target-class=“true”/>。你可以用这个注解来打开@EnableTransactionManagement(proxyTargetClass = true)
1.3.2 Request Mapping 重要
你可以使用@RequestMapping注解去map requests到controllers。
controller类上需要使用@RequestMapping来指明基本的URL
方法上通过使用@RequestMapping,集体到具体方法
URI patterns
- ? 匹配一个
- * 在一个path分隔符里匹配一个或多个字符
- ** 匹配一个或多个path分隔符
@PathVariable
方法级别和类级别都可以有
URI 变量会被自动转成合适的类型,转不到会抛出TypeMismatchException
的错误。简单类型(int、long、Date等),也支持你定义的类型,请看后面呢DataBinder
也可以用@PathVariable("customId")
来明确指出具体哪个章节。
正则表达式解析
{varName:regex}来解析某个字段,
例如spring-web-3.0.5.jar
会被如下的表达式解析成:spring-web,3.0.5,jar。
Pattern Comparison 模式比价
当多个模式同时命中的时候,必须找到最佳的命中。通过使用AntPathMatcher.getPatternComparator(String path)
找具体的pattern。
首先最少拥有一个URI variables(加1分),一个不确定的参数(加1分),两个不确定加两分。相同分数下最长的pattern会被命中。同样的分数和长度下,拥有最多URI variables的会被命中。
默认的mapping pattern是/**
是不计分的,总是排在最后兜底。同样,/public/**
也是被考虑是排在其他不拥有两个不确定符号之后的。
完整的细节请看Path Matching。
Suffix Macth 后缀匹配。
Spring MVC 默认会加.*
在后缀,以便于匹配到/person
的时候也匹配到/person.*
。文件扩展名,可以从请求的headers,Accept字段来获取,例如/person.pdf
、/person.xml
。
使用文件扩展后缀在浏览器级别是必要的,当浏览器用Accept
来标识需要的文件。目前没有比用Accept
最好的选择。
文件后缀也有麻烦,有时候会造成二义性。当时用URI variables认证鉴权也会很麻烦。
可以去关闭
Suffix Match and RFD
A reflected file download(rfd),反射文件攻击和XSS是详细的,query parameter和URI的variable可以影响response。然而,插入JavaScript 到HTML, RFD攻击依赖浏览器去切换下载和对response作为一个二次的触发。
在Spring MVC中,@ResponseBody和ResponseEntity方法是有风险的,以为可以渲染不同的content types–客户端可以通过URL的路径扩展来请求。禁止suffix pattern
和使用path扩展名可以降低风险。但是这不能阻止RFD攻击。
为了阻止RFD攻击,MVC添加Content-Disposition:inline;filename=f.txt
head,去建议有个修改和安全的下载文件。之歌只在URLpath包含文件扩展名的前提下。然而,当URLs被直接写在Brower的浏览器时,会潜在的低的影响。
多数的公用文件扩展有白盒列表。应用可以自定义HttpMessageConverter
实现,来明确的注册文件后缀。
Consumable Media Types(Content-Type条件)
你可以基于请求的Content-Type
来映射到具体的请求
这个consumes
属性也支持否定表达式,比如!text/plain
意味着非text/plain
。
你可以分级使用consumes属性。比如在文件级别。不像多数的请求属性,然而使用它在class level,method level会覆盖他的consumes
属性。
Producible Media Types (Accept条件)
你可以通过Accept
header来罗列一系列被接受的属性。produces
产出对应着被
也可以用 !text/plain
Parameters, headers (按参数条件匹配)
可以根据参数和header来进行条件化的匹配
HTTP HEAD, OPTIONS
@GetMapping
等同于@RequestMapping(method=HttpMethod.GET)
支持的HTTPMapping有如下
GET,HEAD,POST,PUT,PATCH,DELETE,OPTION
Custom Annotations 自定义注解
mvc 内置@GetMapping, @PostMapping, @PutMapping, @DeleteMapping, 和 @PatchMapping。
HTTP也支持自定义注解,需要继承RequestMappingHandlerMapping,实现getCustomMethodCondition()方法。
Explicit Registrations 明确的注册
可以使用代码去动态注册一个类
1.3.3 Handler Methods
@RequestMapping 可以有灵活的签名,来从匹配响应的controller
Method Arguments 方法参数
JDK8的 java.util.Optional
支持方法参数和有required
属性注解混合使用,比如@RequestParam, @RequestHeader
,和对等的required=false。
这个表描述的是一些之策controller 方法参数
。
Return Values 支持的返回值
Type Conversion 参数代表注解
一些controller的方法注解,代表了request的不同输入,但都是String-based(such as @RequestParam, @RequestHeader, @PathVariable, @MatrixVariable, and @CookieValue)。
同时也支持自定义,你可以使用WebDataBinder (see DataBinder),通过注册Formatters到FormattingConversionService
,详见Spring Field Formatting
Matrix Variables
RFC 3968中描述了name-value键值对在path分隔符间的定义。Spring MVC中,我们定义为matrix variables
,形如/cars;color=red,green;year=2012).
。下列是如何使用
简单的匹配
清晰的匹配
设置默认值
返回一个数组
注意要使用这种方式,你需要打开开关。在MVC java配置中,你需要Path Matching
设置UrlPathHelper
with removeSemicolonContent=false
。xml注解中<mvc:annotation-driven enable-matrix-variables="true"/>
@RequestParam
你可以使用@RequestParam注解去绑定Servlet request参数到一个在里的method 参数。
如下
默认的,方法参数中写明,就意味着required
属性为ture
。可以额外指定为flase
。至于对非String的类型转化,请看Type Conversion
。
当一个@RequestParam被声明为Map<String, String> MultiValueMap<String, String>
那么内容就是参数键值对。
注意@RequestParam是可选的,一般来讲处理简单的键值对。
@RequestHeader
使用这个注解来绑定request的header。形如
也可以使用Map<String, String>, MultiValueMap<String, String>,或者HttpHeaders argument
来承接。
注意有的header是list,例如@RequestHeader("Accept")
可以使用String but also String[]或List<String>
来执行。
@CookieValue
HTTP的@CookieValue注解
类如有cookie
@ModelAttribute
主要是request级别
你可以使用@ModelAttribute
一个方法参数上,然后去从一个model初始化。这个model ModelAttribute值也会http的request中取值,如果field name可以匹配的上的话。
一下是绑定的顺序过程
-
- 如果model早已被添加,就使用model
-
- 从HTTP session
-
- 从URL PATH variable获取,通过一个
Converter
。
- 从URL PATH variable获取,通过一个
-
- 从默认的构造器
-
- 从参数构造器,其余request的参数匹配。参数的名称会被javaBeans的@ConstructorProperties获取到
处理使用通用的Modle去绑定值外,还会有Converter<String, T>
方式。可以预先写好转化,把一个字符串,解析成一个对象。不过需要注册Converter
使用Web DataBinder
在使用Model 去获得实例后还可以使用BindingResult
参数去check 参数的合法性。
一些场景你也许直接想用model而不用 databinding如下
配合vailde进行验证
@SessionAttributes
是在HTTP Servlet session级别去存储modle模型。典型的model都会存在session里,对session下的请求都是可见的。
在第一个请求里,放了一个pet
name的属性,就会被添加到model中去,并放置到session里。它会一致保留到另外的方式使用SessionStatus
方法参数去清空。
@SessionAttribute
@SessionAttribute
如果你想要访问一些预置的session属性,它可能存在或者不存在。你可以使用@SessionAttribute
注解在方法参数上,下列的例子就是
如果你想要去添加或者移除一些session属性,请把org.springframework.web.context.request.WebRequest
和javax.servlet.http.HttpSession
放入到controller method中。
对于临时性的存储,在session的控制流中,考虑去使用@SessionAttributes
@RequestAttribute
和SessionAttribute类似,@RequestAttribute注解去拿到预定义在请求里面的属性(比如在Servlet Filter或者HandlerInterceptor创建的)
Redirect Attributes 重定向是属性值得传递
默认的所有的model 属性,都会被作为URL变量暴露给Redirect URL。对于保留的属性,那些可能是私有类型或者集合、数组都会自动appended为query 参数。
如果一个model instance准备把所有的属性暴露给Redirect URL,那么append 私有属性是没有问题的。但是在注解驱动的controller,model还会包含一些被额外添加的属性或者希望丢弃的属性。为了避免这些信息被全量的暴露给URL,@RequestMapping可以声明一个RedirectAttributes
并且使用它去明确指出那些是被暴露给RedirectView
。如果这些方法做了重定向,这个RedirectAttributes
就会被使用
。否则的话全量的model会被使用。RequestMappingHandlerAdapter
提供了一个名为ignoreDefaultModelOnRedirect
的flag,用来指示默认Model是否应该永远不会被使用,当存在RedirectAttributes
的时候。否则所有属性都会被声明为RedirectAttributes
。Spring MVC和MVC Java configuration都会默认设置为false,来保证兼容。新应用应该设置为true。
注意url template里面的变量会被redirect自动获取,不需要明确声明
自定获取
另一种重定向的属性传递是靠flash attribute。
Flash Attributes
flash 属性提供了request之间的属性传递。这个是重定向时最常用的。
例如Post-Redirect-Get模式,flash属性在redirect之前先被临时存储,去让重定向后的请求获得。
Spring MVC提供了两种对flash属性的支持。FlashMap
和 FlashMapManager
去存储flash属性。
flash属性总是"on",不需要明确打开。然而,如果不用的话,HTTP session就永远不会创建。对于每个请求存在input
flash,和output
flashmap。两个FlashMap
实例都可以通过RequestContextUtils
的静态方法获取。
注解controller典型 的需要直接和FlashMap
交互。使用@RequestMapping
接受参数RedirectAttributes
,并使用它去存储和取回就会自动的存入input
和output
FlashMap。
匹配request到 flash attributes
flash在很多web框架都有,但是存在一个问题就是,期待的下一个请求,可能是异步需要等待很久。
为了减少这种可能,RedirectView
会自动标记FlashMap 实例,通过使用path和query参数。反过来,FlashMapManager也会为即将到来的请求匹配"input" FlashMap 。
Multipart 多片传输
在MultipartResolver
打开后,拥有multipart/form-data
的POST请求的内容,会被解析和当做寻常的参数,实例展示如何获得和上传一个文件。
如果声明参数的类型为List<MultipartFile>
,就允许多片传输用一个参数名。
当@RequestParam
名称被宣布成为Map<String, MultipartFile>
或者MultiValueMap<String, MultipartFile>
,无需在注解annotation里具体指明名称,然后这个map会被给的multipart文件自动标识。
你可使用data binding
多片传输的请求也可以非浏览器的client,以restFul 语义传输。
你可以用meat-data
部分,用@RequestParam
作为一个String ,你也可以从一个JSON去反序列化,类似于
@RequestBody。
使用@RequestPart
注解让你HttpMessageConverter
来做转化
也可以和@Vaild一起使用,或者是@Validated。
@RequestBody
使用该注解能够让你从request body读和反序列化成一个对象。通过HttpMessageConverter
。下面的例子是使用@RequestBody
。
就是把返回值序列化成字符串
你可以使用Message Converters
和MVC Config
去配置或者自定义消息转化。
你可以把@RequestBody
和javax.validation.Valid
或@Validated一块使用,两个都是标准Bean的验证。默认的如若不合格,就会造成MethodArgumentNotValidException
,他会被转化成一个400(BAD_REQUEST)的response。你也可以通过Errors or BindingResult
自行进行处理
HttpEntity
HttpEntity 和@RequestBody差不多,但是是基于container 对象,其暴露了request请求和body下列展示的例子。
@ResponseBody
用该注解可以使用HttpMessageConverter通过对responseBody进行处理
@ResponseBody可以在class级别去是用,会被所有的cotroller method所继承。和@RestController
类似,@RestController
就是@Controller和@ResponseBody的复合注解。
你可以把@ResponseBody和JSON一块使用
ResponseEntity
和@ResponseBody类似,但是具有status和headers。
重要关于Jackson JSON
需要Jacksong library
JSON Views
Spring mvc 内置了Jackson’s Serialization Views,循序从一系列在Object的子集合里渲染。为了使用@ResponseBody
或者 ResponseEntity
controller 方法。你可以使用Jackson的@JsonView
注解。
你也可以把添加序列化的view类到model中去,如下
1.3.4 Model
你可以使用@ModelAttribute
- 在一个@RequestMapping标注的
方法参数
上,去创建或者从一个mode获得一个对象,并且通过WebDataBinder
去绑定到request。 - 在用@Controller或者 @ControllerAdvice标注的method-level方法去帮助初始化一个对象,提供给@RequestMapping去调用。
- 在@RequestMapping标注他的返回值为一个model。
这个部署讨论前面罗列的第二种。一个controller可以有很多@ModelAttribute的methods。所有这些方法都是在@RequestMapping
方法之前执行。一个@ModelAttribute方法可以通过@ControllerAdvice被共用见下面的Controller Advice。
@ModelAttribute方法有灵活的方法签名,支持很多和@RequestMapping一样的参数签名。
标注在方法上
前面的ModeAttribue会更详细
这里的作用是将参数值(number)和返回值放入Model(acount)中
这里只放了acount
如果不特定指明属性名就会,基于Object的type,在Conventions的doc中详细阐述。你可以使用addAttribute方法明确一个name,或者在
@ModelAttribute
加一个name,这会为返回值生成一个return。
@ModelAttribute + @RequestMapping + method-level 让返回值会被当做model attribute。
1.3.5 DataBinder重要
@Controller或 @ControllerAdvice可以有@InitBinder
方法区初始化WebDataBinder
,可以做
- bind Request parameter (或者 form or query data)到一个model object
- 转化 String-based的request values—request params、path variables、header、cookies,到目标的controller或者method 参数
- 格式化model队像作为String。
@InitBinder方法可以注册到controller-specificjava.bean.PropertyEditor
或者Spring Converter
或者 Formatter
组件。另外你可以用MVC的配置在全局的FormattingConversionService
,去注册Converter and Formatter
。
@InitBinder 方法支持很多和@RequestMapping一样的方法,除了@ModelAttribute参数。典型的是和一个WebDataBinder 和void参数值使用
也可以使用dataformater
1.3.6 Exceptions 异常处理
注册一个异常
匹配多个异常使,根异常被抛出。
Method Arguments
@ExceptionHandler methods support the following arguments:
return values
REST API exceptions
@RestController 使用@ExceptionHandler 可以用ResponseEntity
来设置response的响应值
1.3.7. Controller Advice
一般@ExceptionHandler和@InitBinder和@ModelAttribute方法在@Controller声明的地方。但是如果想想去应用的全局,你应该把这些注解声明到@ControllerAdvice
或者@RestControllerAdvice
。