'Force Tomcat a JSON response of BadRequest error: Invalid character found in the request target
I have this spring boot (v2.2.8) app for REST-full service, and I want all responses and errors to be only in JSON.
Almost every error works with @ExceptionHandler
and @RestControllerAdvice
, except from exception that seems to be directly from tomcat:
java.lang.IllegalArgumentException: Invalid character found in the request target [/some/url/with/"invalid/char]. The valid characters are defined in RFC 7230 and RFC 3986
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:499) ~[tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:260) [tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) [tomcat-embed-core-9.0.36.jar:9.0.36]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.36.jar:9.0.36]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.36.jar:9.0.36]
at java.lang.Thread.run(Thread.java:834) [?:?]
I know about many answers here on this topic, but I do not want to allow special characters. All I want is to return error response in JSON, instead of this HTML page.
<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 – Bad Request</h1></body></html>%
I can't find any solution on how to customise tomcat error page to return JSON format. Also basic spring boot error handling is ignored with this specific error, I suppose because it is thrown much sooner in process on tomcat level and spring doesn't even know about it?
Is there a way that tomcat will default all responses to JSON?
Solution 1:[1]
There is an issue with spring boot that is about customizing the white label HTML page: issue with example
The solution is to add a custom error valve to your spring context:
public class ApiConfiguration implements WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory> {
@Override
public void customize(ConfigurableTomcatWebServerFactory factory) {
factory.addContextCustomizers((context) -> {
Container parent = context.getParent();
if (parent instanceof StandardHost) {
((StandardHost) parent).setErrorReportValveClass(CustomTomcatErrorValve.class.getName());
}
});
}
}
Then you can do the following in the error valve:
public class CustomTomcatErrorValve extends ErrorReportValve {
private static final Logger logger = LoggerFactory.getLogger(CustomTomcatErrorValve.class);
@Override
protected void report(Request request, Response response, Throwable throwable) {
if (!response.setErrorReported())
return;
if (logger.isDebugEnabled())
logger.debug("Tomcat failed to prepare the request for spring (set response code to {}).", response.getStatus(), throwable);
HttpStatus status = HttpStatus.valueOf(response.getStatus());
try {
response.setContentType("application/problem+json");
Writer writer = response.getReporter();
writer.write(String.format("""
{
"title": "%s",
"status": %d
}""", status.getReasonPhrase(), status.value()));
response.finishResponse();
} catch (IOException ignored) {
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Johannes |