Skip to content

Commit 2cce123

Browse files
committed
Add property to control 'path' field inclusion in error responses
By default it is included. Closes gh-38619
1 parent c4be302 commit 2cce123

File tree

12 files changed

+157
-15
lines changed

12 files changed

+157
-15
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ManagementErrorEndpoint.java

+13-3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
*
3737
* @author Dave Syer
3838
* @author Scott Frederick
39+
* @author Moritz Halbritter
3940
* @since 2.0.0
4041
*/
4142
@Controller
@@ -72,30 +73,39 @@ private ErrorAttributeOptions getErrorAttributeOptions(ServletWebRequest request
7273
if (includeBindingErrors(request)) {
7374
options = options.including(Include.BINDING_ERRORS);
7475
}
76+
options = includePath(request) ? options.including(Include.PATH) : options.excluding(Include.PATH);
7577
return options;
7678
}
7779

7880
private boolean includeStackTrace(ServletWebRequest request) {
7981
return switch (this.errorProperties.getIncludeStacktrace()) {
8082
case ALWAYS -> true;
8183
case ON_PARAM -> getBooleanParameter(request, "trace");
82-
default -> false;
84+
case NEVER -> false;
8385
};
8486
}
8587

8688
private boolean includeMessage(ServletWebRequest request) {
8789
return switch (this.errorProperties.getIncludeMessage()) {
8890
case ALWAYS -> true;
8991
case ON_PARAM -> getBooleanParameter(request, "message");
90-
default -> false;
92+
case NEVER -> false;
9193
};
9294
}
9395

9496
private boolean includeBindingErrors(ServletWebRequest request) {
9597
return switch (this.errorProperties.getIncludeBindingErrors()) {
9698
case ALWAYS -> true;
9799
case ON_PARAM -> getBooleanParameter(request, "errors");
98-
default -> false;
100+
case NEVER -> false;
101+
};
102+
}
103+
104+
private boolean includePath(ServletWebRequest request) {
105+
return switch (this.errorProperties.getIncludePath()) {
106+
case ALWAYS -> true;
107+
case ON_PARAM -> getBooleanParameter(request, "path");
108+
case NEVER -> false;
99109
};
100110
}
101111

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ErrorProperties.java

