【README】
浏览器使用form提交信息的时候只支持GET和POST,如果需要在浏览器上使用PUT和DELETE请求方式的话,只能使用欺骗的方式了,SpringMvc提供了HiddenHttpMethodFilter类来提供支持;
【1】前端
1)list.html
<body><!-- 引入抽取的topbar --><!--模板名: 会使用 thymeleaf的前后缀配置规则进行解析 --><!--<div th:replace="~{dashboard::topbar}"></div-->><div th:replace="commons/bar::topbar"></div><div class="container-fluid"><div class="row"><!-- 引入侧边栏 --><!--<div th:replace="~{dashboard::#sidebar}"></div>--><div th:replace="commons/bar::#sidebar(activeUri='emps')"></div><main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"><h2><a class="btn btn-sm btn-success" href="emp" th:href="@{/emp}">员工添加</a></h2><div class="table-responsive"><table class="table table-striped table-sm"><thead><tr><td>id</td><td>lastName</td><td>email</td><td>gender</td><td>department</td><td>birth</td><td>操作</td></tr></thead><tbody><tr th:each="emp:${emps}"><td th:text="${emp.id}"></td><td>[[${emp.lastName}]]</td><td>[[${emp.email}]]</td><td th:text="${emp.gender=='0'?'女':'男'}"></td><td th:text="${emp.department.departmentName}"></td><td th:text="${#dates.format(emp.birth,'yyyy-MM-dd')}"></td><td><a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑</a><button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除</button></td></tr></tbody></table></div></main><form id="deleteEmpForm" method="post"><input type="hidden" name="_method" value="delete" /></form></div>
</div>
其中删除发送的是 rest风格的delete请求;
<button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除</button><form id="deleteEmpForm" method="post"><input type="hidden" name="_method" value="delete" />
</form><script>$(".deleteBtn").click(function(){// 删除当前员工 $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();return false;});
</script>
【2】springboot后端
EmployeeController 控制器
@Controller
public class EmployeeController {@AutowiredEmployeeDao employeeDao;@AutowiredDepartmentDao departmentDao;// 查询所有员工返回列表页面@GetMapping(value="/emps")public String list(Model model) {Collection<Employee> employees = employeeDao.getAll();// 放在请求域中model.addAttribute("emps", employees);// thymeleaf 默认拼串// classpath:/templates/XXXX.htmlreturn "emp/list";}// 来到员工添加页面@GetMapping("/emp")public String toAddPage(Model model) {// 来到添加页面, 查询所有部门,在页面显示Collection<Department> departments = departmentDao.getDepartments();model.addAttribute("depts", departments);return "emp/add";}// 员工添加功能,springmvc自动将请求参数和入参对象的属性进行一一绑定 ,请求参数名字和javaBean入参属性名是一致的@PostMapping("/emp")public String addEmp(Employee employee) {employeeDao.save(employee);/*** 添加成功后,来到员工列表页面,emp/list.html,有两种方式:* 方式1,redirect:/emps 重定向;方式2,forward: /emps 请求转发;* 不能直接返回 /emps,因为thymeleaf模板引擎会解析为 emps.html*/return "redirect:/emps";}// 来到员工修改页面, 查出当前员工,在页面回显@GetMapping("/emp/{id}")public String toEditPage(@PathVariable("id") Integer id, Model model) {// 页面显示所有部门列表model.addAttribute("depts", departmentDao.getDepartments());// 查询员工model.addAttribute("emp", employeeDao.get(id));// 回到修改或编辑页面(add是新增或编辑页面)return "emp/add";}// 员工修改请求@PutMapping("/emp")public String toEditPage(Employee employee) {employeeDao.save(employee);return "redirect:/emps";}// 员工删除请求@DeleteMapping("/emp/{id}")public String deleteEmployee(@PathVariable("id") Integer id) {employeeDao.delete(id);return "redirect:/emps";}
}
其中处理删除请求的映射如下:
// 员工删除请求,接收delete请求@DeleteMapping("/emp/{id}")public String deleteEmployee(@PathVariable("id") Integer id) {employeeDao.delete(id);return "redirect:/emps";}
【3】点击删除
报错:(type=Method Not Allowed, status=405
method not allowed 405,表示 服务器不接收delete请求;
原因: springboot 没有启用 HiddenHttpMethodFilter 过滤器来支持delete请求;
解决方法:在 application.properties 中启用该过滤器, 如下:
application.properties
# 启动HiddenHttpMethodFilter过滤器,以支持浏览器可以发送DELETE PUT 请求
spring.mvc.hiddenmethod.filter.enabled=true
重启后,再次访问,如下:
【4】附录:HiddenHttpMethodFilter
javax.servlet.Filter 将发布的方法参数转换为 HTTP 方法,可通过 HttpServletRequest.getMethod() 检索。 由于浏览器目前仅支持 GET 和 POST,因此一种常用技术(例如 Prototype 库使用的技术)是使用带有附加隐藏表单字段 (_method) 的普通 POST 来传递“真正的”HTTP 方法。 此过滤器读取该参数并相应地更改 HttpServletRequestWrapper.getMethod() 返回值。 只允许使用“PUT”、“DELETE”和“PATCH”HTTP 方法。
请求参数的名称默认为 _method,但可以通过 methodParam 属性进行调整。
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;}@Overrideprotected 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;}@Overridepublic String getMethod() {return this.method;}}}