以前:不同功能使用不同路径
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作):保持访问路径不变
现在: /user
spring:
mvc:
hiddenmethod:
filter:
enabled: true #开启页面表单的Rest功能
<form action="/user" method="get">
<input value="REST-GET提交" type="submit" />
</form>
<form action="/user" method="post">
<input value="REST-POST提交" type="submit" />
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="DELETE"/>
<input value="REST-DELETE 提交" type="submit"/>
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT" />
<input value="REST-PUT提交"type="submit" />
<form>
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter((ServletRequest)requestToUse, response);
}
PUT,
PATCH,
DELETE,
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
public String getMethod() {
return this.method;
}
}
}
1、表单提交会带上. _method=PUT _method=DELETE
2、请求过来被HiddenHttpMethodFilter拦截
请求是否正常,并且是POST
获取到_method的值
兼容以下请求;PUT、DELETE、PATCH
原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
注意:
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)。 看出rest风格默认时关闭的,使用时需要手动开启
spring:
mvc:
hiddenmethod:
filter:
enabled: true /#开启页面表单的Rest功能
public class WebMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
}
public class OrderedHiddenHttpMethodFilter extends HiddenHttpMethodFilter implements OrderedFilter {
}
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
private static final List<String> ALLOWED_METHODS =
Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(),
HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = DEFAULT_METHOD_PARAM;
public void setMethodParam(String methodParam) {
Assert.hasText(methodParam, "'methodParam' must not be empty");
this.methodParam = methodParam;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
if (ALLOWED_METHODS.contains(method)) {
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter(requestToUse, response);
}
private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {
private final String method;
public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
}
@Override
public String getMethod() {
return this.method;
}
}
}
如PostMan可直接发送put、delete等方式请求。
表单只能写get、post方式,所以需要走过滤器:HiddenHttpMethodFilter。
@RequestMapping(value = "/user",method = RequestMethod.GET) 等价与 @GetMapping("/user")
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
...
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
...
}
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class):意味着在没有HiddenHttpMethodFilter时,才执行hiddenHttpMethodFilter()。因此,我们可以自定义filter,改变默认的_method。例如:
@Configuration(proxyBeanMethods = false)
public class WebConfig{
//自定义filter
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
methodFilter.setMethodParam("_m");
return methodFilter;
}
}
将\_method
改成_m
<form action="/user" method="post">
<input name="_m" type="hidden" value="DELETE"/>
<input value="REST-DELETE 提交" type="submit"/>
</form>
SpringMVC功能分析都从 org.springframework.web.servlet.
DispatcherServlet处理-> doDispatch()
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 找到当前请求使用哪个Handler(Controller的方法)处理
mappedHandler = getHandler(processedRequest);
//HandlerMapping:处理器映射。/xxx->>xxxx
...
}
getHandler()
方法如下:
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
**this.handlerMappings
**在Debug模式下展现的内容:有5个handlerMapping
其中,RequestMappingHandlerMapping:保存了所有@RequestMapping
和handler
的映射规则。
所有的映射:
所有的请求映射都在HandlerMapping中:
SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
如果有就找到这个请求对应的handler
如果没有就是下一个 HandlerMapping
我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping,可以自定义 HandlerMapping
注解:
http:8080:localhost/car/{id}/owner/{username}?age=18&inters=basketball&inters=game
//If the method parameter is {@link java.util.Map Map<String, String>
//then the map is populated with all path variable names and values.
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
}
通过@PathVariable可以将请求中的所有参数放到map中。
使用用例
@RequestMapping("/car/{id}/owner/{username}")
public Map getCar(@PathVariable("id") Integer id,
@PathVariable("username") String username,
@PathVariable Map<String,String> map){
Map<String,Object> maps = new HashMap<>();
maps.put("id",id);
maps.put("username",username);
maps.put("kv",map);
return maps;
}
通过@RequestHeader可以将请求头中的所有参数放到map中。
//If the method parameter is {@link java.util.Map Map<String, String>,
//{@link org.springframework.util.MultiValueMap MultiValueMap<String, String>}, or {@link org.springframework.http}HttpHeaders HttpHeaders}@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader {
}
@RequestMapping("/car/{id}/owner/{username}")
public Map getCar(@PathVariable("id") Integer id,
PathVariable("username") String username,
@PathVariable Map<String,String> map,
@RequestHeader("User-Agent") String agent,
@RequestHeader Map<String,String> headers) {
Map<String,Object> maps = new HashMap<>();
maps.put("id",id);
maps.put("username",username);
maps.put("kv",map);
maps.put("header",agent);
maps.put("headers",headers);
return maps;
通过@RequestParam可以将请求参数中的所有参数放到map中。
//If the method parameter is {@link java.util.Map Map<String, String>,
//{@link org.springframework.util.MultiValueMap MultiValueMap<String, String>}, or {@link org.springframework.http}HttpHeaders HttpHeaders}@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
}
@RequestMapping("/car/{id}/owner/{username}")
public Map getCar(@PathVariable("id") Integer id,
@PathVariable("username") String username,
@PathVariable Map<String,String> map,
@RequestHeader("User-Agent") String agent,
@RequestHeader Map<String,String> headers,
@RequestParam("age")String age,
@RequestParam("inters") List<String> inters,
@RequestParam Map<String,String> params){
Map<String,Object> maps = new HashMap<>();
maps.put("id",id);
maps.put("username",username);
maps.put("kv",map);
maps.put("header",agent);
maps.put("headers",headers);
maps.put("age",age);
maps.put("inters",inters);
maps.put("prams",params);
return maps;
}
通过@CookieValue可以将cookie 放到Cookie中
//The method parameter may be declared as type {@link javax.servlet.http.Cookie} or as cookie value type (String, int, etc.).
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CookieValue {
}
@RequestMapping("/car/{id}/owner/{username}")
public Map getCar(@PathVariable("id") Integer id,
@PathVariable("username") String username,
@PathVariable Map<String,String> map,
@RequestHeader("User-Agent") String agent,
@RequestHeader Map<String,String> headers,
@RequestParam("age")String age,
@RequestParam("inters") List<String> inters,
@RequestParam Map<String,String> params,
@CookieValue("_ga")String _ga,
@CookieValue("_ga") Cookie cookie){
Map<String,Object> maps = new HashMap<>();
maps.put("id",id);
maps.put("username",username);
maps.put("kv",map);
maps.put("header",agent);
maps.put("headers",headers);
maps.put("age",age);
maps.put("inters",inters);
maps.put("prams",params);
map.put("_ga",_ga);
System.out.println(cookie.getName() + ":" + cookie.getValue());
return maps;
}
_ga:GA1.1.1466056184.1630465944
获取表单数据,因为表单有请求体。
@PostMapping("/save")
public Map postMethod(@RequestBody String content){
Map<String,Object> map = new HashMap<>();
map.put("content",content);
return map;
}
@Controller
public class RequestController {
@GetMapping("/goto")
public String goToPage(HttpServletRequest request){
request.setAttribute("msg","成功了...");
request.setAttribute("code",200);
return "forward:/success"; //转发到 /success请求
}
@ResponseBody
@GetMapping("/success")
public Map success(@RequestAttribute(value = "msg") String msg,
@RequestAttribute(value = "code")Integer code,
HttpServletRequest request){
Object msg1 = request.getAttribute("msg");
map.put("reqMethod_msg",msg1);
map.put("annotation_msg",msg);
return map;
}
}
语法:
请求路径:/cars/sell;low=34;brand=byd,audi,yd
SpringBoot默认是禁用了矩阵变量的功能
手动开启:原理是对于路径的处理,都是使用UrlPathHelper的removeSemicolonContent(移除分号内容)设置为false,让其支持矩阵变量的。
矩阵变量必须有url路径变量才能被解析
获取请求参数的两种方式:查询字符串和矩阵变量。
正常:服务端生成session存放内容,返回给客户端封装到cookie中生成jsessionid标识,再次请求时客户端携带cookie到服务端。
当cookie被禁用时,可以使用矩阵变量的方式将jsessionid传给后端。
@RestController
public class ParameterTestController {
///cars/sell;low=34;brand=byd,audi,yd
@GetMapping("/cars/{path}")
public Map carsSell(@MatrixVariable("low") Integer low,
@MatrixVariable("brand") List<String> brand,
String path){
Map<String,Object> map = new HashMap<>();
map.put("low",low);
map.put("brand",brand);
map.put("path",path);
return map;
}
// /boss/1;age=20/2;age=10
@GetMapping("/boss/{bossId}/{empId}")
public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
@MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
Map<String,Object> map = new HashMap<>();
map.put("bossAge",bossAge);
map.put("empAge",empAge);
return map;
}
}
手动开启矩阵变量:
WebMvcConfigurer
接口:@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
// 不移除;后面的内容。矩阵变量功能就可以生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
WebMvcConfigurer
Bean:@Configuration(proxyBeanMethods = false)
public class WebConfig{
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
// 不移除;后面的内容。矩阵变量功能就可以生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
}
}
}
ctrl + F12 : 继承树
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/mingyuli/article/details/120943108
内容来源于网络,如有侵权,请联系作者删除!