9.SpringBoot2请求处理-【源码分析】-Rest映射及源码解析+请求处理-【源码分析】-怎么改变默认的_method

请求映射
@xxxMapping;

        @GetMapping
        @PostMapping
        @PutMapping
        @DeleteMapping
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)

  • 以前:
    • /getUser 获取用户
    • /deleteUser 删除用户
    • /editUser 修改用户
    • /saveUser保存用户
  • 现在: /user
    • GET-获取用户
    • DELETE-删除用户
    • PUT-修改用户
    • POST-保存用户
    • 核心Filter;HiddenHttpMethodFilter

用法

  • 开启页面表单的Rest功能
  • spring:
      mvc:
        hiddenmethod:
          filter:
            enabled: true  
  • 页面 form的属性method=post,隐藏域 _method=put、delete等(如果请求为get或post,无需隐藏域)
  • 编写请求映射
<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>
@GetMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
    return "GET-张三";
}

@PostMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
    return "POST-张三";
}

@PutMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
    return "PUT-张三";
}

@DeleteMapping("/user")
//@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
    return "DELETE-张三";
}

Rest原理(表单提交要使用REST的时候)

  • 表单提交会带上_method=PUT
  • 请求过来被WebMvcAutoConfiguration类中的OrderedHiddenHttpMethodFilter方法拦截
  •     @Bean
        @ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
        @ConditionalOnProperty(
            prefix = "spring.mvc.hiddenmethod.filter",
            name = {"enabled"}
        )
        public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
            return new OrderedHiddenHttpMethodFilter();
        }
  • 请求是否正常,并且是POST
  • 获取到_method的值。
  • 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()));
    
    	/** Default method parameter: {@code _method}. */
    	public static final String DEFAULT_METHOD_PARAM = "_method";
    
    	private String methodParam = DEFAULT_METHOD_PARAM;
    
    
    	/**
    	 * Set the parameter name to look for HTTP methods.
    	 * @see #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);
    	}
    
    
    	/**
    	 * Simple {@link HttpServletRequest} wrapper that returns the supplied method for
    	 * {@link HttpServletRequest#getMethod()}.
    	 */
    	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;
    		}
    	}
    
    }
    
  • 兼容以下请求;PUT.DELETE.PATCH
  • 原生request(post),包装模式requestWrapper重写了getMethod方法,返回的是传入的值。(delete、put)
  • 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requestWrapper的。

请求处理-【源码分析】-怎么改变默认的_method

@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)意味着

在没有HiddenHttpMethodFilter时,才执行hiddenHttpMethodFilter()。因此,我们可以自定义filter,改变默认的\_method

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

因此,可以重写hiddenHttpMethodFilter类,并调用setMethodParam方法,改变默认的_method。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.web.filter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

public class HiddenHttpMethodFilter extends OncePerRequestFilter {
    private static final List<String> ALLOWED_METHODS;
    public static final String DEFAULT_METHOD_PARAM = "_method";
    private String methodParam = "_method";

    public HiddenHttpMethodFilter() {
    }

    public void setMethodParam(String methodParam) {
        Assert.hasText(methodParam, "'methodParam' must not be empty");
        this.methodParam = methodParam;
    }

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

    static {
        ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name()));
    }

    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;
        }
    }
}

@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>


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值