HTTP 405 的錯誤提示:訊息 JSP 只允許 GET、POST 或 HEAD。Jasper 還允許 OPTIONS 的解決方法

檸檬可樂小布丁發表於2020-04-18

如果專案是執行在 Tomcat 8 及以上,會發現發出的 PUT 請求和 DELETE 請求可以被控制其接收到,但是返回頁面時(forward)會報HTTP 405 的錯誤提示:"訊息 JSP 只允許 GET、POST 或 HEAD。Jasper 還允許 OPTIONS"

解決方案:

  1. 使用 Tomcat 8 以下版本。

  2. 使用 @RestController 或者 @Controller + @ResponseBody 標籤,但是這樣就無法跳轉頁面了。

  3. 避免使用 forward 方式跳轉頁面,改為 重定向redirect方式跳轉到另一個控制器方法,再由這個控制器方法跳轉頁面。

    	@RequestMapping(value = "/rest", method = RequestMethod.PUT)
    	public String put() {
    		// 接收表單中的各種資訊
    		System.out.println("PUT --- 更新資料");
    		return "redirect:/success";
    	}
    
    	@RequestMapping(value = "/success")
    	public String success() {
    		return "success";
    	}
    
  4. 給 Tomcat 新增啟動引數,使Tomcat允許寫操作

    <init-param>
        <param-name>readonly</param-name>
        <param-value>false</param-value>
    </init-param>
    
  5. 建立一個新的 Filter 來過濾 FORWARD

    // HiddenHttpMethodFilter.java
    @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)) {
    				requestToUse = new HttpMethodRequestWrapper(request, paramValue);
    			}
    		}
    
    		filterChain.doFilter(requestToUse, response);
    	}
    

    HiddenHttpMethodFilter 中的 doFilterInternal 方法是用來過濾 form 表單中 name 為 _method的請求。可以發現,它把請求作為引數傳進 HttpMethodRequestWrapper 中並且返回了一個新的請求,放行的也是新的請求。所以我們可以重寫 HttpMethodRequestWrapper 中的 getMethod() 方法,讓它支援 forward 方式的跳轉。

    // 重寫 getMethod()
    package com.pudding.conf;
    
    import java.io.IOException;
    
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.web.filter.HiddenHttpMethodFilter;
    
    public class MyHttpMethodFilter extends HiddenHttpMethodFilter {
    
    	@Override
    	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
    			throws ServletException, IOException {
    
    		HttpServletRequest requestToUse = request;
    
    		String method = requestToUse.getMethod();
    		if (method.equalsIgnoreCase("delete") || method.equalsIgnoreCase("put")) {
    			method = "POST";
    		}
    
    		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;
    		}
    
    		public String getMethod() {
    			return this.method;
    		}
    	}
    }
    

    在 web.xml 中配置自己的過濾器:

    	<filter>
    		<filter-name>myFilter</filter-name>
    		<filter-class>com.pudding.conf.MyHttpMethodFilter</filter-class>
    	</filter>
    	<filter-mapping>
    		<filter-name>myFilter</filter-name>
    		<url-pattern>/*</url-pattern>
    		<dispatcher>FORWARD</dispatcher>
    	</filter-mapping>
    
  6. 在 forward 需要跳轉的頁面頭加上 isErrorPage="true"

    <%@ page language="java" contentType="text/html; charset=UTF-8"
    	pageEncoding="UTF-8" isErrorPage="true"%>
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    	<h1>success</h1>
    </body>
    </html>
    

相關文章