Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
S
spring-boot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DEMO
spring-boot
Commits
0d009477
Commit
0d009477
authored
May 12, 2020
by
Scott Frederick
Browse files
Options
Browse Files
Download
Plain Diff
Closes gh-21428
parents
64aac048
6b8d08a6
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
120 additions
and
26 deletions
+120
-26
CompositeHandlerExceptionResolver.java
...figure/web/servlet/CompositeHandlerExceptionResolver.java
+12
-3
CompositeHandlerExceptionResolverTests.java
...e/web/servlet/CompositeHandlerExceptionResolverTests.java
+6
-3
WebMvcEndpointChildContextConfigurationIntegrationTests.java
...MvcEndpointChildContextConfigurationIntegrationTests.java
+102
-20
No files found.
spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/CompositeHandlerExceptionResolver.java
View file @
0d009477
/*
/*
* Copyright 2012-20
19
the original author or authors.
* Copyright 2012-20
20
the original author or authors.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.web.servlet;
...
@@ -19,6 +19,7 @@ package org.springframework.boot.actuate.autoconfigure.web.servlet;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Objects
;
import
java.util.Objects
;
import
java.util.Optional
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletRequest
;
import
javax.servlet.http.HttpServletResponse
;
import
javax.servlet.http.HttpServletResponse
;
...
@@ -36,6 +37,7 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
...
@@ -36,6 +37,7 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
* @author Andy Wilkinson
* @author Andy Wilkinson
* @author Stephane Nicoll
* @author Stephane Nicoll
* @author Phillip Webb
* @author Phillip Webb
* @author Scott Frederick
*/
*/
class
CompositeHandlerExceptionResolver
implements
HandlerExceptionResolver
{
class
CompositeHandlerExceptionResolver
implements
HandlerExceptionResolver
{
...
@@ -50,8 +52,15 @@ class CompositeHandlerExceptionResolver implements HandlerExceptionResolver {
...
@@ -50,8 +52,15 @@ class CompositeHandlerExceptionResolver implements HandlerExceptionResolver {
if
(
this
.
resolvers
==
null
)
{
if
(
this
.
resolvers
==
null
)
{
this
.
resolvers
=
extractResolvers
();
this
.
resolvers
=
extractResolvers
();
}
}
return
this
.
resolvers
.
stream
().
map
((
resolver
)
->
resolver
.
resolveException
(
request
,
response
,
handler
,
ex
))
Optional
<
ModelAndView
>
modelAndView
=
this
.
resolvers
.
stream
()
.
filter
(
Objects:
:
nonNull
).
findFirst
().
orElse
(
null
);
.
map
((
resolver
)
->
resolver
.
resolveException
(
request
,
response
,
handler
,
ex
)).
filter
(
Objects:
:
nonNull
)
.
findFirst
();
modelAndView
.
ifPresent
((
mav
)
->
{
if
(
mav
.
isEmpty
())
{
request
.
setAttribute
(
"javax.servlet.error.exception"
,
ex
);
}
});
return
modelAndView
.
orElse
(
null
);
}
}
private
List
<
HandlerExceptionResolver
>
extractResolvers
()
{
private
List
<
HandlerExceptionResolver
>
extractResolvers
()
{
...
...
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/CompositeHandlerExceptionResolverTests.java
View file @
0d009477
/*
/*
* Copyright 2012-20
19
the original author or authors.
* Copyright 2012-20
20
the original author or authors.
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -38,6 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat;
...
@@ -38,6 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link CompositeHandlerExceptionResolver}.
* Tests for {@link CompositeHandlerExceptionResolver}.
*
*
* @author Madhura Bhave
* @author Madhura Bhave
* @author Scott Frederick
*/
*/
class
CompositeHandlerExceptionResolverTests
{
class
CompositeHandlerExceptionResolverTests
{
...
@@ -62,9 +63,11 @@ class CompositeHandlerExceptionResolverTests {
...
@@ -62,9 +63,11 @@ class CompositeHandlerExceptionResolverTests {
load
(
BaseConfiguration
.
class
);
load
(
BaseConfiguration
.
class
);
CompositeHandlerExceptionResolver
resolver
=
(
CompositeHandlerExceptionResolver
)
this
.
context
CompositeHandlerExceptionResolver
resolver
=
(
CompositeHandlerExceptionResolver
)
this
.
context
.
getBean
(
DispatcherServlet
.
HANDLER_EXCEPTION_RESOLVER_BEAN_NAME
);
.
getBean
(
DispatcherServlet
.
HANDLER_EXCEPTION_RESOLVER_BEAN_NAME
);
ModelAndView
resolved
=
resolver
.
resolveException
(
this
.
request
,
this
.
response
,
null
,
HttpRequestMethodNotSupportedException
exception
=
new
HttpRequestMethodNotSupportedException
(
"POST"
);
new
HttpRequestMethodNotSupportedException
(
"POST"
)
);
ModelAndView
resolved
=
resolver
.
resolveException
(
this
.
request
,
this
.
response
,
null
,
exception
);
assertThat
(
resolved
).
isNotNull
();
assertThat
(
resolved
).
isNotNull
();
assertThat
(
resolved
.
isEmpty
()).
isTrue
();
assertThat
(
this
.
request
.
getAttribute
(
"javax.servlet.error.exception"
)).
isSameAs
(
exception
);
}
}
private
void
load
(
Class
<?>...
configs
)
{
private
void
load
(
Class
<?>...
configs
)
{
...
...
spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfigurationIntegrationTests.java
View file @
0d009477
...
@@ -16,7 +16,12 @@
...
@@ -16,7 +16,12 @@
package
org
.
springframework
.
boot
.
actuate
.
autoconfigure
.
web
.
servlet
;
package
org
.
springframework
.
boot
.
actuate
.
autoconfigure
.
web
.
servlet
;
import
java.util.Collections
;
import
java.util.Map
;
import
java.util.Map
;
import
java.util.function.Consumer
;
import
javax.validation.Valid
;
import
javax.validation.constraints.NotEmpty
;
import
org.junit.jupiter.api.Test
;
import
org.junit.jupiter.api.Test
;
...
@@ -25,15 +30,21 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAu
...
@@ -25,15 +30,21 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAu
import
org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration
;
import
org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration
;
import
org.springframework.boot.actuate.endpoint.annotation.Endpoint
;
import
org.springframework.boot.actuate.endpoint.annotation.Endpoint
;
import
org.springframework.boot.actuate.endpoint.annotation.ReadOperation
;
import
org.springframework.boot.actuate.endpoint.annotation.ReadOperation
;
import
org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint
;
import
org.springframework.boot.autoconfigure.AutoConfigurations
;
import
org.springframework.boot.autoconfigure.AutoConfigurations
;
import
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
;
import
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
;
import
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
;
import
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
;
import
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
;
import
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
;
import
org.springframework.boot.test.context.assertj.AssertableWebApplicationContext
;
import
org.springframework.boot.test.context.runner.ContextConsumer
;
import
org.springframework.boot.test.context.runner.WebApplicationContextRunner
;
import
org.springframework.boot.test.context.runner.WebApplicationContextRunner
;
import
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
;
import
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
;
import
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
;
import
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
;
import
org.springframework.http.MediaType
;
import
org.springframework.http.MediaType
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.ResponseBody
;
import
org.springframework.web.reactive.function.client.ClientResponse
;
import
org.springframework.web.reactive.function.client.ClientResponse
;
import
org.springframework.web.reactive.function.client.WebClient
;
import
org.springframework.web.reactive.function.client.WebClient
;
...
@@ -54,40 +65,80 @@ class WebMvcEndpointChildContextConfigurationIntegrationTests {
...
@@ -54,40 +65,80 @@ class WebMvcEndpointChildContextConfigurationIntegrationTests {
ServletManagementContextAutoConfiguration
.
class
,
WebEndpointAutoConfiguration
.
class
,
ServletManagementContextAutoConfiguration
.
class
,
WebEndpointAutoConfiguration
.
class
,
EndpointAutoConfiguration
.
class
,
DispatcherServletAutoConfiguration
.
class
,
EndpointAutoConfiguration
.
class
,
DispatcherServletAutoConfiguration
.
class
,
ErrorMvcAutoConfiguration
.
class
))
ErrorMvcAutoConfiguration
.
class
))
.
withUserConfiguration
(
FailingEndpoint
.
class
)
.
withUserConfiguration
(
FailingEndpoint
.
class
,
FailingControllerEndpoint
.
class
)
.
withInitializer
(
new
ServerPortInfoApplicationContextInitializer
()).
withPropertyValues
(
.
withInitializer
(
new
ServerPortInfoApplicationContextInitializer
())
"server.port=0"
,
"management.server.port=0"
,
"management.endpoints.web.exposure.include=*"
);
.
withPropertyValues
(
"server.port=0"
,
"management.server.port=0"
,
"management.endpoints.web.exposure.include=*"
,
"server.error.include-exception=true"
,
"server.error.include-message=always"
,
"server.error.include-binding-errors=always"
);
@Test
// gh-17938
@Test
// gh-17938
@SuppressWarnings
(
"unchecked"
)
void
errorEndpointIsUsedWithEndpoint
()
{
void
errorPageAndErrorControllerAreUsed
()
{
this
.
runner
.
run
(
withWebTestClient
((
client
)
->
{
this
.
runner
.
run
((
context
)
->
{
String
port
=
context
.
getEnvironment
().
getProperty
(
"local.management.port"
);
WebClient
client
=
WebClient
.
create
(
"http://localhost:"
+
port
);
ClientResponse
response
=
client
.
get
().
uri
(
"actuator/fail"
).
accept
(
MediaType
.
APPLICATION_JSON
).
exchange
()
ClientResponse
response
=
client
.
get
().
uri
(
"actuator/fail"
).
accept
(
MediaType
.
APPLICATION_JSON
).
exchange
()
.
block
();
.
block
();
Map
<
Object
,
Object
>
body
=
response
.
bodyToMono
(
Map
.
class
).
block
();
Map
<
String
,
?>
body
=
getResponseBody
(
response
);
assertThat
(
body
).
containsEntry
(
"message"
,
""
);
assertThat
(
body
).
hasEntrySatisfying
(
"exception"
,
assertThat
(
body
).
doesNotContainKey
(
"trace"
);
(
value
)
->
assertThat
(
value
).
asString
().
contains
(
"IllegalStateException"
));
});
assertThat
(
body
).
hasEntrySatisfying
(
"message"
,
(
value
)
->
assertThat
(
value
).
asString
().
contains
(
"Epic Fail"
));
}));
}
}
@Test
@Test
void
errorPageAndErrorControllerIncludeDetails
()
{
void
errorPageAndErrorControllerIncludeDetails
()
{
this
.
runner
.
withPropertyValues
(
"server.error.include-stacktrace=always"
,
"server.error.include-message=always"
)
this
.
runner
.
withPropertyValues
(
"server.error.include-stacktrace=always"
,
"server.error.include-message=always"
)
.
run
((
context
)
->
{
.
run
(
withWebTestClient
((
client
)
->
{
String
port
=
context
.
getEnvironment
().
getProperty
(
"local.management.port"
);
WebClient
client
=
WebClient
.
create
(
"http://localhost:"
+
port
);
ClientResponse
response
=
client
.
get
().
uri
(
"actuator/fail"
).
accept
(
MediaType
.
APPLICATION_JSON
)
ClientResponse
response
=
client
.
get
().
uri
(
"actuator/fail"
).
accept
(
MediaType
.
APPLICATION_JSON
)
.
exchange
().
block
();
.
exchange
().
block
();
Map
<
Object
,
Object
>
body
=
response
.
bodyToMono
(
Map
.
class
).
block
();
Map
<
String
,
?>
body
=
getResponseBody
(
response
);
assertThat
(
body
).
containsEntry
(
"message"
,
"Epic Fail"
);
assertThat
(
body
).
hasEntrySatisfying
(
"message"
,
(
value
)
->
assertThat
(
value
).
asString
().
contains
(
"Epic Fail"
));
assertThat
(
body
).
hasEntrySatisfying
(
"trace"
,
(
value
)
->
assertThat
(
value
).
asString
()
assertThat
(
body
).
hasEntrySatisfying
(
"trace"
,
(
value
)
->
assertThat
(
value
).
asString
()
.
contains
(
"java.lang.IllegalStateException: Epic Fail"
));
.
contains
(
"java.lang.IllegalStateException: Epic Fail"
));
});
}));
}
@Test
void
errorEndpointIsUsedWithRestControllerEndpoint
()
{
this
.
runner
.
run
(
withWebTestClient
((
client
)
->
{
ClientResponse
response
=
client
.
get
().
uri
(
"actuator/failController"
).
accept
(
MediaType
.
APPLICATION_JSON
)
.
exchange
().
block
();
Map
<
String
,
?>
body
=
getResponseBody
(
response
);
assertThat
(
body
).
hasEntrySatisfying
(
"exception"
,
(
value
)
->
assertThat
(
value
).
asString
().
contains
(
"IllegalStateException"
));
assertThat
(
body
).
hasEntrySatisfying
(
"message"
,
(
value
)
->
assertThat
(
value
).
asString
().
contains
(
"Epic Fail"
));
}));
}
@Test
void
errorEndpointIsUsedWithRestControllerEndpointOnBindingError
()
{
this
.
runner
.
run
(
withWebTestClient
((
client
)
->
{
ClientResponse
response
=
client
.
post
().
uri
(
"actuator/failController"
)
.
bodyValue
(
Collections
.
singletonMap
(
"content"
,
""
)).
accept
(
MediaType
.
APPLICATION_JSON
).
exchange
()
.
block
();
Map
<
String
,
?>
body
=
getResponseBody
(
response
);
assertThat
(
body
).
hasEntrySatisfying
(
"exception"
,
(
value
)
->
assertThat
(
value
).
asString
().
contains
(
"MethodArgumentNotValidException"
));
assertThat
(
body
).
hasEntrySatisfying
(
"message"
,
(
value
)
->
assertThat
(
value
).
asString
().
contains
(
"Validation failed"
));
assertThat
(
body
).
hasEntrySatisfying
(
"errors"
,
(
value
)
->
assertThat
(
value
).
asList
().
isNotEmpty
());
}));
}
private
ContextConsumer
<
AssertableWebApplicationContext
>
withWebTestClient
(
Consumer
<
WebClient
>
webClient
)
{
return
(
context
)
->
{
String
port
=
context
.
getEnvironment
().
getProperty
(
"local.management.port"
);
WebClient
client
=
WebClient
.
create
(
"http://localhost:"
+
port
);
webClient
.
accept
(
client
);
};
}
@SuppressWarnings
(
"unchecked"
)
private
Map
<
String
,
?>
getResponseBody
(
ClientResponse
response
)
{
return
(
Map
<
String
,
?>)
response
.
bodyToMono
(
Map
.
class
).
block
();
}
}
@Component
@Endpoint
(
id
=
"fail"
)
@Endpoint
(
id
=
"fail"
)
static
class
FailingEndpoint
{
static
class
FailingEndpoint
{
...
@@ -98,4 +149,35 @@ class WebMvcEndpointChildContextConfigurationIntegrationTests {
...
@@ -98,4 +149,35 @@ class WebMvcEndpointChildContextConfigurationIntegrationTests {
}
}
@RestControllerEndpoint
(
id
=
"failController"
)
static
class
FailingControllerEndpoint
{
@GetMapping
String
fail
()
{
throw
new
IllegalStateException
(
"Epic Fail"
);
}
@PostMapping
(
produces
=
"application/json"
)
@ResponseBody
String
bodyValidation
(
@Valid
@RequestBody
TestBody
body
)
{
return
body
.
getContent
();
}
}
public
static
class
TestBody
{
@NotEmpty
private
String
content
;
public
String
getContent
()
{
return
this
.
content
;
}
public
void
setContent
(
String
content
)
{
this
.
content
=
content
;
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment