ResponseEntity allows for setting non-standard status code

Issue: SPR-14205
This commit is contained in:
Juergen Hoeller 2016-04-26 23:05:21 +02:00
parent e5d52a96a7
commit d06188ed4d
7 changed files with 102 additions and 53 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@ -392,17 +392,17 @@ public enum HttpStatus {
NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required");
private final int value;
private final String reasonPhrase;
private HttpStatus(int value, String reasonPhrase) {
HttpStatus(int value, String reasonPhrase) {
this.value = value;
this.reasonPhrase = reasonPhrase;
}
/**
* Return the integer value of this status code.
*/
@ -414,7 +414,7 @@ public enum HttpStatus {
* Return the reason phrase of this status code.
*/
public String getReasonPhrase() {
return reasonPhrase;
return this.reasonPhrase;
}
/**
@ -423,7 +423,7 @@ public enum HttpStatus {
* This is a shortcut for checking the value of {@link #series()}.
*/
public boolean is1xxInformational() {
return (Series.INFORMATIONAL.equals(series()));
return Series.INFORMATIONAL.equals(series());
}
/**
@ -432,7 +432,7 @@ public enum HttpStatus {
* This is a shortcut for checking the value of {@link #series()}.
*/
public boolean is2xxSuccessful() {
return (Series.SUCCESSFUL.equals(series()));
return Series.SUCCESSFUL.equals(series());
}
/**
@ -441,7 +441,7 @@ public enum HttpStatus {
* This is a shortcut for checking the value of {@link #series()}.
*/
public boolean is3xxRedirection() {
return (Series.REDIRECTION.equals(series()));
return Series.REDIRECTION.equals(series());
}
@ -451,7 +451,7 @@ public enum HttpStatus {
* This is a shortcut for checking the value of {@link #series()}.
*/
public boolean is4xxClientError() {
return (Series.CLIENT_ERROR.equals(series()));
return Series.CLIENT_ERROR.equals(series());
}
/**
@ -460,7 +460,7 @@ public enum HttpStatus {
* This is a shortcut for checking the value of {@link #series()}.
*/
public boolean is5xxServerError() {
return (Series.SERVER_ERROR.equals(series()));
return Series.SERVER_ERROR.equals(series());
}
/**
@ -476,7 +476,7 @@ public enum HttpStatus {
*/
@Override
public String toString() {
return Integer.toString(value);
return Integer.toString(this.value);
}
@ -497,10 +497,10 @@ public enum HttpStatus {
/**
* Java 5 enumeration of HTTP status series.
* Enumeration of HTTP status series.
* <p>Retrievable via {@link HttpStatus#series()}.
*/
public static enum Series {
public enum Series {
INFORMATIONAL(1),
SUCCESSFUL(2),
@ -510,7 +510,7 @@ public enum HttpStatus {
private final int value;
private Series(int value) {
Series(int value) {
this.value = value;
}
@ -534,7 +534,6 @@ public enum HttpStatus {
public static Series valueOf(HttpStatus status) {
return valueOf(status.value);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@ -21,6 +21,7 @@ import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
@ -65,45 +66,55 @@ import org.springframework.util.ObjectUtils;
*/
public class ResponseEntity<T> extends HttpEntity<T> {
private final HttpStatus statusCode;
private final Object statusCode;
/**
* Create a new {@code ResponseEntity} with the given status code, and no body nor headers.
* @param statusCode the status code
* @param status the status code
*/
public ResponseEntity(HttpStatus statusCode) {
super();
this.statusCode = statusCode;
public ResponseEntity(HttpStatus status) {
this(null, null, status);
}
/**
* Create a new {@code ResponseEntity} with the given body and status code, and no headers.
* @param body the entity body
* @param statusCode the status code
* @param status the status code
*/
public ResponseEntity(T body, HttpStatus statusCode) {
super(body);
this.statusCode = statusCode;
public ResponseEntity(T body, HttpStatus status) {
this(body, null, status);
}
/**
* Create a new {@code HttpEntity} with the given headers and status code, and no body.
* @param headers the entity headers
* @param statusCode the status code
* @param status the status code
*/
public ResponseEntity(MultiValueMap<String, String> headers, HttpStatus statusCode) {
super(headers);
this.statusCode = statusCode;
public ResponseEntity(MultiValueMap<String, String> headers, HttpStatus status) {
this(null, headers, status);
}
/**
* Create a new {@code HttpEntity} with the given body, headers, and status code.
* @param body the entity body
* @param headers the entity headers
* @param statusCode the status code
* @param status the status code
*/
public ResponseEntity(T body, MultiValueMap<String, String> headers, HttpStatus statusCode) {
public ResponseEntity(T body, MultiValueMap<String, String> headers, HttpStatus status) {
super(body, headers);
Assert.notNull(status, "HttpStatus must not be null");
this.statusCode = status;
}
/**
* Create a new {@code HttpEntity} with the given body, headers, and status code.
* Just used behind the nested builder API.
* @param body the entity body
* @param headers the entity headers
* @param statusCode the status code (as {@code HttpStatus} or as {@code Integer} value)
*/
private ResponseEntity(T body, MultiValueMap<String, String> headers, Object statusCode) {
super(body, headers);
this.statusCode = statusCode;
}
@ -111,10 +122,29 @@ public class ResponseEntity<T> extends HttpEntity<T> {
/**
* Return the HTTP status code of the response.
* @return the HTTP status as an HttpStatus enum value
* @return the HTTP status as an HttpStatus enum entry
*/
public HttpStatus getStatusCode() {
return this.statusCode;
if (this.statusCode instanceof HttpStatus) {
return (HttpStatus) this.statusCode;
}
else {
return HttpStatus.valueOf((Integer) this.statusCode);
}
}
/**
* Return the HTTP status code of the response.
* @return the HTTP status as an int value
* @since 4.3
*/
public int getStatusCodeValue() {
if (this.statusCode instanceof HttpStatus) {
return ((HttpStatus) this.statusCode).value();
}
else {
return (Integer) this.statusCode;
}
}
@ -139,8 +169,10 @@ public class ResponseEntity<T> extends HttpEntity<T> {
public String toString() {
StringBuilder builder = new StringBuilder("<");
builder.append(this.statusCode.toString());
builder.append(' ');
builder.append(this.statusCode.getReasonPhrase());
if (this.statusCode instanceof HttpStatus) {
builder.append(' ');
builder.append(((HttpStatus) this.statusCode).getReasonPhrase());
}
builder.append(',');
T body = getBody();
HttpHeaders headers = getHeaders();
@ -167,6 +199,7 @@ public class ResponseEntity<T> extends HttpEntity<T> {
* @since 4.1
*/
public static BodyBuilder status(HttpStatus status) {
Assert.notNull(status, "HttpStatus must not be null");
return new DefaultBuilder(status);
}
@ -177,7 +210,7 @@ public class ResponseEntity<T> extends HttpEntity<T> {
* @since 4.1
*/
public static BodyBuilder status(int status) {
return status(HttpStatus.valueOf(status));
return new DefaultBuilder(status);
}
/**
@ -388,12 +421,12 @@ public class ResponseEntity<T> extends HttpEntity<T> {
private static class DefaultBuilder implements BodyBuilder {
private final HttpStatus status;
private final Object statusCode;
private final HttpHeaders headers = new HttpHeaders();
public DefaultBuilder(HttpStatus status) {
this.status = status;
public DefaultBuilder(Object statusCode) {
this.statusCode = statusCode;
}
@Override
@ -473,12 +506,12 @@ public class ResponseEntity<T> extends HttpEntity<T> {
@Override
public ResponseEntity<Void> build() {
return new ResponseEntity<Void>(null, this.headers, this.status);
return body(null);
}
@Override
public <T> ResponseEntity<T> body(T body) {
return new ResponseEntity<T>(body, this.headers, this.status);
return new ResponseEntity<T>(body, this.headers, this.statusCode);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@ -73,6 +73,7 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
@Override
public void setStatusCode(HttpStatus status) {
Assert.notNull(status, "HttpStatus must not be null");
this.servletResponse.setStatus(status.value());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@ -252,4 +252,22 @@ public class ResponseEntityTests {
assertThat(cacheControlHeader, Matchers.equalTo("no-store"));
}
@Test
public void statusCodeAsInt() {
Integer entity = new Integer(42);
ResponseEntity<Integer> responseEntity = ResponseEntity.status(200).body(entity);
assertEquals(200, responseEntity.getStatusCode().value());
assertEquals(entity, responseEntity.getBody());
}
@Test
public void customStatusCode() {
Integer entity = new Integer(42);
ResponseEntity<Integer> responseEntity = ResponseEntity.status(299).body(entity);
assertEquals(299, responseEntity.getStatusCodeValue());
assertEquals(entity, responseEntity.getBody());
}
}

View File

@ -184,9 +184,8 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
}
}
Object body = responseEntity.getBody();
if (responseEntity instanceof ResponseEntity) {
outputMessage.setStatusCode(((ResponseEntity<?>) responseEntity).getStatusCode());
outputMessage.getServletResponse().setStatus(((ResponseEntity<?>) responseEntity).getStatusCodeValue());
HttpMethod method = inputMessage.getMethod();
boolean isGetOrHead = (HttpMethod.GET == method || HttpMethod.HEAD == method);
if (isGetOrHead && isResourceNotModified(inputMessage, outputMessage)) {
@ -197,6 +196,8 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
return;
}
}
Object body = responseEntity.getBody();
if (inputMessage.getHeaders().containsKey(HttpHeaders.RANGE) &&
Resource.class.isAssignableFrom(body.getClass())) {
try {

View File

@ -130,9 +130,9 @@ public class ResponseBodyEmitterReturnValueHandler implements AsyncHandlerMethod
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
ServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
if (ResponseEntity.class.isAssignableFrom(returnValue.getClass())) {
if (returnValue instanceof ResponseEntity) {
ResponseEntity<?> responseEntity = (ResponseEntity<?>) returnValue;
outputMessage.setStatusCode(responseEntity.getStatusCode());
response.setStatus(responseEntity.getStatusCodeValue());
outputMessage.getHeaders().putAll(responseEntity.getHeaders());
returnValue = responseEntity.getBody();
if (returnValue == null) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc.method.annotation;
import java.io.OutputStream;
import java.util.concurrent.Callable;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -33,7 +33,6 @@ import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Supports return values of type
* {@link org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody}
@ -68,11 +67,10 @@ public class StreamingResponseBodyReturnValueHandler implements HandlerMethodRet
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
ServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
if (ResponseEntity.class.isAssignableFrom(returnValue.getClass())) {
if (returnValue instanceof ResponseEntity) {
ResponseEntity<?> responseEntity = (ResponseEntity<?>) returnValue;
outputMessage.setStatusCode(responseEntity.getStatusCode());
response.setStatus(responseEntity.getStatusCodeValue());
outputMessage.getHeaders().putAll(responseEntity.getHeaders());
returnValue = responseEntity.getBody();
if (returnValue == null) {
mavContainer.setRequestHandled(true);
@ -97,7 +95,6 @@ public class StreamingResponseBodyReturnValueHandler implements HandlerMethodRet
private final StreamingResponseBody streamingBody;
public StreamingResponseBodyTask(OutputStream outputStream, StreamingResponseBody streamingBody) {
this.outputStream = outputStream;
this.streamingBody = streamingBody;