Springboot 2.x系列文章5:WEB开发2-动态访问
前言
Springboot 开发,最常用的就是WEB项目动态访问开发
一、新旧动态访问变化
- Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
— 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
— 现在: /user GET-查询/获取用户 DELETE-删除用户 PUT-修改用户 POST-新增/保存用户
二、method方法介绍
1.CreateXXX-post
HTML前端
<form action="/user" method="post">
<input value="rest-post" type="submit" />
</form>
Controller接口后端
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String PostUser()
{
return "张三-Post";
}
2. DeleteXXX-delete
HTML前端
- 正常理解method=“delete”,但是method只有get和post;不支持(需修改)
<form action="/user" method="delete">
<input value="rest-delete" type="submit">
</form>
- 修改为post方法,加上method,delete协助
<form action="/user" method="post">
<input name="_method" type="hidden" value="delete"/>
<input value="rest-update" type="submit">
</form>
Controller接口后端
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String DeleteUser()
{
return "张三-delete";
}
3. UpdateXXX-put
- 正常理解,但是method只有get和post,不支持put
<form action="/user" method="PUT">
<input value="rest-update" type="submit">
</form>
- 修改为post方法,加上method,put协助
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT"/>
<input value="rest-update" type="submit">
</form>
Controller接口后端
@RequestMapping(value = "/user",method =RequestMethod.PUT)
public String UpdateUser()
{
return "张三-update";
}
4.Query-get
HTML前端
<form action="/user" method="get">
<input value="rest-get" type="submit" />
</form>
Controller接口后端
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String GetUser()
{
return "张三-Get";
}
5. 增加put和delete支持
修改yaml文件
mvc:
hiddenmethod:
filter:
enabled: true
原理
- WebMvcAutoConfiguration(OrderedHiddenHttpMethodFilter)
- OrderedHiddenHttpMethodFilter
- HiddenHttpMethodFilter( _method 和 post)
- HttpMethodRequestWrapper(内部类)
命令集合 ALLOWED_METHODS = HttpMethod.PUT、DELETE.name(), PATCH.name())
public class HiddenHttpMethodFilter extends OncePerRequestFilter {
private static final List<String> ALLOWED_METHODS;
public static final String DEFAULT_METHOD_PARAM = "_method";
private String methodParam = "_method";
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);
}
自定义参数名,增加请求方法
如修改为“FuMethod”
- 由配置可知,当不存在时使用默认的,那么自定义即先加载一个自定义的HiddenHttpMethodFilter类
@Bean
@ConditionalOnMissingBean({HiddenHttpMethodFilter.class})
@ConditionalOnProperty(
prefix = "spring.mvc.hiddenmethod.filter",
name = {"enabled"}
)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
- 自定义一个HiddenHttpMethodFilter类
@Configuration
public class MyFilterConfig {
@Bean
public HiddenHttpMethodFilter MyHiddenHttpMethodFilter()
{
HiddenHttpMethodFilter myMethodFilter=new HiddenHttpMethodFilter();
myMethodFilter.setMethodParam("_FuMethod");
return myMethodFilter;
}
}
三、rest请求映射原理
任何调用后端的方法都要经过doDispatch
- DispatchServlet的类继承关系
- 常用方法及实现类
doGet方法调用顺序
- HttpServlet的doGet
- FrameworkServlet的processRequest
- DispatchServlet的doService
- DispatchServlet的doDispatch
doDispatch方法解读
- 获取request
- 处理文件上传
- 找到request对应处理方法mappedHandler(controller的方法)
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
常见HandlerMappings
所有的请求映射都在HandlerMapping中,如下图所示。
- SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
- SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
- 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
○ 如果有就找到这个请求对应的handler
○ 如果没有就是下一个 HandlerMapping - 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping
四、注意事项
Form表单提交
Rest原理(表单提交要使用REST的时候)
- 表单提交会带上_method=PUT
- 请求过来被HiddenHttpMethodFilter拦截
○ 请求是否正常,并且是POST
■ 获取到_method的值。
■ 兼容以下请求;PUT.DELETE.PATCH
■ 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
■ 过滤器链放行的时候用wrapper。
yaml配置文件
mvc:
hiddenmethod:
filter:
enabled: true
客户端工具
- Rest使用客户端工具,无需设置Filter。
如PostMan(JMeter)直接发送Put、delete等方式请求,无需Filter。