+13
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ public class ErrorProperties {
5555
*/
5656
private IncludeAttribute includeBindingErrors = IncludeAttribute.NEVER;
5757

58+
/**
59+
* When to include "path" attribute.
60+
*/
61+
private IncludeAttribute includePath = IncludeAttribute.ALWAYS;
62+
5863
private final Whitelabel whitelabel = new Whitelabel();
5964

6065
public String getPath() {
@@ -97,6 +102,14 @@ public void setIncludeBindingErrors(IncludeAttribute includeBindingErrors) {
97102
this.includeBindingErrors = includeBindingErrors;
98103
}
99104

105+
public IncludeAttribute getIncludePath() {
106+
return this.includePath;
107+
}
108+
109+
public void setIncludePath(IncludeAttribute includePath) {
110+
this.includePath = includePath;
111+
}
112+
100113
public Whitelabel getWhitelabel() {
101114
return this.whitelabel;
102115
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/error/AbstractErrorWebExceptionHandler.java

+12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
*
5555
* @author Brian Clozel
5656
* @author Scott Frederick
57+
* @author Moritz Halbritter
5758
* @since 2.0.0
5859
* @see ErrorAttributes
5960
*/
@@ -168,6 +169,17 @@ protected boolean isBindingErrorsEnabled(ServerRequest request) {
168169
return getBooleanParameter(request, "errors");
169170
}
170171

172+
/**
173+
* Check whether the path attribute has been set on the given request.
174+
* @param request the source request
175+
* @return {@code true} if the path attribute has been requested, {@code false}
176+
* otherwise
177+
* @since 3.3.0
178+
*/
179+
protected boolean isPathEnabled(ServerRequest request) {
180+
return getBooleanParameter(request, "path");
181+
}
182+
171183
private boolean getBooleanParameter(ServerRequest request, String parameterName) {
172184
String parameter = request.queryParam(parameterName).orElse("false");
173185
return !"false".equalsIgnoreCase(parameter);

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/error/DefaultErrorWebExceptionHandler.java

+20-3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
*
7676
* @author Brian Clozel
7777
* @author Scott Frederick
78+
* @author Moritz Halbritter
7879
* @since 2.0.0
7980
*/
8081
public class DefaultErrorWebExceptionHandler extends AbstractErrorWebExceptionHandler {
@@ -164,6 +165,7 @@ protected ErrorAttributeOptions getErrorAttributeOptions(ServerRequest request,
164165
if (isIncludeBindingErrors(request, mediaType)) {
165166
options = options.including(Include.BINDING_ERRORS);
166167
}
168+
options = isIncludePath(request, mediaType) ? options.including(Include.PATH) : options.excluding(Include.PATH);
167169
return options;
168170
}
169171

@@ -177,7 +179,7 @@ protected boolean isIncludeStackTrace(ServerRequest request, MediaType produces)
177179
return switch (this.errorProperties.getIncludeStacktrace()) {
178180
case ALWAYS -> true;
179181
case ON_PARAM -> isTraceEnabled(request);
180-
default -> false;
182+
case NEVER -> false;
181183
};
182184
}
183185

@@ -191,7 +193,7 @@ protected boolean isIncludeMessage(ServerRequest request, MediaType produces) {
191193
return switch (this.errorProperties.getIncludeMessage()) {
192194
case ALWAYS -> true;
193195
case ON_PARAM -> isMessageEnabled(request);
194-
default -> false;
196+
case NEVER -> false;
195197
};
196198
}
197199

@@ -205,7 +207,22 @@ protected boolean isIncludeBindingErrors(ServerRequest request, MediaType produc
205207
return switch (this.errorProperties.getIncludeBindingErrors()) {
206208
case ALWAYS -> true;
207209
case ON_PARAM -> isBindingErrorsEnabled(request);
208-
default -> false;
210+
case NEVER -> false;
211+
};
212+
}
213+
214+
/**
215+
* Determine if the path attribute should be included.
216+
* @param request the source request
217+
* @param produces the media type produced (or {@code MediaType.ALL})
218+
* @return if the path attribute should be included
219+
* @since 3.3.0
220+
*/
221+
protected boolean isIncludePath(ServerRequest request, MediaType produces) {
222+
return switch (this.errorProperties.getIncludePath()) {
223+
case ALWAYS -> true;
224+
case ON_PARAM -> isPathEnabled(request);
225+
case NEVER -> false;
209226
};
210227
}
211228

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/AbstractErrorController.java

+26
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
* @author Dave Syer
4242
* @author Phillip Webb
4343
* @author Scott Frederick
44+
* @author Moritz Halbritter
4445
* @since 1.3.0
4546
* @see ErrorAttributes
4647
*/
@@ -74,18 +75,43 @@ protected Map<String, Object> getErrorAttributes(HttpServletRequest request, Err
7475
return this.errorAttributes.getErrorAttributes(webRequest, options);
7576
}
7677

78+
/**
79+
* Returns whether the trace parameter is set.
80+
* @param request the request
81+
* @return whether the trace parameter is set
82+
*/
7783
protected boolean getTraceParameter(HttpServletRequest request) {
7884
return getBooleanParameter(request, "trace");
7985
}
8086

87+
/**
88+
* Returns whether the message parameter is set.
89+
* @param request the request
90+
* @return whether the message parameter is set
91+
*/
8192
protected boolean getMessageParameter(HttpServletRequest request) {
8293
return getBooleanParameter(request, "message");
8394
}
8495

96+
/**
97+
* Returns whether the errors parameter is set.
98+
* @param request the request
99+
* @return whether the errors parameter is set
100+
*/
85101
protected boolean getErrorsParameter(HttpServletRequest request) {
86102
return getBooleanParameter(request, "errors");
87103
}
88104

105+
/**
106+
* Returns whether the path parameter is set.
107+
* @param request the request
108+
* @return whether the path parameter is set
109+
* @since 3.3.0
110+
*/
111+
protected boolean getPathParameter(HttpServletRequest request) {
112+
return getBooleanParameter(request, "path");
113+
}
114+
89115
protected boolean getBooleanParameter(HttpServletRequest request, String parameterName) {
90116
String parameter = request.getParameter(parameterName);
91117
if (parameter == null) {

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorController.java

+20-3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
* @author Michael Stummvoll
5050
* @author Stephane Nicoll
5151
* @author Scott Frederick
52+
* @author Moritz Halbritter
5253
* @since 1.0.0
5354
* @see ErrorAttributes
5455
* @see ErrorProperties
@@ -121,6 +122,7 @@ protected ErrorAttributeOptions getErrorAttributeOptions(HttpServletRequest requ
121122
if (isIncludeBindingErrors(request, mediaType)) {
122123
options = options.including(Include.BINDING_ERRORS);
123124
}
125+
options = isIncludePath(request, mediaType) ? options.including(Include.PATH) : options.excluding(Include.PATH);
124126
return options;
125127
}
126128

@@ -134,7 +136,7 @@ protected boolean isIncludeStackTrace(HttpServletRequest request, MediaType prod
134136
return switch (getErrorProperties().getIncludeStacktrace()) {
135137
case ALWAYS -> true;
136138
case ON_PARAM -> getTraceParameter(request);
137-
default -> false;
139+
case NEVER -> false;
138140
};
139141
}
140142

@@ -148,7 +150,7 @@ protected boolean isIncludeMessage(HttpServletRequest request, MediaType produce
148150
return switch (getErrorProperties().getIncludeMessage()) {
149151
case ALWAYS -> true;
150152
case ON_PARAM -> getMessageParameter(request);
151-
default -> false;
153+
case NEVER -> false;
152154
};
153155
}
154156

@@ -162,7 +164,22 @@ protected boolean isIncludeBindingErrors(HttpServletRequest request, MediaType p
162164
return switch (getErrorProperties().getIncludeBindingErrors()) {
163165
case ALWAYS -> true;
164166
case ON_PARAM -> getErrorsParameter(request);
165-
default -> false;
167+
case NEVER -> false;
168+
};
169+
}
170+
171+
/**
172+
* Determine if the path attribute should be included.
173+
* @param request the source request
174+
* @param produces the media type produced (or {@code MediaType.ALL})
175+
* @return if the path attribute should be included
176+
* @since 3.3.0
177+
*/
178+
protected boolean isIncludePath(HttpServletRequest request, MediaType produces) {
179+
return switch (getErrorProperties().getIncludePath()) {
180+
case ALWAYS -> true;
181+
case ON_PARAM -> getPathParameter(request);
182+
case NEVER -> false;
166183
};
167184
}
168185

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfigurationTests.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,7 @@ private DispatcherServletWebRequest createWebRequest(Exception ex, boolean commi
112112
}
113113

114114
private ErrorAttributeOptions withAllOptions() {
115-
return ErrorAttributeOptions.of(Include.EXCEPTION, Include.STACK_TRACE, Include.MESSAGE,
116-
Include.BINDING_ERRORS);
115+
return ErrorAttributeOptions.of(Include.values());
117116
}
118117

119118
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/error/ErrorAttributeOptions.java

+8-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ private EnumSet<Include> copyIncludes() {
8888
* @return an {@code ErrorAttributeOptions}
8989
*/
9090
public static ErrorAttributeOptions defaults() {
91-
return of();
91+
return of(Include.PATH);
9292
}
9393

9494
/**
@@ -135,7 +135,13 @@ public enum Include {
135135
/**
136136
* Include the binding errors attribute.
137137
*/
138-
BINDING_ERRORS
138+
BINDING_ERRORS,
139+
140+
/**
141+
* Include the request path.
142+
* @since 3.3.0
143+
*/
144+
PATH
139145

140146
}
141147

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/error/DefaultErrorAttributes.java

+4
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
* @author Stephane Nicoll
5858
* @author Michele Mancioppi
5959
* @author Scott Frederick
60+
* @author Moritz Halbritter
6061
* @since 2.0.0
6162
* @see ErrorAttributes
6263
*/
@@ -79,6 +80,9 @@ public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttrib
7980
if (!options.isIncluded(Include.BINDING_ERRORS)) {
8081
errorAttributes.remove("errors");
8182
}
83+
if (!options.isIncluded(Include.PATH)) {
84+
errorAttributes.remove("path");
85+
}
8286
return errorAttributes;
8387
}
8488

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/servlet/error/DefaultErrorAttributes.java

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
* @author Stephane Nicoll
6262
* @author Vedran Pavic
6363
* @author Scott Frederick
64+
* @author Moritz Halbritter
6465
* @since 2.0.0
6566
* @see ErrorAttributes
6667
*/
@@ -100,6 +101,9 @@ public Map<String, Object> getErrorAttributes(WebRequest webRequest, ErrorAttrib
100101
if (!options.isIncluded(Include.BINDING_ERRORS)) {
101102
errorAttributes.remove("errors");
102103
}
104+
if (!options.isIncluded(Include.PATH)) {
105+
errorAttributes.remove("path");
106+
}
103107
return errorAttributes;
104108
}
105109

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/reactive/error/DefaultErrorAttributesTests.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 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.
@@ -50,6 +50,7 @@
5050
* @author Brian Clozel
5151
* @author Stephane Nicoll
5252
* @author Scott Frederick
53+
* @author Moritz Halbritter
5354
*/
5455
class DefaultErrorAttributesTests {
5556

@@ -212,6 +213,14 @@ void includeTrace() {
212213
assertThat(attributes.get("trace").toString()).startsWith("java.lang");
213214
}
214215

216+
@Test
217+
void includePathByDefault() {
218+
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
219+
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, NOT_FOUND),
220+
ErrorAttributeOptions.defaults());
221+
assertThat(attributes).containsEntry("path", "/test");
222+
}
223+
215224
@Test
216225
void includePath() {
217226
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
@@ -220,6 +229,14 @@ void includePath() {
220229
assertThat(attributes).containsEntry("path", "/test");
221230
}
222231

232+
@Test
233+
void excludePath() {
234+
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();
235+
Map<String, Object> attributes = this.errorAttributes.getErrorAttributes(buildServerRequest(request, NOT_FOUND),
236+
ErrorAttributeOptions.of());
237+
assertThat(attributes).doesNotContainEntry("path", "/test");
238+
}
239+
223240
@Test
224241
void includeLogPrefix() {
225242
MockServerHttpRequest request = MockServerHttpRequest.get("/test").build();

0 commit comments

Comments
 (0)