【问题标题】:405 JSP error with Put Method使用 Put 方法出现 405 JSP 错误
【发布时间】:2014-08-31 14:55:17
【问题描述】:

我遇到了关于 JSP 不接受 PUT 请求的问题。所以我想知道如何解决它。我在堆栈溢出中阅读了这个相关的问题,但它没有解释如何解决它。

HTTP Status 405 - JSPs only permit GET POST or HEAD

来自 Rails 背景,我正在努力做到这一点,所以我使用 Rails REST 样式,如 PUT 进行更新,并使用 DELETE 删除用户资源。

但每当此控制器出现错误时,它会尝试将请求返回给原始 JSP,但 Tomcat 8.0.9 不接受请求并给出以下错误:“HTTP 状态 405 - JSP 只允许 GET POST 或 HEAD ”。我尝试在 Tomcat web.xml 中禁用只读 - 它没有任何效果,我仍然得到错误。我已将其切换为 POST 方法,并且流程运行良好。

有没有办法强制转发为 POST 方法,同时仍接受 PUT 方法的请求?

/**
     * Edit a user account.
     * @return the edit user view
     */
    @RequestMapping(value = {"/update/{userId}"}, method = RequestMethod.PUT)
    public String updateUser(@Valid @ModelAttribute("user") User user, BindingResult result, final RedirectAttributes redirectAttributes)
    {
        logger.debug(user);

        // we check for duplicate email addresses during the update operation.
        List<User> userCheckList = userRepository.findByEmail(user.getEmail());
        if (userCheckList.size() > 0)
        {
            // size of list should only ever be 1
            User userCheck = userCheckList.get(0);
            if (userCheck.getId() != user.getId())
            {
                result.rejectValue("email", "error.user", "An account already exists for this user email address.");
            }
        }


        if (result.hasErrors())
        {
            return "admin.users.edit";
        }

        // we locate the user and add it to the model
        userRepository.save(user);



        // the save operation was successful so we show the user message.
        redirectAttributes.addFlashAttribute("user", user);
        redirectAttributes.addFlashAttribute("message", "Updated successfully");


        String viewName = "redirect:/admin/users";
        logger.debug(viewName);

        return viewName;
    }

【问题讨论】:

标签: spring-mvc


【解决方案1】:

问题在于,当您从控制器方法返回视图名称时,Spring DispatcherServlet 将转发到给定视图,保留原始的 PUT 方法。

在尝试处理此转发时,Tomcat 将拒绝它,理由是对 JSP 的 PUT 可以解释为“用此请求的内容替换服务器上的此 JSP 文件。”

您确实希望您的控制器处理您的PUT 请求,然后以GET 的形式转发到您的JSP。幸运的是,Servlet 3.0 提供了一种纯粹在 FORWARD 调度程序上进行过滤的方法。

创建过滤器:

public class GetMethodConvertingFilter implements Filter {

    @Override
    public void init(FilterConfig config) throws ServletException {
        // do nothing
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {

        chain.doFilter(wrapRequest((HttpServletRequest) request), response);
    }

    @Override
    public void destroy() {
        // do nothing
    }

    private static HttpServletRequestWrapper wrapRequest(HttpServletRequest request) {
        return new HttpServletRequestWrapper(request) {
            @Override
            public String getMethod() {
                return "GET";
            }
        };
    }
}

然后将其连接到您的web.xml 中:

<filter>
    <filter-name>getMethodConvertingFilter</filter-name>
    <filter-class>my.GetMethodConvertingFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>getMethodConvertingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

这将仅在转发时将请求转换为GET,而通过其他调度程序的请求保持不变,因此PUTs 将被您的控制器正常拦截。

我(可能不正确)的理解是 Tomcat 8.0.9 引入了一个修复程序,该修复程序有效地为 ERROR 调度程序自动完成 - 请参阅链接问题中的 answer。但是您没有使用容器的错误处理机制来呈现错误页面,而是使用 Spring MVC 手动转发到视图,因此您需要这样做。就我个人而言,我在 Jetty 9.2.7 下遇到了这个问题,没有这样的修复,我将错误处理委托给容器,所以我的过滤器映射中也配置了 &lt;dispatcher&gt;ERROR&lt;/dispatcher&gt;

这一切看起来有点神秘,但这是我发现成功跳过这个特殊的 RESTful-Spring-JSP-web-application 箍的唯一方法。

【讨论】:

  • 谢谢,我还是把所有东西都切换到了 Post,使用 Post v Put 没什么大不了的。我一直想知道是否有一种方法可以重定向回新方法,尽管当出现错误时,同时保持 BindingResult 可用于请求,这会将其重定向回 GET 并且对我的代码更整洁。不过,我还没有深入研究它。这个答案是一个很好的解决方案,所以我会正确标记它。
【解决方案2】:

我遇到了同样的问题,最后通过添加解决了

<%@ page isErrorPage="true" %>

在我的 JSP 页面的开头。
使用 apache-jsp-8.0.33,org/apache/jasper/compiler/Generator.java 会跳过对设置了此标志的页面的检查,从而允许 JSP 响应任何方法。

【讨论】:

    【解决方案3】:

    如果 web.xml 还包含 Spring 安全配置,例如:

    <!--    springSecurityFilterChain-->
    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>
    

    ,然后 - 只是 @ryanp 描述的 above 的一个小改进, 处理 DELETEPUT 方法:

    public class GetMethodConvertingFilter implements Filter {
    
    @Override
    public void init(FilterConfig config) throws ServletException {
        // do nothing
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
    
        chain.doFilter(wrapRequest((HttpServletRequest) request), response);
    }
    
    @Override
    public void destroy() {
        // do nothing
    }
    
    private static HttpServletRequestWrapper wrapRequest(HttpServletRequest request) {
        return new HttpServletRequestWrapper(request) {
            @Override
            public String getMethod() {
                String requestMethod = request.getMethod();
                return HttpMethod.PUT.matches(requestMethod) || HttpMethod.DELETE.matches(requestMethod) ?
                        HttpMethod.GET.toString() : requestMethod;
            }
        };
      }
    }
    

    Tomcat 8.5.51

    上测试

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-04
      • 2014-10-04
      • 1970-01-01
      • 2016-11-07
      • 2015-10-14
      • 2018-07-15
      • 2020-04-18
      • 2016-10-16
      相关资源
      最近更新 更多