Skip to content

Commit d06188e

Browse files
committed
ResponseEntity allows for setting non-standard status code
Issue: SPR-14205
1 parent e5d52a9 commit d06188e

File tree

7 files changed

+102
-53
lines changed

7 files changed

+102
-53
lines changed

spring-web/src/main/java/org/springframework/http/HttpStatus.java

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -392,17 +392,17 @@ public enum HttpStatus {
392392
NETWORK_AUTHENTICATION_REQUIRED(511, "Network Authentication Required");
393393

394394

395-
396395
private final int value;
397396

398397
private final String reasonPhrase;
399398

400399

401-
private HttpStatus(int value, String reasonPhrase) {
400+
HttpStatus(int value, String reasonPhrase) {
402401
this.value = value;
403402
this.reasonPhrase = reasonPhrase;
404403
}
405404

405+
406406
/**
407407
* Return the integer value of this status code.
408408
*/
@@ -414,7 +414,7 @@ public int value() {
414414
* Return the reason phrase of this status code.
415415
*/
416416
public String getReasonPhrase() {
417-
return reasonPhrase;
417+
return this.reasonPhrase;
418418
}
419419

420420
/**
@@ -423,7 +423,7 @@ public String getReasonPhrase() {
423423
* This is a shortcut for checking the value of {@link #series()}.
424424
*/
425425
public boolean is1xxInformational() {
426-
return (Series.INFORMATIONAL.equals(series()));
426+
return Series.INFORMATIONAL.equals(series());
427427
}
428428

429429
/**
@@ -432,7 +432,7 @@ public boolean is1xxInformational() {
432432
* This is a shortcut for checking the value of {@link #series()}.
433433
*/
434434
public boolean is2xxSuccessful() {
435-
return (Series.SUCCESSFUL.equals(series()));
435+
return Series.SUCCESSFUL.equals(series());
436436
}
437437

438438
/**
@@ -441,7 +441,7 @@ public boolean is2xxSuccessful() {
441441
* This is a shortcut for checking the value of {@link #series()}.
442442
*/
443443
public boolean is3xxRedirection() {
444-
return (Series.REDIRECTION.equals(series()));
444+
return Series.REDIRECTION.equals(series());
445445
}
446446

447447

@@ -451,7 +451,7 @@ public boolean is3xxRedirection() {
451451
* This is a shortcut for checking the value of {@link #series()}.
452452
*/
453453
public boolean is4xxClientError() {
454-
return (Series.CLIENT_ERROR.equals(series()));
454+
return Series.CLIENT_ERROR.equals(series());
455455
}
456456

457457
/**
@@ -460,7 +460,7 @@ public boolean is4xxClientError() {
460460
* This is a shortcut for checking the value of {@link #series()}.
461461
*/
462462
public boolean is5xxServerError() {
463-
return (Series.SERVER_ERROR.equals(series()));
463+
return Series.SERVER_ERROR.equals(series());
464464
}
465465

466466
/**
@@ -476,7 +476,7 @@ public Series series() {
476476
*/
477477
@Override
478478
public String toString() {
479-
return Integer.toString(value);
479+
return Integer.toString(this.value);
480480
}
481481

482482

@@ -497,10 +497,10 @@ public static HttpStatus valueOf(int statusCode) {
497497

498498

499499
/**
500-
* Java 5 enumeration of HTTP status series.
500+
* Enumeration of HTTP status series.
501501
* <p>Retrievable via {@link HttpStatus#series()}.
502502
*/
503-
public static enum Series {
503+
public enum Series {
504504

505505
INFORMATIONAL(1),
506506
SUCCESSFUL(2),
@@ -510,7 +510,7 @@ public static enum Series {
510510

511511
private final int value;
512512

513-
private Series(int value) {
513+
Series(int value) {
514514
this.value = value;
515515
}
516516

@@ -534,7 +534,6 @@ public static Series valueOf(int status) {
534534
public static Series valueOf(HttpStatus status) {
535535
return valueOf(status.value);
536536
}
537-
538537
}
539538

540539
}

spring-web/src/main/java/org/springframework/http/ResponseEntity.java

Lines changed: 59 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -21,6 +21,7 @@
2121
import java.util.LinkedHashSet;
2222
import java.util.Set;
2323

24+
import org.springframework.util.Assert;
2425
import org.springframework.util.MultiValueMap;
2526
import org.springframework.util.ObjectUtils;
2627

@@ -65,56 +66,85 @@
6566
*/
6667
public class ResponseEntity<T> extends HttpEntity<T> {
6768

68-
private final HttpStatus statusCode;
69+
private final Object statusCode;
6970

7071

7172
/**
7273
* Create a new {@code ResponseEntity} with the given status code, and no body nor headers.
73-
* @param statusCode the status code
74+
* @param status the status code
7475
*/
75-
public ResponseEntity(HttpStatus statusCode) {
76-
super();
77-
this.statusCode = statusCode;
76+
public ResponseEntity(HttpStatus status) {
77+
this(null, null, status);
7878
}
7979

8080
/**
8181
* Create a new {@code ResponseEntity} with the given body and status code, and no headers.
8282
* @param body the entity body
83-
* @param statusCode the status code
83+
* @param status the status code
8484
*/
85-
public ResponseEntity(T body, HttpStatus statusCode) {
86-
super(body);
87-
this.statusCode = statusCode;
85+
public ResponseEntity(T body, HttpStatus status) {
86+
this(body, null, status);
8887
}
8988

9089
/**
9190
* Create a new {@code HttpEntity} with the given headers and status code, and no body.
9291
* @param headers the entity headers
93-
* @param statusCode the status code
92+
* @param status the status code
9493
*/
95-
public ResponseEntity(MultiValueMap<String, String> headers, HttpStatus statusCode) {
96-
super(headers);
97-
this.statusCode = statusCode;
94+
public ResponseEntity(MultiValueMap<String, String> headers, HttpStatus status) {
95+
this(null, headers, status);
96+
}
97+
98+
/**
99+
* Create a new {@code HttpEntity} with the given body, headers, and status code.
100+
* @param body the entity body
101+
* @param headers the entity headers
102+
* @param status the status code
103+
*/
104+
public ResponseEntity(T body, MultiValueMap<String, String> headers, HttpStatus status) {
105+
super(body, headers);
106+
Assert.notNull(status, "HttpStatus must not be null");
107+
this.statusCode = status;
98108
}
99109

100110
/**
101111
* Create a new {@code HttpEntity} with the given body, headers, and status code.
112+
* Just used behind the nested builder API.
102113
* @param body the entity body
103114
* @param headers the entity headers
104-
* @param statusCode the status code
115+
* @param statusCode the status code (as {@code HttpStatus} or as {@code Integer} value)
105116
*/
106-
public ResponseEntity(T body, MultiValueMap<String, String> headers, HttpStatus statusCode) {
117+
private ResponseEntity(T body, MultiValueMap<String, String> headers, Object statusCode) {
107118
super(body, headers);
108119
this.statusCode = statusCode;
109120
}
110121

111122

112123
/**
113124
* Return the HTTP status code of the response.
114-
* @return the HTTP status as an HttpStatus enum value
125+
* @return the HTTP status as an HttpStatus enum entry
115126
*/
116127
public HttpStatus getStatusCode() {
117-
return this.statusCode;
128+
if (this.statusCode instanceof HttpStatus) {
129+
return (HttpStatus) this.statusCode;
130+
}
131+
else {
132+
return HttpStatus.valueOf((Integer) this.statusCode);
133+
}
134+
}
135+
136+
/**
137+
* Return the HTTP status code of the response.
138+
* @return the HTTP status as an int value
139+
* @since 4.3
140+
*/
141+
public int getStatusCodeValue() {
142+
if (this.statusCode instanceof HttpStatus) {
143+
return ((HttpStatus) this.statusCode).value();
144+
}
145+
else {
146+
return (Integer) this.statusCode;
147+
}
118148
}
119149

120150

@@ -139,8 +169,10 @@ public int hashCode() {
139169
public String toString() {
140170
StringBuilder builder = new StringBuilder("<");
141171
builder.append(this.statusCode.toString());
142-
builder.append(' ');
143-
builder.append(this.statusCode.getReasonPhrase());
172+
if (this.statusCode instanceof HttpStatus) {
173+
builder.append(' ');
174+
builder.append(((HttpStatus) this.statusCode).getReasonPhrase());
175+
}
144176
builder.append(',');
145177
T body = getBody();
146178
HttpHeaders headers = getHeaders();
@@ -167,6 +199,7 @@ public String toString() {
167199
* @since 4.1
168200
*/
169201
public static BodyBuilder status(HttpStatus status) {
202+
Assert.notNull(status, "HttpStatus must not be null");
170203
return new DefaultBuilder(status);
171204
}
172205

@@ -177,7 +210,7 @@ public static BodyBuilder status(HttpStatus status) {
177210
* @since 4.1
178211
*/
179212
public static BodyBuilder status(int status) {
180-
return status(HttpStatus.valueOf(status));
213+
return new DefaultBuilder(status);
181214
}
182215

183216
/**
@@ -388,12 +421,12 @@ public interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
388421

389422
private static class DefaultBuilder implements BodyBuilder {
390423

391-
private final HttpStatus status;
424+
private final Object statusCode;
392425

393426
private final HttpHeaders headers = new HttpHeaders();
394427

395-
public DefaultBuilder(HttpStatus status) {
396-
this.status = status;
428+
public DefaultBuilder(Object statusCode) {
429+
this.statusCode = statusCode;
397430
}
398431

399432
@Override
@@ -473,12 +506,12 @@ public BodyBuilder varyBy(String... requestHeaders) {
473506

474507
@Override
475508
public ResponseEntity<Void> build() {
476-
return new ResponseEntity<Void>(null, this.headers, this.status);
509+
return body(null);
477510
}
478511

479512
@Override
480513
public <T> ResponseEntity<T> body(T body) {
481-
return new ResponseEntity<T>(body, this.headers, this.status);
514+
return new ResponseEntity<T>(body, this.headers, this.statusCode);
482515
}
483516
}
484517

spring-web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -73,6 +73,7 @@ public HttpServletResponse getServletResponse() {
7373

7474
@Override
7575
public void setStatusCode(HttpStatus status) {
76+
Assert.notNull(status, "HttpStatus must not be null");
7677
this.servletResponse.setStatus(status.value());
7778
}
7879

spring-web/src/test/java/org/springframework/http/ResponseEntityTests.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -252,4 +252,22 @@ public void cacheControlNoCache() {
252252
assertThat(cacheControlHeader, Matchers.equalTo("no-store"));
253253
}
254254

255+
@Test
256+
public void statusCodeAsInt() {
257+
Integer entity = new Integer(42);
258+
ResponseEntity<Integer> responseEntity = ResponseEntity.status(200).body(entity);
259+
260+
assertEquals(200, responseEntity.getStatusCode().value());
261+
assertEquals(entity, responseEntity.getBody());
262+
}
263+
264+
@Test
265+
public void customStatusCode() {
266+
Integer entity = new Integer(42);
267+
ResponseEntity<Integer> responseEntity = ResponseEntity.status(299).body(entity);
268+
269+
assertEquals(299, responseEntity.getStatusCodeValue());
270+
assertEquals(entity, responseEntity.getBody());
271+
}
272+
255273
}

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/HttpEntityMethodProcessor.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,8 @@ public void handleReturnValue(Object returnValue, MethodParameter returnType,
184184
}
185185
}
186186

187-
Object body = responseEntity.getBody();
188187
if (responseEntity instanceof ResponseEntity) {
189-
outputMessage.setStatusCode(((ResponseEntity<?>) responseEntity).getStatusCode());
188+
outputMessage.getServletResponse().setStatus(((ResponseEntity<?>) responseEntity).getStatusCodeValue());
190189
HttpMethod method = inputMessage.getMethod();
191190
boolean isGetOrHead = (HttpMethod.GET == method || HttpMethod.HEAD == method);
192191
if (isGetOrHead && isResourceNotModified(inputMessage, outputMessage)) {
@@ -197,6 +196,8 @@ public void handleReturnValue(Object returnValue, MethodParameter returnType,
197196
return;
198197
}
199198
}
199+
200+
Object body = responseEntity.getBody();
200201
if (inputMessage.getHeaders().containsKey(HttpHeaders.RANGE) &&
201202
Resource.class.isAssignableFrom(body.getClass())) {
202203
try {

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ResponseBodyEmitterReturnValueHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,9 @@ public void handleReturnValue(Object returnValue, MethodParameter returnType,
130130
HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
131131
ServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
132132

133-
if (ResponseEntity.class.isAssignableFrom(returnValue.getClass())) {
133+
if (returnValue instanceof ResponseEntity) {
134134
ResponseEntity<?> responseEntity = (ResponseEntity<?>) returnValue;
135-
outputMessage.setStatusCode(responseEntity.getStatusCode());
135+
response.setStatus(responseEntity.getStatusCodeValue());
136136
outputMessage.getHeaders().putAll(responseEntity.getHeaders());
137137
returnValue = responseEntity.getBody();
138138
if (returnValue == null) {

0 commit comments

Comments
 (0)