In our plugin Chaos Monkey For Spring Boot, we are experiencing a strange problem, regarding different ports of the management server.
If we change the default management port then the error messages change. Inputs to our Actuator REST API are @Validated
(in this case against min/max).
Port different via management.server.port
:
https://i.stack.imgur.com/ozRYN.png
Request:
echo '{"level": -2}' | curl -X POST -H "Content-Type: application/json" -d @- http://localhost:8888/actuator/chaosmonkey/assaults
Response:
{
"timestamp": "2020-04-20T13:18:40.267+0000",
"status": 400,
"error": "Bad Request",
"message": "No message available",
"path": "/actuator/chaosmonkey/assaults"
}
Same port as application:
https://i.stack.imgur.com/cAoNf.png
Request:
echo '{"level": -2}' | curl -X POST -H "Content-Type: application/json" -d @- http://localhost:8080/actuator/chaosmonkey/assaults
Response:
{
"timestamp": "2020-04-20T13:15:47.652+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"Min.assaultPropertiesUpdate.level",
"Min.level",
"Min.java.lang.Integer",
"Min"
],
"arguments": [
{
"codes": [
"assaultPropertiesUpdate.level",
"level"
],
"arguments": null,
"defaultMessage": "level",
"code": "level"
},
1
],
"defaultMessage": "must be greater than or equal to 1",
"objectName": "assaultPropertiesUpdate",
"field": "level",
"rejectedValue": -2,
"bindingFailure": false,
"code": "Min"
}
],
"message": "Validation failed for object='assaultPropertiesUpdate'. Error count: 1",
"trace": "org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public org.springframework.http.ResponseEntity<?> de.codecentric.spring.boot.chaos.monkey.endpoints.ChaosMonkeyRestEndpoint.updateAssaultProperties(de.codecentric.spring.boot.chaos.monkey.endpoints.AssaultPropertiesUpdate): [Field error in object 'assaultPropertiesUpdate' on field 'level': rejected value [-2]; codes [Min.assaultPropertiesUpdate.level,Min.level,Min.java.lang.Integer,Min]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [assaultPropertiesUpdate.level,level]; arguments []; default message [level],1]; default message [must be greater than or equal to 1]] \n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:138)\n\tat org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:127)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:167)\n\tat org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:88)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:114)\n\tat org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:104)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:118)\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:526)\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860)\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587)\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\n\tat java.base/java.lang.Thread.run(Thread.java:834)\n",
"path": "/actuator/chaosmonkey/assaults"
}
We're guessing that this might be related to: https://github.com/spring-projects/spring-boot/issues/10560 where the HTTP status code was fixed to be the same in both cases.
I am not 100 % sure this is a bug, or whether this is the intended behavior. We also asked on Stackoverflow (https://stackoverflow.com/questions/61267877/different-actuator-management-server-port-changes-http-response) for any ideas on how to get around the different messages (without too much customization on our side), because we would like to supply our plugin users always the same error message. We would prefer the second, descriptive error message.
Maybe good to know, we did not apply any custom filters in Chaos Monkey for Spring Boot.
Please let me know if there's anything we can do to help.
Spring Version: 2.2.6.RELEASE Chaos Monkey For Spring Boot also needs the appropriate spring-boot-starter-web
Comment From: scottfrederick
I've confirmed this with 2.2.6
.
When the application port and management port are the same, all errors are handled by a Controller
and run through DispatcherServlet
. The DefaultHandlerExceptionResolver
resolves the exception to an empty ModelAndView
, then DispatcherServlet
stores the exception in the request so that the error handler can later retrieve it.
When the management port is different from the application port, actuator exceptions go through a different path. DefaultHandlerExceptionResolver
resolves the exception in the same way, but after that it is handled by CompositeHandlerExceptionResolver
, which just returns the empty ModelAndView
. The result is that the exception is lost in this path.
Comment From: wilkinsona
I think this has caused the regression reported in #21591. 2.2.8 hasn't shipped yet so the 2.2.x regression's only in snapshots. Re-opening so we can address it before 2.2.8 is released.
Comment From: wilkinsona
We now set the javax.servlet.error.exception
attribute on the request:
https://github.com/spring-projects/spring-boot/blob/6b8d08a6e3f32d55a30a059595823c64aab8e8dd/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/CompositeHandlerExceptionResolver.java#L60
This attribute is found by Tomcat and results in a 500 response being sent.