在Spring 3.2中使用AJAX的PUT方法不起作用

我正在尝试使用以下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’不受支持

description对于请求的资源,不允许使用指定的HTTP方法(不支持请求方法’GET’)。

Apache Tomcat / 6.0.26

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


如果需要查看dispatcher-servlet.xml文件,则完整内容如下所示。

                atom=application/atom+xml html=text/html json=application/json *=*/*          fileUploadingFailure        indexController       

如何在Spring 3.2中使GETPOST以外的HTTP方法工作?


编辑:

根据以下注释,以下是我的整个web.xml文件。

    contextConfigLocation  /WEB-INF/applicationContext.xml /WEB-INF/spring-security.xml    springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy   springSecurityFilterChain /*   NoCacheFilter filter.NoCacheFilter   NoCacheFilter /admin_side/*   FileUploadFilter com.ckfinder.connector.FileUploadFilter  sessionCookieName JSESSIONID   sessionParameterName jsessionid    FileUploadFilter  /ckfinder/core/connector/java/connector.java    multipartFilter org.springframework.web.multipart.support.MultipartFilter   multipartFilter /*   httpMethodFilter org.springframework.web.filter.HiddenHttpMethodFilter   httpMethodFilter /*   openSessionInViewFilter org.springframework.orm.hibernate3.support.OpenSessionInViewFilter  singleSession false    openSessionInViewFilter /*   org.springframework.web.context.ContextLoaderListener   ServletContextListener listener.UnregisterDatabaseDrivers   ConnectorServlet com.ckfinder.connector.ConnectorServlet  XMLConfig /WEB-INF/config.xml   debug false  1   ConnectorServlet  /ckfinder/core/connector/java/connector.java     org.springframework.security.web.session.HttpSessionEventPublisher    Missing login 401 /WEB-INF/jsp/admin_side/ErrorPage.jsp   Forbidden directory listing 403 /WEB-INF/jsp/admin_side/ErrorPage.jsp   Missing page 404 /WEB-INF/jsp/admin_side/ErrorPage.jsp   Uncaught exception 500 /WEB-INF/jsp/admin_side/ErrorPage.jsp   Unsupported servlet method 503 /WEB-INF/jsp/admin_side/ErrorPage.jsp   dispatcher org.springframework.web.servlet.DispatcherServlet 2   dispatcher *.htm    30    redirect.jsp   

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

从 Spring 3.1开始, HttpPutFormContentFilter可用于使@RequestParam适用于application/x-www-form-urlencoded数据:

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

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

但是:此filter使用请求的输入流,使其不能用于FormHttpMessageConverter类的转换器,例如用于@RequestBody MultiValueMapHttpEntity> 。 因此,一旦在应用程序中配置了上述filter,在调用使用其他转换器的方法时,您将获得“IOException:stream closed”,这些转换器也需要原始application/x-www-form-urlencoded PUT数据。

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

 @RequestMapping(value="ajax/UpdateUserRole", method=RequestMethod.PUT, produces = MediaType.TEXT_PLAIN_VALUE) public @ResponseBody String updateUserRole( @RequestBody final MultiValueMap data, final HttpServletResponse response) { Map 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 updateUserRole( final HttpEntity> entity) { Map params = entity.getBody().toSingleValueMap(); String id = params.get("id"); ... 

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

MockMvc的.param .param(name, value)也适用于HttpPutFormContentFilter 。 但是当使用MockMvc测试@RequestBodyHttpEntity ,还需要手动创建任何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 params = request.getParameterMap(); if (params != null) { StringBuilder content = new StringBuilder(); for (Entry 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()); request.setContent(content.toString().getBytes()); request.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE); } } return request; } }; } 

…然后在.with(convertParameters()) .param(name, value)旁边使用.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 entity如使用Jackson JSON在Spring MVC中解析JSON中所述 。