2929import org .springframework .boot .autoconfigure .web .reactive .HttpHandlerAutoConfiguration ;
3030import org .springframework .boot .autoconfigure .web .reactive .ReactiveWebServerFactoryAutoConfiguration ;
3131import org .springframework .boot .autoconfigure .web .reactive .WebFluxAutoConfiguration ;
32+ import org .springframework .boot .test .context .assertj .AssertableReactiveWebApplicationContext ;
3233import org .springframework .boot .test .context .runner .ReactiveWebApplicationContextRunner ;
3334import org .springframework .boot .testsupport .rule .OutputCapture ;
3435import org .springframework .context .annotation .Configuration ;
4243import org .springframework .web .bind .annotation .RestController ;
4344import org .springframework .web .server .ResponseStatusException ;
4445import org .springframework .web .server .ServerWebExchange ;
46+ import org .springframework .web .server .WebFilter ;
47+ import org .springframework .web .server .WebFilterChain ;
4548
4649import static org .assertj .core .api .Assertions .assertThat ;
4750import static org .assertj .core .api .Assertions .assertThatExceptionOfType ;
@@ -57,6 +60,8 @@ public class DefaultErrorWebExceptionHandlerIntegrationTests {
5760 @ Rule
5861 public OutputCapture outputCapture = new OutputCapture ();
5962
63+ private final LogIdFilter logIdFilter = new LogIdFilter ();
64+
6065 private ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner ()
6166 .withConfiguration (AutoConfigurations .of (
6267 ReactiveWebServerFactoryAutoConfiguration .class ,
@@ -71,15 +76,15 @@ public class DefaultErrorWebExceptionHandlerIntegrationTests {
7176 @ Test
7277 public void jsonError () {
7378 this .contextRunner .run ((context ) -> {
74- WebTestClient client = WebTestClient .bindToApplicationContext (context )
75- .build ();
79+ WebTestClient client = getWebClient (context );
7680 client .get ().uri ("/" ).exchange ().expectStatus ()
7781 .isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR ).expectBody ()
7882 .jsonPath ("status" ).isEqualTo ("500" ).jsonPath ("error" )
7983 .isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR .getReasonPhrase ())
8084 .jsonPath ("path" ).isEqualTo (("/" )).jsonPath ("message" )
8185 .isEqualTo ("Expected!" ).jsonPath ("exception" ).doesNotExist ()
82- .jsonPath ("trace" ).doesNotExist ();
86+ .jsonPath ("trace" ).doesNotExist ().jsonPath ("logPrefix" )
87+ .isEqualTo (this .logIdFilter .getLogId ());
8388 this .outputCapture .expect (Matchers .allOf (
8489 containsString ("500 Server Error for HTTP GET \" /\" " ),
8590 containsString ("java.lang.IllegalStateException: Expected!" )));
@@ -89,20 +94,19 @@ public void jsonError() {
8994 @ Test
9095 public void notFound () {
9196 this .contextRunner .run ((context ) -> {
92- WebTestClient client = WebTestClient .bindToApplicationContext (context )
93- .build ();
97+ WebTestClient client = getWebClient (context );
9498 client .get ().uri ("/notFound" ).exchange ().expectStatus ().isNotFound ()
9599 .expectBody ().jsonPath ("status" ).isEqualTo ("404" ).jsonPath ("error" )
96100 .isEqualTo (HttpStatus .NOT_FOUND .getReasonPhrase ()).jsonPath ("path" )
97- .isEqualTo (("/notFound" )).jsonPath ("exception" ).doesNotExist ();
101+ .isEqualTo (("/notFound" )).jsonPath ("exception" ).doesNotExist ()
102+ .jsonPath ("logPrefix" ).isEqualTo (this .logIdFilter .getLogId ());
98103 });
99104 }
100105
101106 @ Test
102107 public void htmlError () {
103108 this .contextRunner .run ((context ) -> {
104- WebTestClient client = WebTestClient .bindToApplicationContext (context )
105- .build ();
109+ WebTestClient client = getWebClient (context );
106110 String body = client .get ().uri ("/" ).accept (MediaType .TEXT_HTML ).exchange ()
107111 .expectStatus ().isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR )
108112 .expectHeader ().contentType (MediaType .TEXT_HTML )
@@ -117,14 +121,14 @@ public void htmlError() {
117121 @ Test
118122 public void bindingResultError () {
119123 this .contextRunner .run ((context ) -> {
120- WebTestClient client = WebTestClient .bindToApplicationContext (context )
121- .build ();
124+ WebTestClient client = getWebClient (context );
122125 client .post ().uri ("/bind" ).contentType (MediaType .APPLICATION_JSON )
123126 .syncBody ("{}" ).exchange ().expectStatus ().isBadRequest ().expectBody ()
124127 .jsonPath ("status" ).isEqualTo ("400" ).jsonPath ("error" )
125128 .isEqualTo (HttpStatus .BAD_REQUEST .getReasonPhrase ()).jsonPath ("path" )
126129 .isEqualTo (("/bind" )).jsonPath ("exception" ).doesNotExist ()
127- .jsonPath ("errors" ).isArray ().jsonPath ("message" ).isNotEmpty ();
130+ .jsonPath ("errors" ).isArray ().jsonPath ("message" ).isNotEmpty ()
131+ .jsonPath ("logPrefix" ).isEqualTo (this .logIdFilter .getLogId ());
128132 });
129133 }
130134
@@ -134,63 +138,62 @@ public void includeStackTraceOnParam() {
134138 .withPropertyValues ("server.error.include-exception=true" ,
135139 "server.error.include-stacktrace=on-trace-param" )
136140 .run ((context ) -> {
137- WebTestClient client = WebTestClient .bindToApplicationContext (context )
138- .build ();
141+ WebTestClient client = getWebClient (context );
139142 client .get ().uri ("/?trace=true" ).exchange ().expectStatus ()
140143 .isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR ).expectBody ()
141144 .jsonPath ("status" ).isEqualTo ("500" ).jsonPath ("error" )
142145 .isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR .getReasonPhrase ())
143146 .jsonPath ("exception" )
144147 .isEqualTo (IllegalStateException .class .getName ())
145- .jsonPath ("trace" ).exists ();
148+ .jsonPath ("trace" ).exists ().jsonPath ("logPrefix" )
149+ .isEqualTo (this .logIdFilter .getLogId ());
146150 });
147151 }
148152
149153 @ Test
150- public void alwaysIncludeStackTrace () throws Exception {
154+ public void alwaysIncludeStackTrace () {
151155 this .contextRunner .withPropertyValues ("server.error.include-exception=true" ,
152156 "server.error.include-stacktrace=always" ).run ((context ) -> {
153- WebTestClient client = WebTestClient .bindToApplicationContext (context )
154- .build ();
157+ WebTestClient client = getWebClient (context );
155158 client .get ().uri ("/?trace=false" ).exchange ().expectStatus ()
156159 .isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR ).expectBody ()
157160 .jsonPath ("status" ).isEqualTo ("500" ).jsonPath ("error" )
158161 .isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR .getReasonPhrase ())
159162 .jsonPath ("exception" )
160163 .isEqualTo (IllegalStateException .class .getName ())
161- .jsonPath ("trace" ).exists ();
164+ .jsonPath ("trace" ).exists ().jsonPath ("logPrefix" )
165+ .isEqualTo (this .logIdFilter .getLogId ());
162166 });
163167 }
164168
165169 @ Test
166170 public void neverIncludeStackTrace () {
167171 this .contextRunner .withPropertyValues ("server.error.include-exception=true" ,
168172 "server.error.include-stacktrace=never" ).run ((context ) -> {
169- WebTestClient client = WebTestClient .bindToApplicationContext (context )
170- .build ();
173+ WebTestClient client = getWebClient (context );
171174 client .get ().uri ("/?trace=true" ).exchange ().expectStatus ()
172175 .isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR ).expectBody ()
173176 .jsonPath ("status" ).isEqualTo ("500" ).jsonPath ("error" )
174177 .isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR .getReasonPhrase ())
175178 .jsonPath ("exception" )
176179 .isEqualTo (IllegalStateException .class .getName ())
177- .jsonPath ("trace" ).doesNotExist ();
178-
180+ .jsonPath ("trace" ).doesNotExist (). jsonPath ( "logPrefix" )
181+ . exists ();
179182 });
180183 }
181184
182185 @ Test
183186 public void statusException () {
184187 this .contextRunner .withPropertyValues ("server.error.include-exception=true" )
185188 .run ((context ) -> {
186- WebTestClient client = WebTestClient .bindToApplicationContext (context )
187- .build ();
189+ WebTestClient client = getWebClient (context );
188190 client .get ().uri ("/badRequest" ).exchange ().expectStatus ()
189191 .isBadRequest ().expectBody ().jsonPath ("status" )
190192 .isEqualTo ("400" ).jsonPath ("error" )
191193 .isEqualTo (HttpStatus .BAD_REQUEST .getReasonPhrase ())
192194 .jsonPath ("exception" )
193- .isEqualTo (ResponseStatusException .class .getName ());
195+ .isEqualTo (ResponseStatusException .class .getName ())
196+ .jsonPath ("logPrefix" ).isEqualTo (this .logIdFilter .getLogId ());
194197 });
195198 }
196199
@@ -200,14 +203,14 @@ public void defaultErrorView() {
200203 .withPropertyValues ("spring.mustache.prefix=classpath:/unknown/" ,
201204 "server.error.include-stacktrace=always" )
202205 .run ((context ) -> {
203- WebTestClient client = WebTestClient .bindToApplicationContext (context )
204- .build ();
206+ WebTestClient client = getWebClient (context );
205207 String body = client .get ().uri ("/" ).accept (MediaType .TEXT_HTML )
206208 .exchange ().expectStatus ()
207209 .isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR ).expectHeader ()
208210 .contentType (MediaType .TEXT_HTML ).expectBody (String .class )
209211 .returnResult ().getResponseBody ();
210212 assertThat (body ).contains ("Whitelabel Error Page" )
213+ .contains (this .logIdFilter .getLogId ())
211214 .contains ("<div>Expected!</div>" ).contains (
212215 "<div style='white-space:pre-wrap;'>java.lang.IllegalStateException" );
213216 });
@@ -218,14 +221,14 @@ public void escapeHtmlInDefaultErrorView() {
218221 this .contextRunner
219222 .withPropertyValues ("spring.mustache.prefix=classpath:/unknown/" )
220223 .run ((context ) -> {
221- WebTestClient client = WebTestClient .bindToApplicationContext (context )
222- .build ();
224+ WebTestClient client = getWebClient (context );
223225 String body = client .get ().uri ("/html" ).accept (MediaType .TEXT_HTML )
224226 .exchange ().expectStatus ()
225227 .isEqualTo (HttpStatus .INTERNAL_SERVER_ERROR ).expectHeader ()
226228 .contentType (MediaType .TEXT_HTML ).expectBody (String .class )
227229 .returnResult ().getResponseBody ();
228230 assertThat (body ).contains ("Whitelabel Error Page" )
231+ .contains (this .logIdFilter .getLogId ())
229232 .doesNotContain ("<script>" ).contains ("<script>" );
230233 });
231234 }
@@ -235,22 +238,21 @@ public void testExceptionWithNullMessage() {
235238 this .contextRunner
236239 .withPropertyValues ("spring.mustache.prefix=classpath:/unknown/" )
237240 .run ((context ) -> {
238- WebTestClient client = WebTestClient .bindToApplicationContext (context )
239- .build ();
241+ WebTestClient client = getWebClient (context );
240242 String body = client .get ().uri ("/notfound" )
241243 .accept (MediaType .TEXT_HTML ).exchange ().expectStatus ()
242244 .isNotFound ().expectHeader ().contentType (MediaType .TEXT_HTML )
243245 .expectBody (String .class ).returnResult ().getResponseBody ();
244246 assertThat (body ).contains ("Whitelabel Error Page" )
247+ .contains (this .logIdFilter .getLogId ())
245248 .contains ("type=Not Found, status=404" );
246249 });
247250 }
248251
249252 @ Test
250253 public void responseCommitted () {
251254 this .contextRunner .run ((context ) -> {
252- WebTestClient client = WebTestClient .bindToApplicationContext (context )
253- .build ();
255+ WebTestClient client = getWebClient (context );
254256 assertThatExceptionOfType (RuntimeException .class )
255257 .isThrownBy (
256258 () -> client .get ().uri ("/commit" ).exchange ().expectStatus ())
@@ -263,8 +265,7 @@ public void responseCommitted() {
263265 public void whitelabelDisabled () {
264266 this .contextRunner .withPropertyValues ("server.error.whitelabel.enabled=false" ,
265267 "spring.mustache.prefix=classpath:/unknown/" ).run ((context ) -> {
266- WebTestClient client = WebTestClient .bindToApplicationContext (context )
267- .build ();
268+ WebTestClient client = getWebClient (context );
268269 client .get ().uri ("/notfound" ).accept (MediaType .TEXT_HTML ).exchange ()
269270 .expectStatus ().isNotFound ().expectBody ().isEmpty ();
270271 });
@@ -276,8 +277,7 @@ public void exactStatusTemplateErrorPage() {
276277 .withPropertyValues ("server.error.whitelabel.enabled=false" ,
277278 "spring.mustache.prefix=" + getErrorTemplatesLocation ())
278279 .run ((context ) -> {
279- WebTestClient client = WebTestClient .bindToApplicationContext (context )
280- .build ();
280+ WebTestClient client = getWebClient (context );
281281 String body = client .get ().uri ("/notfound" )
282282 .accept (MediaType .TEXT_HTML ).exchange ().expectStatus ()
283283 .isNotFound ().expectBody (String .class ).returnResult ()
@@ -292,8 +292,7 @@ public void seriesStatusTemplateErrorPage() {
292292 .withPropertyValues ("server.error.whitelabel.enabled=false" ,
293293 "spring.mustache.prefix=" + getErrorTemplatesLocation ())
294294 .run ((context ) -> {
295- WebTestClient client = WebTestClient .bindToApplicationContext (context )
296- .build ();
295+ WebTestClient client = getWebClient (context );
297296 String body = client .get ().uri ("/badRequest" )
298297 .accept (MediaType .TEXT_HTML ).exchange ().expectStatus ()
299298 .isBadRequest ().expectBody (String .class ).returnResult ()
@@ -302,21 +301,41 @@ public void seriesStatusTemplateErrorPage() {
302301 });
303302 }
304303
305- private String getErrorTemplatesLocation ( ) {
306- String packageName = getClass (). getPackage (). getName ();
307- return "classpath:/" + packageName . replace ( '.' , '/' ) + "/templates/" ;
304+ private WebTestClient getWebClient ( AssertableReactiveWebApplicationContext context ) {
305+ return WebTestClient . bindToApplicationContext ( context ). webFilter ( this . logIdFilter )
306+ . build () ;
308307 }
309308
310309 @ Test
311310 public void invalidAcceptMediaType () {
312311 this .contextRunner .run ((context ) -> {
313- WebTestClient client = WebTestClient .bindToApplicationContext (context )
314- .build ();
312+ WebTestClient client = getWebClient (context );
315313 client .get ().uri ("/notfound" ).header ("Accept" , "v=3.0" ).exchange ()
316314 .expectStatus ().isEqualTo (HttpStatus .NOT_FOUND );
317315 });
318316 }
319317
318+ private String getErrorTemplatesLocation () {
319+ String packageName = getClass ().getPackage ().getName ();
320+ return "classpath:/" + packageName .replace ('.' , '/' ) + "/templates/" ;
321+ }
322+
323+ private static final class LogIdFilter implements WebFilter {
324+
325+ private String logId ;
326+
327+ @ Override
328+ public Mono <Void > filter (ServerWebExchange exchange , WebFilterChain chain ) {
329+ this .logId = exchange .getLogPrefix ();
330+ return chain .filter (exchange );
331+ }
332+
333+ String getLogId () {
334+ return this .logId ;
335+ }
336+
337+ }
338+
320339 @ Configuration
321340 public static class Application {
322341
0 commit comments