【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 {
    @Autowired
    EmployeeDao employeeDao;

    @Autowired
    DepartmentDao departmentDao;

    // 查询所有员工返回列表页面
    @GetMapping(value="/emps")
    public String list(Model model) {
        Collection<Employee> employees = employeeDao.getAll();
        // 放在请求域中
        model.addAttribute("emps", employees);
        // thymeleaf 默认拼串
        // classpath:/templates/XXXX.html
        return "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;
	}

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

}

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