diff --git a/spring-web/src/main/java/org/springframework/http/ResponseEntity.java b/spring-web/src/main/java/org/springframework/http/ResponseEntity.java index 56fa4c1ba5..1ee8d339a6 100644 --- a/spring-web/src/main/java/org/springframework/http/ResponseEntity.java +++ b/spring-web/src/main/java/org/springframework/http/ResponseEntity.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,28 +16,47 @@ package org.springframework.http; +import java.net.URI; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + import org.springframework.util.MultiValueMap; import org.springframework.util.ObjectUtils; /** * Extension of {@link HttpEntity} that adds a {@link HttpStatus} status code. + * Used in {@code RestTemplate} as well {@code @Controller} methods. * - *

Returned by {@link org.springframework.web.client.RestTemplate#getForEntity}: + *

In {@code RestTemplate}, this class is returned by + * {@link org.springframework.web.client.RestTemplate#getForEntity getForEntity()} and + * {@link org.springframework.web.client.RestTemplate#exchange exchange()}: *

  * ResponseEntity<String> entity = template.getForEntity("http://example.com", String.class);
  * String body = entity.getBody();
  * MediaType contentType = entity.getHeaders().getContentType();
  * HttpStatus statusCode = entity.getStatusCode();
  * 
- *

Can also be used in Spring MVC, as a return value from a @Controller method: + * + *

Can also be used in Spring MVC, as the return value from a @Controller method: *

  * @RequestMapping("/handle")
  * public ResponseEntity<String> handle() {
+ *   URI location = ...;
  *   HttpHeaders responseHeaders = new HttpHeaders();
+ *   responseHeaders.setLocation(location);
  *   responseHeaders.set("MyResponseHeader", "MyValue");
  *   return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED);
  * }
  * 
+ * Or, by using the static convenience methods: + *
+ * @RequestMapping("/handle")
+ * public ResponseEntity<String> handle() {
+ *   URI location = ...;
+ *   return ResponseEntity.created(location).header("MyResponseHeader", "MyValue").body("Hello World");
+ * }
+ * 
* * @author Arjen Poutsma * @since 3.0.2 @@ -136,4 +155,238 @@ public class ResponseEntity extends HttpEntity { return builder.toString(); } + // Static builder methods + + /** + * Creates a new response entity builder with the given status. + * @param status the response status + * @return the new response entity builder + */ + public static BodyBuilder status(HttpStatus status) { + return new DefaultBuilder(status); + } + + /** + * Creates a new response entity builder with the given status. + * @param status the response status + * @return the new response entity builder + */ + public static BodyBuilder status(int status) { + return status(HttpStatus.valueOf(status)); + } + + /** + * Creates a new response entity builder with the status set to + * {@linkplain HttpStatus#OK OK}. + * @return the new response entity builder + */ + public static BodyBuilder ok() { + return status(HttpStatus.OK); + } + + /** + * Creates a new response entity with the given body and the status set to + * {@linkplain HttpStatus#OK OK}. + * @return the new response entity + */ + public static ResponseEntity ok(T body) { + BodyBuilder builder = ok(); + return builder.body(body); + } + + /** + * Creates a new response entity builder with a + * {@linkplain HttpStatus#CREATED CREATED} status and a location header set to the + * given URI. + * @param location the location URI + * @return the new response entity builder + */ + public static BodyBuilder created(URI location) { + BodyBuilder builder = status(HttpStatus.CREATED); + return builder.location(location); + } + + /** + * Creates a new response entity builder with an + * {@link HttpStatus#ACCEPTED ACCEPTED} status. + * @return the new response entity builder + */ + public static BodyBuilder accepted() { + return status(HttpStatus.ACCEPTED); + } + + /** + * Creates a new response entity builder with an + * {@link HttpStatus#NO_CONTENT NO_CONTENT} status. + * @return the new response entity builder + */ + public static HeadersBuilder noContent() { + return status(HttpStatus.NO_CONTENT); + } + + + /** + * Defines a builder that adds headers to the response entity. + * @param the builder subclass + */ + public interface HeadersBuilder> { + + /** + * Add the given, single header value under the given name. + * @param headerName the header name + * @param headerValue the header value(s) + * @return this builder + * @see HttpHeaders#add(String, String) + */ + B header(String headerName, String... headerValues); + + /** + * Set the set of allowed {@link HttpMethod HTTP methods}, as specified by the + * {@code Allow} header. + * @param allowedMethods the allowed methods + * @return this builder + * @see HttpHeaders#setAllow(Set) + */ + B allow(HttpMethod... allowedMethods); + + /** + * Sets the entity tag of the body, as specified by the {@code ETag} header. + * @param eTag the new entity tag + * @return this builder + * @see HttpHeaders#setETag(String) + */ + B eTag(String eTag); + + /** + * Sets the time the resource was last changed, as specified by the + * {@code Last-Modified} header. + *

The date should be specified as the number of milliseconds since January 1, + * 1970 GMT. + * @param lastModified the last modified date + * @return this builder + * @see HttpHeaders#setLastModified(long) + */ + B lastModified(long lastModified); + + /** + * Set the location of a resource, as specified by the {@code Location} header. + * @param location the location + * @return this builder + * @see HttpHeaders#setLocation(URI) + */ + B location(URI location); + + /** + * Builds the response entity with no body. + * @return the response entity + * @see ResponseBodyBuilder#body(Object) + */ + ResponseEntity build(); + + } + + + /** + * Defines a builder that adds a body to the response entity. + */ + public interface BodyBuilder extends HeadersBuilder { + + /** + * Set the length of the body in bytes, as specified by the {@code Content-Length} + * header. + * @param contentLength the content length + * @return this builder + * @see HttpHeaders#setContentLength(long) + */ + BodyBuilder contentLength(long contentLength); + + /** + * Set the {@linkplain MediaType media type} of the body, as specified by the + * {@code Content-Type} header. + * @param contentType the content type + * @return this builder + * @see HttpHeaders#setContentType(MediaType) + */ + BodyBuilder contentType(MediaType contentType); + + /** + * Sets the body of the response entity and returns it. + * @param body the body of the response entity + * @param the type of the body + * @return the built response entity + */ + ResponseEntity body(T body); + + } + + + private static class DefaultBuilder implements BodyBuilder { + + private final HttpStatus status; + + private final HttpHeaders headers = new HttpHeaders(); + + + public DefaultBuilder(HttpStatus status) { + this.status = status; + } + + @Override + public BodyBuilder header(String headerName, String... headerValues) { + for (String headerValue : headerValues) { + headers.add(headerName, headerValue); + } + return this; + } + + @Override + public BodyBuilder allow(HttpMethod... allowedMethods) { + headers.setAllow(new HashSet(Arrays.asList(allowedMethods))); + return this; + } + + @Override + public BodyBuilder contentLength(long contentLength) { + headers.setContentLength(contentLength); + return this; + } + + @Override + public BodyBuilder contentType(MediaType contentType) { + headers.setContentType(contentType); + return this; + } + + @Override + public BodyBuilder eTag(String eTag) { + headers.setETag(eTag); + return this; + } + + @Override + public BodyBuilder lastModified(long date) { + headers.setLastModified(date); + return this; + } + + @Override + public BodyBuilder location(URI location) { + headers.setLocation(location); + return this; + } + + + @Override + public ResponseEntity build() { + return new ResponseEntity(null, headers, status); + } + + @Override + public ResponseEntity body(T body) { + return new ResponseEntity(body, headers, status); + } + + } + + } diff --git a/spring-web/src/main/java/org/springframework/http/ResponseEntityBuilder.java b/spring-web/src/main/java/org/springframework/http/ResponseEntityBuilder.java deleted file mode 100644 index 99ce9234b8..0000000000 --- a/spring-web/src/main/java/org/springframework/http/ResponseEntityBuilder.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright 2002-2014 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.http; - -import java.net.URI; -import java.util.Set; - -/** - * A builder for {@link ResponseEntity} objects. Enforces common HTTP response practices - * through a fluent API. - * - *

This class is typically used in @Controller methods through a static import. - * For instance: - *

- * import static org.springframework.http.ResponseEntityBuilder.*;
- *
- * @Controller
- * public class MyController {
- *
- *     @RequestMapping(value="/entity", method=RequestMethod.GET)
- *     public ResponseEntity<MyEntity> myBean() {
- *         MyEntity entity = ...
- *         long lastModifiedDate = ...
- *         ResponseEntity<MyEntity> responseEntity = status(HttpStatus.OK)
- *             .header("Last-Modified", lastModifiedDate).body(entity);
- *         return responseEntity;
- *     }
- * }
- * - * Or, using some of the convenience methods: - * - *
- * ok().lastModified(lastModifiedDate).body(entity);
- * 
- * - * @author Arjen Poutsma - * @since 4.1 - * @see ResponseEntity - */ -@SuppressWarnings("rawtypes") -public class ResponseEntityBuilder { - - private ResponseEntityBuilder() { - } - - /** - * Creates a new {@code ResponseEntityBuilder} with the given status. - * @param status the response status - * @return the new response entity builder - */ - public static ResponseBodyBuilder status(HttpStatus status) { - return new DefaultResponseBuilder(status); - } - - /** - * Creates a new {@code ResponseEntityBuilder} with the given status. - * @param status the response status - * @return the new response entity builder - */ - public static ResponseBodyBuilder status(int status) { - return status(HttpStatus.valueOf(status)); - } - - /** - * Creates a new {@code ResponseEntityBuilder} with the status set to - * {@linkplain HttpStatus#OK OK}. - * @return the new response entity builder - */ - public static ResponseBodyBuilder ok() { - return status(HttpStatus.OK); - } - - /** - * Creates a new {@code ResponseEntity} with the given body and the status set to - * {@linkplain HttpStatus#OK OK}. - * @return the new response entity - */ - public static ResponseHeadersBuilder ok(Object body) { - ResponseBodyBuilder builder = ok(); - builder.body(body); - return builder; - } - - /** - * Creates a new {@code ResponseEntityBuilder} with a - * {@linkplain HttpStatus#CREATED CREATED} status and a location header set to the - * given URI. - * @param location the location URI - * @return the new response entity builder - */ - public static ResponseBodyBuilder created(URI location) { - ResponseBodyBuilder builder = status(HttpStatus.CREATED); - return builder.location(location); - } - - /** - * Creates a new {@code ResponseEntityBuilder} with an - * {@link HttpStatus#ACCEPTED ACCEPTED} status. - * @return the new response entity builder - */ - public static ResponseBodyBuilder accepted() { - return status(HttpStatus.ACCEPTED); - } - - /** - * Creates a new {@code ResponseEntityBuilder} with an - * {@link HttpStatus#NO_CONTENT NO_CONTENT} status. - * @return the new response entity builder - */ - public static ResponseHeadersBuilder noContent() { - return status(HttpStatus.NO_CONTENT); - } - - - /** - * Defines a builder that adds headers to the response entity. - * @param the builder subclass - */ - public interface ResponseHeadersBuilder> { - - /** - * Add the given, single header value under the given name. - * @param headerName the header name - * @param headerValues the header value(s) - * @return this builder - * @see HttpHeaders#add(String, String) - */ - B header(String headerName, String... headerValues); - - /** - * Set the set of allowed {@link HttpMethod HTTP methods}, as specified by the - * {@code Allow} header. - * @param allowedMethods the allowed methods - * @see HttpHeaders#setAllow(Set) - */ - B allow(Set allowedMethods); - - /** - * Sets the entity tag of the body, as specified by the {@code ETag} header. - * @param eTag the new entity tag - * @see HttpHeaders#setETag(String) - */ - B eTag(String eTag); - - /** - * Sets the time the resource was last changed, as specified by the - * {@code Last-Modified} header. - *

The date should be specified as the number of milliseconds since January 1, 1970 GMT. - * @param lastModified the last modified date - * @see HttpHeaders#setLastModified(long) - */ - B lastModified(long lastModified); - - /** - * Set the location of a resource, as specified by the {@code Location} header. - * @param location the location - * @see HttpHeaders#setLocation(URI) - */ - B location(URI location); - - /** - * Builds the response entity. - * @return the response entity - * @see ResponseBodyBuilder#body(Object) - */ - ResponseEntity build(); - - } - - - /** - * Defines a builder that adds a body to the response entity. - */ - public interface ResponseBodyBuilder extends ResponseHeadersBuilder { - - /** - * Set the length of the body in bytes, as specified by the {@code Content-Length} - * header. - * @param contentLength the content length - * @see HttpHeaders#setContentLength(long) - */ - ResponseBodyBuilder contentLength(long contentLength); - - /** - * Set the {@linkplain MediaType media type} of the body, as specified by the - * {@code Content-Type} header. - * @param contentType the content type - * @see HttpHeaders#setContentType(MediaType) - */ - ResponseBodyBuilder contentType(MediaType contentType); - - /** - * Sets the body of the response entity and returns it. - * @param body the body of the response entity - * @param the type of the body - * @return the built response entity - */ - ResponseEntity body(T body); - - } - - private static class DefaultResponseBuilder implements ResponseBodyBuilder { - - private final HttpStatus status; - - private final HttpHeaders headers = new HttpHeaders(); - - private Object body = null; - - public DefaultResponseBuilder(HttpStatus status) { - this.status = status; - } - - @Override - public ResponseBodyBuilder header(String headerName, String... headerValues) { - for (String headerValue : headerValues) { - headers.add(headerName, headerValue); - } - return this; - } - - @Override - public ResponseBodyBuilder allow(Set allowedMethods) { - headers.setAllow(allowedMethods); - return this; - } - - @Override - public ResponseBodyBuilder contentLength(long contentLength) { - headers.setContentLength(contentLength); - return this; - } - - @Override - public ResponseBodyBuilder contentType(MediaType contentType) { - headers.setContentType(contentType); - return this; - } - - @Override - public ResponseBodyBuilder eTag(String eTag) { - headers.setETag(eTag); - return this; - } - - @Override - public ResponseBodyBuilder lastModified(long date) { - headers.setLastModified(date); - return this; - } - - @Override - public ResponseBodyBuilder location(URI location) { - headers.setLocation(location); - return this; - } - - - @Override - @SuppressWarnings("unchecked") - public ResponseEntity build() { - return new ResponseEntity(body, headers, status); - } - - @Override - public ResponseEntity body(T body) { - this.body = body; - return new ResponseEntity(body, headers, status); - } - - } - -} diff --git a/spring-web/src/test/java/org/springframework/http/ResponseEntityBuilderTests.java b/spring-web/src/test/java/org/springframework/http/ResponseEntityTests.java similarity index 81% rename from spring-web/src/test/java/org/springframework/http/ResponseEntityBuilderTests.java rename to spring-web/src/test/java/org/springframework/http/ResponseEntityTests.java index efe8aa937f..d05d08728a 100644 --- a/spring-web/src/test/java/org/springframework/http/ResponseEntityBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/http/ResponseEntityTests.java @@ -18,24 +18,22 @@ package org.springframework.http; import java.net.URI; import java.net.URISyntaxException; -import java.util.Collections; import java.util.List; import static org.junit.Assert.*; import org.junit.Test; -import static org.springframework.http.ResponseEntityBuilder.*; -public class ResponseEntityBuilderTests { +public class ResponseEntityTests { @Test public void normal() { String headerName = "My-Custom-Header"; String headerValue1 = "HeaderValue1"; String headerValue2 = "HeaderValue2"; - Object entity = new Object(); + Integer entity = new Integer(42); - ResponseEntity responseEntity = - status(HttpStatus.OK).header(headerName, headerValue1, headerValue2).body(entity); + ResponseEntity responseEntity = + ResponseEntity.status(HttpStatus.OK).header(headerName, headerValue1, headerValue2).body(entity); assertNotNull(responseEntity); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); @@ -49,7 +47,7 @@ public class ResponseEntityBuilderTests { @Test public void okNoBody() { - ResponseEntity responseEntity = ok().build(); + ResponseEntity responseEntity = ResponseEntity.ok().build(); assertNotNull(responseEntity); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); @@ -58,8 +56,8 @@ public class ResponseEntityBuilderTests { @Test public void okEntity() { - Object entity = new Object(); - ResponseEntity responseEntity = ok(entity).build(); + Integer entity = new Integer(42); + ResponseEntity responseEntity = ResponseEntity.ok(entity); assertNotNull(responseEntity); assertEquals(HttpStatus.OK, responseEntity.getStatusCode()); @@ -69,7 +67,7 @@ public class ResponseEntityBuilderTests { @Test public void createdLocation() throws URISyntaxException { URI location = new URI("location"); - ResponseEntity responseEntity = created(location).build(); + ResponseEntity responseEntity = ResponseEntity.created(location).build(); assertNotNull(responseEntity); assertEquals(HttpStatus.CREATED, responseEntity.getStatusCode()); @@ -77,11 +75,13 @@ public class ResponseEntityBuilderTests { assertEquals(location.toString(), responseEntity.getHeaders().getFirst("Location")); assertNull(responseEntity.getBody()); + + ResponseEntity.created(location).header("MyResponseHeader", "MyValue").body("Hello World"); } @Test public void acceptedNoBody() throws URISyntaxException { - ResponseEntity responseEntity = accepted().build(); + ResponseEntity responseEntity = ResponseEntity.accepted().build(); assertNotNull(responseEntity); assertEquals(HttpStatus.ACCEPTED, responseEntity.getStatusCode()); @@ -90,7 +90,7 @@ public class ResponseEntityBuilderTests { @Test public void noContent() throws URISyntaxException { - ResponseEntity responseEntity = ResponseEntityBuilder.noContent().build(); + ResponseEntity responseEntity = ResponseEntity.noContent().build(); assertNotNull(responseEntity); assertEquals(HttpStatus.NO_CONTENT, responseEntity.getStatusCode()); @@ -104,8 +104,8 @@ public class ResponseEntityBuilderTests { long contentLength = 67890; MediaType contentType = MediaType.TEXT_PLAIN; - ResponseEntity responseEntity = ResponseEntityBuilder.ok(). - allow(Collections.singleton(HttpMethod.GET)). + ResponseEntity responseEntity = ResponseEntity.ok(). + allow(HttpMethod.GET). eTag(eTag). lastModified(12345L). location(location).