5

我正在尝试使用以下 jQuery 1.6通过 AJAX 在 Spring ( 3.2.0 ) 中调用一个方法。

function updateRoleEnabled(id)
{
    $.ajax({
            datatype:"json",                        
            type: "PUT",
            url: "/wagafashion/ajax/UpdateUserRole.htm",
            data: "id="+id+"&t="+new Date().getTime(),
            success: function(response)
            {
            },
            error: function(e)
            {
                alert('Error: ' + e);
            }
    });
}

它尝试在 Spring 中调用以下方法。

@RequestMapping(value=("ajax/UpdateUserRole"), method=RequestMethod.PUT)
public @ResponseBody void updateUserRole(@RequestParam(value=("id")) String id)
{
    System.out.println("id = "+id);
}

FireFox 响应以下错误。

HTTP 状态 405 - 不支持请求方法“GET”

类型状态报告

不支持消息请求方法“GET”

描述 请求的资源不允许指定的 HTTP 方法(不支持请求方法“GET”)。

Apache Tomcat/6.0.26

它与GETandPOST方法一起使用,JSON(与 Jackson-2.1.1 一起)在应用程序的其他部分也可以正常工作。


如需查看dispatcher-servlet.xml文件,完整内容如下。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"

       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
       http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">

    <context:component-scan base-package="controller" />
    <context:component-scan base-package="validatorbeans" />

    <mvc:annotation-driven content-negotiation-manager="contentNegotiationManager" >
        <mvc:message-converters register-defaults="false">
        <bean id="jacksonMessageConverter" p:supportedMediaTypes="application/json" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
    </mvc:message-converters>
    </mvc:annotation-driven>

    <bean id="contentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <property name="favorPathExtension" value="false" />
        <property name="favorParameter" value="false" />
        <property name="ignoreAcceptHeader" value="false" />
        <property name="mediaTypes" >
            <value>
                atom=application/atom+xml
                html=text/html
                json=application/json
                *=*/*
            </value>
        </property>
    </bean>

    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <prop key="org.springframework.web.multipart.MaxUploadSizeExceededException">
                    fileUploadingFailure
                </prop>
            </props>
        </property>
    </bean>
    <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="index.htm">indexController</prop>
            </props>
        </property>
    </bean>

    <bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/jsp/"
          p:suffix=".jsp" />

    <bean name="indexController"
          class="org.springframework.web.servlet.mvc.ParameterizableViewController"
          p:viewName="index" />
</beans>

如何制作除 Spring 3.2 之外的 HTTP 方法GETPOST在 Spring 3.2 中工作?


编辑:

根据下面的评论,以下是我的整个web.xml文件。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/applicationContext.xml
            /WEB-INF/spring-security.xml
        </param-value>
    </context-param>
    <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>
    </filter-mapping>



    <filter>
        <filter-name>NoCacheFilter</filter-name>
        <filter-class>filter.NoCacheFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>NoCacheFilter</filter-name>
        <url-pattern>/admin_side/*</url-pattern>
    </filter-mapping>


    <filter>
        <filter-name>FileUploadFilter</filter-name>
        <filter-class>com.ckfinder.connector.FileUploadFilter</filter-class>
        <init-param>
            <param-name>sessionCookieName</param-name>
            <param-value>JSESSIONID</param-value>
        </init-param>
        <init-param>
            <param-name>sessionParameterName</param-name>
            <param-value>jsessionid</param-value>
        </init-param>
    </filter>


    <filter-mapping>
        <filter-name>FileUploadFilter</filter-name>
        <url-pattern>
                    /ckfinder/core/connector/java/connector.java
     </url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>multipartFilter</filter-name>
        <filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>multipartFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <filter>
        <filter-name>httpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>httpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>


    <filter>
        <filter-name>openSessionInViewFilter</filter-name>
        <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
        <init-param>
            <param-name>singleSession</param-name>
            <param-value>false</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>openSessionInViewFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>





    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <listener>
        <description>ServletContextListener</description>
        <listener-class>listener.UnregisterDatabaseDrivers</listener-class>
    </listener>
    <servlet>
        <servlet-name>ConnectorServlet</servlet-name>
        <servlet-class>com.ckfinder.connector.ConnectorServlet</servlet-class>
        <init-param>
            <param-name>XMLConfig</param-name>
            <param-value>/WEB-INF/config.xml</param-value>
        </init-param>
        <init-param>
            <param-name>debug</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>ConnectorServlet</servlet-name>
        <url-pattern>
                    /ckfinder/core/connector/java/connector.java
            </url-pattern>
    </servlet-mapping>


     <listener>
        <listener-class>
          org.springframework.security.web.session.HttpSessionEventPublisher
        </listener-class>
      </listener>




    <error-page>
        <description>Missing login</description>
        <error-code>401</error-code>
        <location>/WEB-INF/jsp/admin_side/ErrorPage.jsp</location>
    </error-page>

    <error-page>
        <description>Forbidden directory listing</description>
        <error-code>403</error-code>
        <location>/WEB-INF/jsp/admin_side/ErrorPage.jsp</location>
    </error-page>

    <error-page>
        <description>Missing page</description>
        <error-code>404</error-code>
        <location>/WEB-INF/jsp/admin_side/ErrorPage.jsp</location>
    </error-page>

    <error-page>
        <description>Uncaught exception</description>
        <error-code>500</error-code>
        <location>/WEB-INF/jsp/admin_side/ErrorPage.jsp</location>
    </error-page>

    <error-page>
        <description>Unsupported servlet method</description>
        <error-code>503</error-code>
        <location>/WEB-INF/jsp/admin_side/ErrorPage.jsp</location>
    </error-page>



    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>*.htm</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>redirect.jsp</welcome-file>
    </welcome-file-list>
</web-app>
4

1 回答 1

2

除非只使用路径参数,否则处理常规 HTTP PUT 需要更多工作。

Spring 3.1 开始,HttpPutFormContentFilter可用于@RequestParamapplication/x-www-form-urlencoded数据工作:

ServletRequest.getParameter*()在 HTTP PUT 请求期间通过一系列方法使表单编码数据可用的过滤器。

Servlet 规范要求表单数据可用于 HTTP POST,但不能用于 HTTP PUT 请求。此过滤器拦截内容类型为“ application/x-www-form-urlencoded”的 HTTP PUT 请求,从请求正文中读取表单编码的内容,并包装 ServletRequest 以使表单数据可用作请求参数,就像它用于 HTTP POST 请求一样。

然而:这个过滤器消耗了请求的输入流,使得它对转换器不可用,比如FormHttpMessageConverter, like used for @RequestBody MultiValueMap<String, String>or HttpEntity<MultiValueMap<String, String>>。因此,一旦您在应用程序中配置了上述过滤器,当调用使用其他转换器的方法时,您将收到“IOException:流已关闭”,这些转换器也需要原始application/x-www-form-urlencodedPUT 数据。


或者,可以使用or手动完成所有操作:@RequestBodyHttpEntity<?>

@RequestMapping(value="ajax/UpdateUserRole", method=RequestMethod.PUT,
    produces = MediaType.TEXT_PLAIN_VALUE)
public @ResponseBody String updateUserRole(
    @RequestBody final MultiValueMap<String, String> data,
    final HttpServletResponse response) {
  Map<String, String> params = data.toSingleValueMap();
  String id = params.get("id");
  String a = params.get("a");
  String b = params.get("b");
  if(id == null || a == null || b == null) {
    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
    return null;
  }
  return "id = " + id;
}

另请参阅使用或使用的示例:WebDataBinder

public ResponseEntity<String> updateUserRole(
    final HttpEntity<MultiValueMap<String, String>> entity) {
  Map<String, String> params = entity.getBody().toSingleValueMap();
  String id = params.get("id");
  ...

请注意,对于测试,使用MockMvc mockMvc.perform(put(url).param(name, value))实际上也可以处理问题中的代码,即使它在 servlet 容器中会失败。但是 MockMvc 并没有在这样的 servlet 容器中运行,因此有点愚弄你。

MockMvc.param(name, value)也可以很好地与HttpPutFormContentFilter. 但是在使用 MockMvc 测试@RequestBodyor时HttpEntity<?>,还需要application/x-www-form-urlencoded手动创建任何 PUT 内容。喜欢:

mockMvc.perform(put(url).content("id=" + URLEncoder.encode(id, "UTF-8")
  + "&a=" + URLEncoder.encode(a, "UTF-8") + "&b=" + ...)

为了能够简单地使用.param(name, value),就像 GET 和 POST 一样,可以定义:

public static RequestPostProcessor convertParameters() {
  return new RequestPostProcessor() {
    @Override
    public MockHttpServletRequest postProcessRequest(
        final MockHttpServletRequest request) {
      if ("PUT".equalsIgnoreCase(request.getMethod()) {
        Map<String, String[]> params = request.getParameterMap();
        if (params != null) {
          StringBuilder content = new StringBuilder();
          for (Entry<String, String[]> es : params.entrySet()) {
            for (String value : es.getValue()) {
              try {
                content.append(URLEncoder.encode(es.getKey(), "UTF-8"))
                  .append("=")
                  .append(URLEncoder.encode(value, "UTF-8"))
                  .append("&");
              }
              catch (UnsupportedEncodingException e) {
                throw new IllegalArgumentException("UTF-8 not supported");
              }
            }
          }
          request.setParameters(new HashMap<String, String[]>());
          request.setContent(content.toString().getBytes());
          request.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE);
        }
      }
      return request;
    }
  };
}

...然后.with(convertParameters())在旁边使用.param(name, value)

mockMvc.perform(put(url)
    .with(convertParameters())
    .param("id", id).param("a", a).param("b", b) ...)

综上所述,简单地使用HttpPutFormContentFilter数据application/x-www-form-urlencoded确实让生活更轻松。

当浏览器发送的不是application/x-www-form-urlencoded数据,而是 JSON 等数据时,尝试映射到MultiValueMap将产生 415 Unsupported Media Type。相反,使用类似@RequestBody MyDTO dataHttpEntity<MyDTO> entityParsing JSON in Spring MVC using Jackson JSON中所述的内容。

于 2013-01-28T19:10:50.483 回答