Add support for HTTP PATCH method
The HTTP PATCH method is now supported whereever HTTP methods are used. Annotated controllers can be mapped to RequestMethod.PATCH. On the client side the RestTemplate execute(..) and exchange(..) methods can be used with HttpMethod.PATCH. In terms of HTTP client libraries, Apache HttpComponents HttpClient version 4.2 or later is required (see HTTPCLIENT-1191). The JDK HttpURLConnection does not support the HTTP PATCH method. Issue: SPR-7985
This commit is contained in:
parent
f05e2bc56f
commit
a0747458e7
|
|
@ -360,7 +360,7 @@ project('spring-web') {
|
||||||
compile("commons-fileupload:commons-fileupload:1.2", optional)
|
compile("commons-fileupload:commons-fileupload:1.2", optional)
|
||||||
runtime("commons-io:commons-io:1.3", optional)
|
runtime("commons-io:commons-io:1.3", optional)
|
||||||
compile("commons-httpclient:commons-httpclient:3.1", optional)
|
compile("commons-httpclient:commons-httpclient:3.1", optional)
|
||||||
compile("org.apache.httpcomponents:httpclient:4.1.1", optional)
|
compile("org.apache.httpcomponents:httpclient:4.2", optional)
|
||||||
compile("org.codehaus.jackson:jackson-mapper-asl:1.4.2", optional)
|
compile("org.codehaus.jackson:jackson-mapper-asl:1.4.2", optional)
|
||||||
compile("com.fasterxml.jackson.core:jackson-databind:2.0.1", optional)
|
compile("com.fasterxml.jackson.core:jackson-databind:2.0.1", optional)
|
||||||
compile("taglibs:standard:1.1.2", optional)
|
compile("taglibs:standard:1.1.2", optional)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2009 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -26,6 +26,6 @@ package org.springframework.http;
|
||||||
*/
|
*/
|
||||||
public enum HttpMethod {
|
public enum HttpMethod {
|
||||||
|
|
||||||
GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE
|
GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -141,6 +141,9 @@ public class CommonsClientHttpRequestFactory implements ClientHttpRequestFactory
|
||||||
return new PutMethod(uri);
|
return new PutMethod(uri);
|
||||||
case TRACE:
|
case TRACE:
|
||||||
return new TraceMethod(uri);
|
return new TraceMethod(uri);
|
||||||
|
case PATCH:
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"HTTP method PATCH not available before Apache HttpComponents HttpClient 4.2");
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
|
throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,12 +17,9 @@
|
||||||
package org.springframework.http.client;
|
package org.springframework.http.client;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
import org.springframework.beans.factory.DisposableBean;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.client.methods.HttpDelete;
|
import org.apache.http.client.methods.HttpDelete;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
|
@ -40,6 +37,10 @@ import org.apache.http.impl.client.DefaultHttpClient;
|
||||||
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
|
||||||
import org.apache.http.params.CoreConnectionPNames;
|
import org.apache.http.params.CoreConnectionPNames;
|
||||||
import org.apache.http.protocol.HttpContext;
|
import org.apache.http.protocol.HttpContext;
|
||||||
|
import org.springframework.beans.factory.DisposableBean;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link org.springframework.http.client.ClientHttpRequestFactory} implementation that uses
|
* {@link org.springframework.http.client.ClientHttpRequestFactory} implementation that uses
|
||||||
|
|
@ -155,11 +156,30 @@ public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequest
|
||||||
return new HttpPut(uri);
|
return new HttpPut(uri);
|
||||||
case TRACE:
|
case TRACE:
|
||||||
return new HttpTrace(uri);
|
return new HttpTrace(uri);
|
||||||
|
case PATCH:
|
||||||
|
return createHttpPatch(uri);
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
|
throw new IllegalArgumentException("Invalid HTTP method: " + httpMethod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HttpUriRequest createHttpPatch(URI uri) {
|
||||||
|
String className = "org.apache.http.client.methods.HttpPatch";
|
||||||
|
ClassLoader classloader = this.getClass().getClassLoader();
|
||||||
|
if (!ClassUtils.isPresent(className, classloader)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"HTTP method PATCH not available before Apache HttpComponents HttpClient 4.2");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Class<?> clazz = classloader.loadClass(className);
|
||||||
|
Constructor<?> constructor = clazz.getConstructor(URI.class);
|
||||||
|
return (HttpUriRequest) constructor.newInstance(uri);
|
||||||
|
}
|
||||||
|
catch (Throwable ex) {
|
||||||
|
throw new IllegalStateException("Unable to instantiate " + className, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Template method that allows for manipulating the {@link HttpUriRequest} before it is
|
* Template method that allows for manipulating the {@link HttpUriRequest} before it is
|
||||||
* returned as part of a {@link HttpComponentsClientHttpRequest}.
|
* returned as part of a {@link HttpComponentsClientHttpRequest}.
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -152,7 +152,7 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory
|
||||||
else {
|
else {
|
||||||
connection.setInstanceFollowRedirects(false);
|
connection.setInstanceFollowRedirects(false);
|
||||||
}
|
}
|
||||||
if ("PUT".equals(httpMethod) || "POST".equals(httpMethod)) {
|
if ("PUT".equals(httpMethod) || "POST".equals(httpMethod) || "PATCH".equals(httpMethod)) {
|
||||||
connection.setDoOutput(true);
|
connection.setDoOutput(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2009 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -21,7 +21,7 @@ import java.util.List;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception thrown when a client POSTs or PUTs content
|
* Exception thrown when a client POSTs, PUTs, or PATCHes content of a type
|
||||||
* not supported by request handler.
|
* not supported by request handler.
|
||||||
*
|
*
|
||||||
* @author Arjen Poutsma
|
* @author Arjen Poutsma
|
||||||
|
|
|
||||||
|
|
@ -271,7 +271,7 @@ public @interface RequestMapping {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The HTTP request methods to map to, narrowing the primary mapping:
|
* The HTTP request methods to map to, narrowing the primary mapping:
|
||||||
* GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE.
|
* GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
|
||||||
* <p><b>Supported at the type level as well as at the method level!</b>
|
* <p><b>Supported at the type level as well as at the method level!</b>
|
||||||
* When used at the type level, all method-level mappings inherit
|
* When used at the type level, all method-level mappings inherit
|
||||||
* this HTTP method restriction (i.e. the type-level restriction
|
* this HTTP method restriction (i.e. the type-level restriction
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2009 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -22,7 +22,7 @@ package org.springframework.web.bind.annotation;
|
||||||
* {@link RequestMapping} annotation.
|
* {@link RequestMapping} annotation.
|
||||||
*
|
*
|
||||||
* <p>Note that, by default, {@link org.springframework.web.servlet.DispatcherServlet}
|
* <p>Note that, by default, {@link org.springframework.web.servlet.DispatcherServlet}
|
||||||
* supports GET, HEAD, POST, PUT and DELETE only. DispatcherServlet will
|
* supports GET, HEAD, POST, PUT, PATCH and DELETE only. DispatcherServlet will
|
||||||
* process TRACE and OPTIONS with the default HttpServlet behavior unless
|
* process TRACE and OPTIONS with the default HttpServlet behavior unless
|
||||||
* explicitly told to dispatch those request types as well: Check out
|
* explicitly told to dispatch those request types as well: Check out
|
||||||
* the "dispatchOptionsRequest" and "dispatchTraceRequest" properties,
|
* the "dispatchOptionsRequest" and "dispatchTraceRequest" properties,
|
||||||
|
|
@ -36,6 +36,6 @@ package org.springframework.web.bind.annotation;
|
||||||
*/
|
*/
|
||||||
public enum RequestMethod {
|
public enum RequestMethod {
|
||||||
|
|
||||||
GET, HEAD, POST, PUT, DELETE, OPTIONS, TRACE
|
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -44,24 +44,24 @@ import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link javax.servlet.Filter} that makes form encoded data available through
|
* {@link javax.servlet.Filter} that makes form encoded data available through
|
||||||
* the {@code ServletRequest.getParameter*()} family of methods during HTTP PUT
|
* the {@code ServletRequest.getParameter*()} family of methods during HTTP PUT
|
||||||
* requests.
|
* or PATCH requests.
|
||||||
*
|
*
|
||||||
* <p>The Servlet spec requires form data to be available for HTTP POST but
|
* <p>The Servlet spec requires form data to be available for HTTP POST but
|
||||||
* not for HTTP PUT requests. This filter intercepts HTTP PUT requests where
|
* not for HTTP PUT or PATCH requests. This filter intercepts HTTP PUT and PATCH
|
||||||
* content type is {@code 'application/x-www-form-urlencoded'}, reads form
|
* requests where content type is {@code 'application/x-www-form-urlencoded'},
|
||||||
* encoded content from the body of the request, and wraps the ServletRequest
|
* reads form encoded content from the body of the request, and wraps the ServletRequest
|
||||||
* in order to make the form data available as request parameters just like
|
* in order to make the form data available as request parameters just like
|
||||||
* it is for HTTP POST requests.
|
* it is for HTTP POST requests.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @since 3.1
|
* @since 3.1
|
||||||
*/
|
*/
|
||||||
public class HttpPutFormContentFilter extends OncePerRequestFilter {
|
public class HttpPutFormContentFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final FormHttpMessageConverter formConverter = new XmlAwareFormHttpMessageConverter();
|
private final FormHttpMessageConverter formConverter = new XmlAwareFormHttpMessageConverter();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default character set to use for reading form data.
|
* The default character set to use for reading form data.
|
||||||
*/
|
*/
|
||||||
|
|
@ -73,7 +73,7 @@ public class HttpPutFormContentFilter extends OncePerRequestFilter {
|
||||||
protected void doFilterInternal(final HttpServletRequest request, HttpServletResponse response,
|
protected void doFilterInternal(final HttpServletRequest request, HttpServletResponse response,
|
||||||
FilterChain filterChain) throws ServletException, IOException {
|
FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
|
||||||
if ("PUT".equals(request.getMethod()) && isFormContentType(request)) {
|
if (("PUT".equals(request.getMethod()) || "PATCH".equals(request.getMethod())) && isFormContentType(request)) {
|
||||||
HttpInputMessage inputMessage = new ServletServerHttpRequest(request) {
|
HttpInputMessage inputMessage = new ServletServerHttpRequest(request) {
|
||||||
@Override
|
@Override
|
||||||
public InputStream getBody() throws IOException {
|
public InputStream getBody() throws IOException {
|
||||||
|
|
@ -102,7 +102,7 @@ public class HttpPutFormContentFilter extends OncePerRequestFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class HttpPutFormContentRequestWrapper extends HttpServletRequestWrapper {
|
private static class HttpPutFormContentRequestWrapper extends HttpServletRequestWrapper {
|
||||||
|
|
||||||
private MultiValueMap<String, String> formParameters;
|
private MultiValueMap<String, String> formParameters;
|
||||||
|
|
||||||
public HttpPutFormContentRequestWrapper(HttpServletRequest request, MultiValueMap<String, String> parameters) {
|
public HttpPutFormContentRequestWrapper(HttpServletRequest request, MultiValueMap<String, String> parameters) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -68,6 +68,7 @@ public abstract class AbstractHttpRequestFactoryTestCase {
|
||||||
jettyContext.addServlet(new ServletHolder(new MethodServlet("OPTIONS")), "/methods/options");
|
jettyContext.addServlet(new ServletHolder(new MethodServlet("OPTIONS")), "/methods/options");
|
||||||
jettyContext.addServlet(new ServletHolder(new PostServlet()), "/methods/post");
|
jettyContext.addServlet(new ServletHolder(new PostServlet()), "/methods/post");
|
||||||
jettyContext.addServlet(new ServletHolder(new MethodServlet("PUT")), "/methods/put");
|
jettyContext.addServlet(new ServletHolder(new MethodServlet("PUT")), "/methods/put");
|
||||||
|
jettyContext.addServlet(new ServletHolder(new MethodServlet("PATCH")), "/methods/patch");
|
||||||
jettyServer.start();
|
jettyServer.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,7 +161,7 @@ public abstract class AbstractHttpRequestFactoryTestCase {
|
||||||
assertHttpMethod("delete", HttpMethod.DELETE);
|
assertHttpMethod("delete", HttpMethod.DELETE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertHttpMethod(String path, HttpMethod method) throws Exception {
|
protected void assertHttpMethod(String path, HttpMethod method) throws Exception {
|
||||||
ClientHttpResponse response = null;
|
ClientHttpResponse response = null;
|
||||||
try {
|
try {
|
||||||
ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/methods/" + path), method);
|
ClientHttpRequest request = factory.createRequest(new URI(baseUrl + "/methods/" + path), method);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,10 +16,26 @@
|
||||||
|
|
||||||
package org.springframework.http.client;
|
package org.springframework.http.client;
|
||||||
|
|
||||||
|
import java.net.ProtocolException;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
||||||
public class BufferedSimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
|
public class BufferedSimpleHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ClientHttpRequestFactory createRequestFactory() {
|
protected ClientHttpRequestFactory createRequestFactory() {
|
||||||
return new SimpleClientHttpRequestFactory();
|
return new SimpleClientHttpRequestFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void httpMethods() throws Exception {
|
||||||
|
try {
|
||||||
|
assertHttpMethod("patch", HttpMethod.PATCH);
|
||||||
|
}
|
||||||
|
catch (ProtocolException ex) {
|
||||||
|
// Currently HttpURLConnection does not support HTTP PATCH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2009 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,9 +16,10 @@
|
||||||
|
|
||||||
package org.springframework.http.client;
|
package org.springframework.http.client;
|
||||||
|
|
||||||
import org.springframework.http.client.AbstractHttpRequestFactoryTestCase;
|
import java.net.URI;
|
||||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
|
||||||
import org.springframework.http.client.CommonsClientHttpRequestFactory;
|
import org.junit.Test;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
||||||
public class CommonsHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
|
public class CommonsHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
|
||||||
|
|
||||||
|
|
@ -26,4 +27,10 @@ public class CommonsHttpRequestFactoryTests extends AbstractHttpRequestFactoryTe
|
||||||
protected ClientHttpRequestFactory createRequestFactory() {
|
protected ClientHttpRequestFactory createRequestFactory() {
|
||||||
return new CommonsClientHttpRequestFactory();
|
return new CommonsClientHttpRequestFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected=IllegalArgumentException.class)
|
||||||
|
public void httpPatch() throws Exception {
|
||||||
|
factory.createRequest(new URI(baseUrl + "/methods/PATCH"), HttpMethod.PATCH);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,10 +16,19 @@
|
||||||
|
|
||||||
package org.springframework.http.client;
|
package org.springframework.http.client;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
||||||
public class HttpComponentsClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
|
public class HttpComponentsClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryTestCase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ClientHttpRequestFactory createRequestFactory() {
|
protected ClientHttpRequestFactory createRequestFactory() {
|
||||||
return new HttpComponentsClientHttpRequestFactory();
|
return new HttpComponentsClientHttpRequestFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void httpMethods() throws Exception {
|
||||||
|
assertHttpMethod("patch", HttpMethod.PATCH);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -29,25 +29,26 @@ import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.mock.web.MockFilterChain;
|
import org.springframework.mock.web.MockFilterChain;
|
||||||
import org.springframework.mock.web.MockHttpServletRequest;
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.MockHttpServletResponse;
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test fixture for {@link HttpPutFormContentFilter}.
|
* Test fixture for {@link HttpPutFormContentFilter}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
public class HttpPutFormContentFilterTests {
|
public class HttpPutFormContentFilterTests {
|
||||||
|
|
||||||
private HttpPutFormContentFilter filter;
|
private HttpPutFormContentFilter filter;
|
||||||
|
|
||||||
private MockHttpServletRequest request;
|
private MockHttpServletRequest request;
|
||||||
|
|
||||||
private MockHttpServletResponse response;
|
private MockHttpServletResponse response;
|
||||||
|
|
||||||
private MockFilterChain filterChain;
|
private MockFilterChain filterChain;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
filter = new HttpPutFormContentFilter();
|
filter = new HttpPutFormContentFilter();
|
||||||
|
|
@ -59,14 +60,18 @@ public class HttpPutFormContentFilterTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void wrapPutOnly() throws Exception {
|
public void wrapPutAndPatchOnly() throws Exception {
|
||||||
request.setContent("".getBytes("ISO-8859-1"));
|
request.setContent("".getBytes("ISO-8859-1"));
|
||||||
String[] methods = new String[] {"GET", "POST", "DELETE", "HEAD", "OPTIONS", "TRACE"};
|
for (HttpMethod method : HttpMethod.values()) {
|
||||||
for (String method : methods) {
|
request.setMethod(method.name());
|
||||||
request.setMethod(method);
|
|
||||||
filterChain = new MockFilterChain();
|
filterChain = new MockFilterChain();
|
||||||
filter.doFilter(request, response, filterChain);
|
filter.doFilter(request, response, filterChain);
|
||||||
assertSame("Should not wrap for HTTP method " + method, request, filterChain.getRequest());
|
if (method.equals(HttpMethod.PUT) || method.equals(HttpMethod.PATCH)) {
|
||||||
|
assertNotSame("Should wrap HTTP method " + method, request, filterChain.getRequest());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertSame("Should not wrap for HTTP method " + method, request, filterChain.getRequest());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -81,31 +86,31 @@ public class HttpPutFormContentFilterTests {
|
||||||
assertSame("Should not wrap for content type " + contentType, request, filterChain.getRequest());
|
assertSame("Should not wrap for content type " + contentType, request, filterChain.getRequest());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getParameter() throws Exception {
|
public void getParameter() throws Exception {
|
||||||
request.setContent("name=value".getBytes("ISO-8859-1"));
|
request.setContent("name=value".getBytes("ISO-8859-1"));
|
||||||
filter.doFilter(request, response, filterChain);
|
filter.doFilter(request, response, filterChain);
|
||||||
|
|
||||||
assertEquals("value", filterChain.getRequest().getParameter("name"));
|
assertEquals("value", filterChain.getRequest().getParameter("name"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getParameterFromQueryString() throws Exception {
|
public void getParameterFromQueryString() throws Exception {
|
||||||
request.addParameter("name", "value1");
|
request.addParameter("name", "value1");
|
||||||
request.setContent("name=value2".getBytes("ISO-8859-1"));
|
request.setContent("name=value2".getBytes("ISO-8859-1"));
|
||||||
filter.doFilter(request, response, filterChain);
|
filter.doFilter(request, response, filterChain);
|
||||||
|
|
||||||
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
||||||
assertEquals("Query string parameters should be listed ahead of form parameters",
|
assertEquals("Query string parameters should be listed ahead of form parameters",
|
||||||
"value1", filterChain.getRequest().getParameter("name"));
|
"value1", filterChain.getRequest().getParameter("name"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getParameterNullValue() throws Exception {
|
public void getParameterNullValue() throws Exception {
|
||||||
request.setContent("name=value".getBytes("ISO-8859-1"));
|
request.setContent("name=value".getBytes("ISO-8859-1"));
|
||||||
filter.doFilter(request, response, filterChain);
|
filter.doFilter(request, response, filterChain);
|
||||||
|
|
||||||
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
||||||
assertNull(filterChain.getRequest().getParameter("noSuchParam"));
|
assertNull(filterChain.getRequest().getParameter("noSuchParam"));
|
||||||
}
|
}
|
||||||
|
|
@ -115,27 +120,27 @@ public class HttpPutFormContentFilterTests {
|
||||||
request.addParameter("name1", "value1");
|
request.addParameter("name1", "value1");
|
||||||
request.addParameter("name2", "value2");
|
request.addParameter("name2", "value2");
|
||||||
request.setContent("name1=value1&name3=value3&name4=value4".getBytes("ISO-8859-1"));
|
request.setContent("name1=value1&name3=value3&name4=value4".getBytes("ISO-8859-1"));
|
||||||
|
|
||||||
filter.doFilter(request, response, filterChain);
|
filter.doFilter(request, response, filterChain);
|
||||||
List<String> names = Collections.list(filterChain.getRequest().getParameterNames());
|
List<String> names = Collections.list(filterChain.getRequest().getParameterNames());
|
||||||
|
|
||||||
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
||||||
assertEquals(Arrays.asList("name1", "name2", "name3", "name4"), names);
|
assertEquals(Arrays.asList("name1", "name2", "name3", "name4"), names);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getParameterValues() throws Exception {
|
public void getParameterValues() throws Exception {
|
||||||
request.addParameter("name", "value1");
|
request.addParameter("name", "value1");
|
||||||
request.addParameter("name", "value2");
|
request.addParameter("name", "value2");
|
||||||
request.setContent("name=value3&name=value4".getBytes("ISO-8859-1"));
|
request.setContent("name=value3&name=value4".getBytes("ISO-8859-1"));
|
||||||
|
|
||||||
filter.doFilter(request, response, filterChain);
|
filter.doFilter(request, response, filterChain);
|
||||||
String[] values = filterChain.getRequest().getParameterValues("name");
|
String[] values = filterChain.getRequest().getParameterValues("name");
|
||||||
|
|
||||||
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
||||||
assertArrayEquals(new String[]{"value1", "value2", "value3", "value4"}, values);
|
assertArrayEquals(new String[]{"value1", "value2", "value3", "value4"}, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getParameterValuesFromQueryString() throws Exception {
|
public void getParameterValuesFromQueryString() throws Exception {
|
||||||
request.addParameter("name", "value1");
|
request.addParameter("name", "value1");
|
||||||
|
|
@ -148,33 +153,33 @@ public class HttpPutFormContentFilterTests {
|
||||||
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
||||||
assertArrayEquals(new String[]{"value1", "value2"}, values);
|
assertArrayEquals(new String[]{"value1", "value2"}, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getParameterValuesFromFormContent() throws Exception {
|
public void getParameterValuesFromFormContent() throws Exception {
|
||||||
request.addParameter("name", "value1");
|
request.addParameter("name", "value1");
|
||||||
request.addParameter("name", "value2");
|
request.addParameter("name", "value2");
|
||||||
request.setContent("anotherName=anotherValue".getBytes("ISO-8859-1"));
|
request.setContent("anotherName=anotherValue".getBytes("ISO-8859-1"));
|
||||||
|
|
||||||
filter.doFilter(request, response, filterChain);
|
filter.doFilter(request, response, filterChain);
|
||||||
String[] values = filterChain.getRequest().getParameterValues("anotherName");
|
String[] values = filterChain.getRequest().getParameterValues("anotherName");
|
||||||
|
|
||||||
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
||||||
assertArrayEquals(new String[]{"anotherValue"}, values);
|
assertArrayEquals(new String[]{"anotherValue"}, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getParameterValuesInvalidName() throws Exception {
|
public void getParameterValuesInvalidName() throws Exception {
|
||||||
request.addParameter("name", "value1");
|
request.addParameter("name", "value1");
|
||||||
request.addParameter("name", "value2");
|
request.addParameter("name", "value2");
|
||||||
request.setContent("anotherName=anotherValue".getBytes("ISO-8859-1"));
|
request.setContent("anotherName=anotherValue".getBytes("ISO-8859-1"));
|
||||||
|
|
||||||
filter.doFilter(request, response, filterChain);
|
filter.doFilter(request, response, filterChain);
|
||||||
String[] values = filterChain.getRequest().getParameterValues("noSuchParameter");
|
String[] values = filterChain.getRequest().getParameterValues("noSuchParameter");
|
||||||
|
|
||||||
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
assertNotSame("Request not wrapped", request, filterChain.getRequest());
|
||||||
assertNull(values);
|
assertNull(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getParameterMap() throws Exception {
|
public void getParameterMap() throws Exception {
|
||||||
request.addParameter("name", "value1");
|
request.addParameter("name", "value1");
|
||||||
|
|
@ -189,5 +194,5 @@ public class HttpPutFormContentFilterTests {
|
||||||
assertArrayEquals(new String[] {"value1", "value2", "value3"}, parameters.get("name"));
|
assertArrayEquals(new String[] {"value1", "value2", "value3"}, parameters.get("name"));
|
||||||
assertArrayEquals(new String[] {"value4"}, parameters.get("name4"));
|
assertArrayEquals(new String[] {"value4"}, parameters.get("name4"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
import org.springframework.util.ObjectUtils;
|
import org.springframework.util.ObjectUtils;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
import org.springframework.web.context.ConfigurableWebApplicationContext;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
import org.springframework.web.context.request.RequestAttributes;
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
|
@ -767,6 +768,25 @@ public abstract class FrameworkServlet extends HttpServletBean {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Override the parent class implementation in order to intercept PATCH
|
||||||
|
* requests.
|
||||||
|
*
|
||||||
|
* @see #doPatch(HttpServletRequest, HttpServletResponse)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void service(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
String method = request.getMethod();
|
||||||
|
if (method.equalsIgnoreCase(RequestMethod.PATCH.name())) {
|
||||||
|
doPatch(request, response);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.service(request, response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate GET requests to processRequest/doService.
|
* Delegate GET requests to processRequest/doService.
|
||||||
* <p>Will also be invoked by HttpServlet's default implementation of <code>doHead</code>,
|
* <p>Will also be invoked by HttpServlet's default implementation of <code>doHead</code>,
|
||||||
|
|
@ -803,6 +823,16 @@ public abstract class FrameworkServlet extends HttpServletBean {
|
||||||
processRequest(request, response);
|
processRequest(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate PATCH requests to {@link #processRequest}.
|
||||||
|
* @see #doService
|
||||||
|
*/
|
||||||
|
protected final void doPatch(HttpServletRequest request, HttpServletResponse response)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
processRequest(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate DELETE requests to {@link #processRequest}.
|
* Delegate DELETE requests to {@link #processRequest}.
|
||||||
* @see #doService
|
* @see #doService
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2011 the original author or authors.
|
* Copyright 2002-2012 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -144,20 +144,20 @@ import org.springframework.web.servlet.view.InternalResourceViewResolver;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The origin of this test class is {@link ServletAnnotationControllerHandlerMethodTests}.
|
* The origin of this test class is {@link ServletAnnotationControllerHandlerMethodTests}.
|
||||||
*
|
*
|
||||||
* Tests in this class run against the {@link HandlerMethod} infrastructure:
|
* Tests in this class run against the {@link HandlerMethod} infrastructure:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>RequestMappingHandlerMapping
|
* <li>RequestMappingHandlerMapping
|
||||||
* <li>RequestMappingHandlerAdapter
|
* <li>RequestMappingHandlerAdapter
|
||||||
* <li>ExceptionHandlerExceptionResolver
|
* <li>ExceptionHandlerExceptionResolver
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>Rather than against the existing infrastructure:
|
* <p>Rather than against the existing infrastructure:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>DefaultAnnotationHandlerMapping
|
* <li>DefaultAnnotationHandlerMapping
|
||||||
* <li>AnnotationMethodHandlerAdapter
|
* <li>AnnotationMethodHandlerAdapter
|
||||||
* <li>AnnotationMethodHandlerExceptionResolver
|
* <li>AnnotationMethodHandlerExceptionResolver
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
*/
|
*/
|
||||||
|
|
@ -249,7 +249,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
context.registerBeanDefinition("ppc", ppc);
|
context.registerBeanDefinition("ppc", ppc);
|
||||||
}
|
}
|
||||||
}, DefaultExpressionValueParamController.class);
|
}, DefaultExpressionValueParamController.class);
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myPath.do");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/myApp/myPath.do");
|
||||||
request.setContextPath("/myApp");
|
request.setContextPath("/myApp");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
|
@ -656,7 +656,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
final MockServletContext servletContext = new MockServletContext();
|
final MockServletContext servletContext = new MockServletContext();
|
||||||
final MockServletConfig servletConfig = new MockServletConfig(servletContext);
|
final MockServletConfig servletConfig = new MockServletConfig(servletContext);
|
||||||
|
|
||||||
WebApplicationContext webAppContext =
|
WebApplicationContext webAppContext =
|
||||||
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
|
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
|
||||||
public void initialize(GenericWebApplicationContext wac) {
|
public void initialize(GenericWebApplicationContext wac) {
|
||||||
wac.setServletContext(servletContext);
|
wac.setServletContext(servletContext);
|
||||||
|
|
@ -705,7 +705,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
getServlet().service(request, response);
|
getServlet().service(request, response);
|
||||||
assertEquals("mySurpriseView", response.getContentAsString());
|
assertEquals("mySurpriseView", response.getContentAsString());
|
||||||
|
|
||||||
MyParameterDispatchingController deserialized =
|
MyParameterDispatchingController deserialized =
|
||||||
(MyParameterDispatchingController) SerializationTestUtils.serializeAndDeserialize(
|
(MyParameterDispatchingController) SerializationTestUtils.serializeAndDeserialize(
|
||||||
webAppContext.getBean(MyParameterDispatchingController.class.getSimpleName()));
|
webAppContext.getBean(MyParameterDispatchingController.class.getSimpleName()));
|
||||||
assertNotNull(deserialized.request);
|
assertNotNull(deserialized.request);
|
||||||
|
|
@ -813,6 +813,21 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
assertEquals(requestBody, response.getContentAsString());
|
assertEquals(requestBody, response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void httpPatch() throws ServletException, IOException {
|
||||||
|
initServletWithControllers(RequestResponseBodyController.class);
|
||||||
|
|
||||||
|
MockHttpServletRequest request = new MockHttpServletRequest("PATCH", "/something");
|
||||||
|
String requestBody = "Hello world!";
|
||||||
|
request.setContent(requestBody.getBytes("UTF-8"));
|
||||||
|
request.addHeader("Content-Type", "text/plain; charset=utf-8");
|
||||||
|
request.addHeader("Accept", "text/*, */*");
|
||||||
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
|
getServlet().service(request, response);
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
assertEquals(requestBody, response.getContentAsString());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void responseBodyNoAcceptableMediaType() throws ServletException, IOException {
|
public void responseBodyNoAcceptableMediaType() throws ServletException, IOException {
|
||||||
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
|
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
|
||||||
|
|
@ -1248,7 +1263,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
assertEquals("Content-Type=[text/html],Custom-Header=[value21,value22]", response.getContentAsString());
|
assertEquals("Content-Type=[text/html],Custom-Header=[value21,value22]", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestMappingInterface() throws Exception {
|
public void requestMappingInterface() throws Exception {
|
||||||
initServletWithControllers(IMyControllerImpl.class);
|
initServletWithControllers(IMyControllerImpl.class);
|
||||||
|
|
@ -1298,7 +1313,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
assertEquals("handle", response.getContentAsString());
|
assertEquals("handle", response.getContentAsString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void trailingSlash() throws Exception {
|
public void trailingSlash() throws Exception {
|
||||||
initServletWithControllers(TrailingSlashController.class);
|
initServletWithControllers(TrailingSlashController.class);
|
||||||
|
|
@ -1444,7 +1459,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
getServlet().service(request, response);
|
getServlet().service(request, response);
|
||||||
|
|
||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
assertEquals("home", response.getForwardedUrl());
|
assertEquals("home", response.getForwardedUrl());
|
||||||
|
|
||||||
|
|
@ -1462,11 +1477,11 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
request.addHeader("Accept", "application/json");
|
request.addHeader("Accept", "application/json");
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
getServlet().service(request, response);
|
getServlet().service(request, response);
|
||||||
|
|
||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
assertEquals("application/json", response.getHeader("Content-Type"));
|
assertEquals("application/json", response.getHeader("Content-Type"));
|
||||||
assertEquals("homeJson", response.getContentAsString());
|
assertEquals("homeJson", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void redirectAttribute() throws Exception {
|
public void redirectAttribute() throws Exception {
|
||||||
|
|
@ -1479,7 +1494,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
|
|
||||||
// POST -> bind error
|
// POST -> bind error
|
||||||
getServlet().service(request, response);
|
getServlet().service(request, response);
|
||||||
|
|
||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
assertEquals("messages/new", response.getForwardedUrl());
|
assertEquals("messages/new", response.getForwardedUrl());
|
||||||
assertTrue(RequestContextUtils.getOutputFlashMap(request).isEmpty());
|
assertTrue(RequestContextUtils.getOutputFlashMap(request).isEmpty());
|
||||||
|
|
@ -1516,20 +1531,20 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
context.registerBeanDefinition("controller", beanDef);
|
context.registerBeanDefinition("controller", beanDef);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
|
||||||
request.addParameter("param", "1");
|
request.addParameter("param", "1");
|
||||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||||
getServlet().service(request, response);
|
getServlet().service(request, response);
|
||||||
|
|
||||||
assertEquals("count:3", response.getContentAsString());
|
assertEquals("count:3", response.getContentAsString());
|
||||||
|
|
||||||
response = new MockHttpServletResponse();
|
response = new MockHttpServletResponse();
|
||||||
getServlet().service(request, response);
|
getServlet().service(request, response);
|
||||||
|
|
||||||
assertEquals("count:3", response.getContentAsString());
|
assertEquals("count:3", response.getContentAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Controllers
|
* Controllers
|
||||||
*/
|
*/
|
||||||
|
|
@ -2335,6 +2350,12 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
public String handle(@RequestBody String body) throws IOException {
|
public String handle(@RequestBody String body) throws IOException {
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/something", method = RequestMethod.PATCH)
|
||||||
|
@ResponseBody
|
||||||
|
public String handlePartialUpdate(@RequestBody String content) throws IOException {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
|
|
@ -2366,7 +2387,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
|
|
||||||
@XmlRootElement
|
@XmlRootElement
|
||||||
public static class A {
|
public static class A {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@XmlRootElement
|
@XmlRootElement
|
||||||
|
|
@ -2729,7 +2750,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
public void root(Writer writer) throws IOException {
|
public void root(Writer writer) throws IOException {
|
||||||
writer.write("root");
|
writer.write("root");
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "/{templatePath}/", method = RequestMethod.GET)
|
@RequestMapping(value = "/{templatePath}/", method = RequestMethod.GET)
|
||||||
public void templatePath(Writer writer) throws IOException {
|
public void templatePath(Writer writer) throws IOException {
|
||||||
writer.write("templatePath");
|
writer.write("templatePath");
|
||||||
|
|
@ -2839,7 +2860,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
static class HeadersConditionController {
|
static class HeadersConditionController {
|
||||||
|
|
||||||
@RequestMapping(value = "/", method = RequestMethod.GET)
|
@RequestMapping(value = "/", method = RequestMethod.GET)
|
||||||
public String home() {
|
public String home() {
|
||||||
return "home";
|
return "home";
|
||||||
|
|
@ -2887,7 +2908,7 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
public void initBinder(WebDataBinder dataBinder) {
|
public void initBinder(WebDataBinder dataBinder) {
|
||||||
this.count++;
|
this.count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ModelAttribute
|
@ModelAttribute
|
||||||
public void populate(Model model) {
|
public void populate(Model model) {
|
||||||
this.count++;
|
this.count++;
|
||||||
|
|
@ -2899,16 +2920,16 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
writer.write("count:" + this.count);
|
writer.write("count:" + this.count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test cases deleted from the original SevletAnnotationControllerTests:
|
// Test cases deleted from the original SevletAnnotationControllerTests:
|
||||||
|
|
||||||
// @Ignore("Controller interface => no method-level @RequestMapping annotation")
|
// @Ignore("Controller interface => no method-level @RequestMapping annotation")
|
||||||
// public void standardHandleMethod() throws Exception {
|
// public void standardHandleMethod() throws Exception {
|
||||||
|
|
||||||
// @Ignore("ControllerClassNameHandlerMapping")
|
// @Ignore("ControllerClassNameHandlerMapping")
|
||||||
// public void emptyRequestMapping() throws Exception {
|
// public void emptyRequestMapping() throws Exception {
|
||||||
|
|
||||||
// @Ignore("Controller interface => no method-level @RequestMapping annotation")
|
// @Ignore("Controller interface => no method-level @RequestMapping annotation")
|
||||||
// public void proxiedStandardHandleMethod() throws Exception {
|
// public void proxiedStandardHandleMethod() throws Exception {
|
||||||
|
|
||||||
// @Ignore("ServletException no longer thrown for unmatched parameter constraints")
|
// @Ignore("ServletException no longer thrown for unmatched parameter constraints")
|
||||||
|
|
@ -2919,10 +2940,10 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
|
||||||
|
|
||||||
// @Ignore("Method name dispatching")
|
// @Ignore("Method name dispatching")
|
||||||
// public void methodNameDispatchingControllerWithSuffix() throws Exception {
|
// public void methodNameDispatchingControllerWithSuffix() throws Exception {
|
||||||
|
|
||||||
// @Ignore("ControllerClassNameHandlerMapping")
|
// @Ignore("ControllerClassNameHandlerMapping")
|
||||||
// public void controllerClassNamePlusMethodNameDispatchingController() throws Exception {
|
// public void controllerClassNamePlusMethodNameDispatchingController() throws Exception {
|
||||||
|
|
||||||
// @Ignore("Method name dispatching")
|
// @Ignore("Method name dispatching")
|
||||||
// public void postMethodNameDispatchingController() throws Exception {
|
// public void postMethodNameDispatchingController() throws Exception {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ Changes in version 3.2 M2
|
||||||
* add JacksonObjectMapperFactoryBean for configuring a Jackson ObjectMapper in XML
|
* add JacksonObjectMapperFactoryBean for configuring a Jackson ObjectMapper in XML
|
||||||
* infer return type of parameterized factory methods (SPR-9493)
|
* infer return type of parameterized factory methods (SPR-9493)
|
||||||
* add ContentNegotiationManager/ContentNegotiationStrategy to resolve requested media types
|
* add ContentNegotiationManager/ContentNegotiationStrategy to resolve requested media types
|
||||||
|
* add support for the HTTP PATCH method
|
||||||
|
|
||||||
Changes in version 3.2 M1 (2012-05-28)
|
Changes in version 3.2 M1 (2012-05-28)
|
||||||
--------------------------------------
|
--------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1975,7 +1975,7 @@ public class EditPetForm {
|
||||||
<literal>ServletRequest.getParameter*()</literal> family of methods to
|
<literal>ServletRequest.getParameter*()</literal> family of methods to
|
||||||
support form field access only for HTTP POST, not for HTTP PUT.</para>
|
support form field access only for HTTP POST, not for HTTP PUT.</para>
|
||||||
|
|
||||||
<para>To support HTTP PUT requests, the <literal>spring-web</literal>
|
<para>To support HTTP PUT and PATCH requests, the <literal>spring-web</literal>
|
||||||
module provides the filter
|
module provides the filter
|
||||||
<classname>HttpPutFormContentFilter</classname>, which can be
|
<classname>HttpPutFormContentFilter</classname>, which can be
|
||||||
configured in <filename>web.xml</filename>:</para>
|
configured in <filename>web.xml</filename>:</para>
|
||||||
|
|
@ -1995,7 +1995,7 @@ public class EditPetForm {
|
||||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||||
</servlet></programlisting>
|
</servlet></programlisting>
|
||||||
|
|
||||||
<para>The above filter intercepts HTTP PUT requests with content type
|
<para>The above filter intercepts HTTP PUT and PATCH requests with content type
|
||||||
<literal>application/x-www-form-urlencoded</literal>, reads the form
|
<literal>application/x-www-form-urlencoded</literal>, reads the form
|
||||||
data from the body of the request, and wraps the
|
data from the body of the request, and wraps the
|
||||||
<classname>ServletRequest</classname> in order to make the form data
|
<classname>ServletRequest</classname> in order to make the form data
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue