How to return Custom Response for MaxUploadSizeExceededException during MultiPart File Upload Request? - spring

I'm using a Spring Boot 2.7.5 Backend for a Multipart File Upload.
I want to return a Custom Response if the uploaded file exceeds the specified upload size limit in the configuration.
I originally tried using an ExceptionHandler in a ControllerAdvice as such:
#ExceptionHandler(value = [MaxUploadSizeExceededException::class])
fun handleSizeLimitExceededException(): ResponseEntity<Any> {
val fileSizeMegabyte = multipartProperties.maxFileSize.toMegabytes().toString()
val message = "The file is too big for the upload. Files are not allowed to be bigger than $fileSizeMegabyte MB"
LOG.warn(message)
return ResponseEntity(CustomErrorDto(FILE_TOO_LARGE, message, mapOf("maxFileSize" to fileSizeMegabyte)), HttpStatus.BAD_REQUEST)
}
Now this code gets executed, but it is ultimately ignored, as the Multipart Exception already gets handled (Connection Aborted) before the ControllerAdvice is even in play.
An exception occurred: java.lang.Throwable: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.lang.IllegalStateException: SRVE8021E: The file being uploaded is too large.
I tried using the spring.servlet.multipart.resolve-lazily: true property which seemed to work for some people in this thread here, as well as using an ErrorController (which is not applicable for my use case, as it is not a Spring MVC application)
Any hints will be much appreciated, so if you ever dealt with this or a similar issue, please let me know!
Thanks in advance!

Related

How can I test my controller for throwing an exception in Kotlin?

So I have a controller with one GET method. I need to test it. When I write in URL request with incorrect iso code of the country, it throws me back a custom exception. So how can I test it?
So here 'UA' is incorrect argument
#Test
fun check_for_incorrect_iso_code() {
mockMvc.perform(get("/countries/UA"))
.andDo(print())
.andExpect(status().is4xxClientError)
}
Test is working, but I need to extend it and check if it throws my custom exception - 'InvalidIsoCodeException' for example.
Thanks for the answer.
Your Java code is throwing an InvalidIsoCodeException but your server/controller cannot throw exceptions. Instead it sends back an HTTP response to the client. The InvalidIsoCodeException is mapped by Spring to a specific response. You're already checking the status of the response with .andExpect(status().is4xxClientError()). You can also verify the body of the response if you want to be more specific.
If you want to test for the exception then you have to test your controller like a normal Java class without MockMVC.

IllegalStateException: async support must be enabled on a servlet and for all filters

I'm attempting to use the return type Callable in a controller in order to release the servlet thread while I process the request, but i'm getting the following error when the spring boot application is deployed on development environment:
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
java.lang.IllegalStateException: Async support must be enabled on a
servlet and for all filters involved in async request processing. This
is done in Java code using the Servlet API or by adding
\"true\" to servlet and filter
declarations in web.xml.
NOTE: I already read several posts about this error but none of them solved my problem.
The particular behavior i'm having is that locally everything works as expected: the servlet thread is released and the request is processed out of band to finally return to the client the desired response.
But when the application is deployed in development environment as mentioned before, things doesn't work as expected.
When testing locally, I checked that the servlet/filters were async supported; to do when debugging i just put a breakpoint in a given filter of ApplicationFilterChain and then inspect the whole chain. There I can inspect the servlet properties (where I see the asyncSupported on true) along with each filter included in the chain (one by one i checked them; all of them had the asyncSupported setted on true).
I also have a custom JwtAuthenticationFilter which extends from AbstractAuthenticationProcessingFilter as part of the authentication phase so I also put a breakpoint in such filter and inspect the chain. There I see the originalChain (the ApplicationFilterChain commented before) and "additionalFilters" collection in which appears my JwtAuthenticationFilter along with the spring security filters. But none of them have asyncSupported property so i'm assuming they aren't part of the error thrown by the development server. Note that as I mentioned locally everything works fine; the error just appear when deploying to development server.
Controller method:
#GetMapping(path = "/export")
public Callable<ResponseEntity> exportData() throws IOException {
return new CustomContextAwareCallable(() -> handleResponse());
}
So my question is: even if each filter on ApplicationFilterChain along with the servlet have asyncSupported on true, why could I receive the error shown above from the server after deploying?
The application isn't deployed as a .WAR on the server, it's just using embedded tomcat (the same I doing locally).
UPDATE: another difference is that in development environment client requests pass through nginx proxy (just wondering if for some reason the request attribute org.apache.catalina.ASYNC_SUPPORTED could be modified there to be false (locally it's coming on true).
Any ideas?
I found a workaround by adding the async support attribute to the current request like:
#GetMapping(path = "/export")
public Callable<ResponseEntity> exportData(HttpServletRequest request) throws IOException {
request.setAttribute(org.apache.catalina.Globals.ASYNC_SUPPORTED_ATTR, true);
return new CustomContextAwareCallable(() -> handleResponse());
}
Since all filters are being async supported along with dispatchServlet i'm using (as commented at the original question) seems that when deploying in the development environment some of the following tomcat valves is setting such attribute on false for the request:
StandardEngineValve
StandardHostValve
StandardContextValve
I verified the embedded tomcat version i'm using locally and it's the same as the development environment: Apache Tomcat/8.5.23
Seems also that the error I showed before:
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
java.lang.IllegalStateException: Async support must be enabled on a
servlet and for all filters involved in async request processing. This
is done in Java code using the Servlet API or by adding \"true\" to
servlet and filter declarations in web.xml.
isn't exclusively tied to servlet & filters as it describes, because any of listed tomcat valves could eventually disallow the request to be async supported.
At the moment I don't have enough details to know why that's happening (it would be great to have the chance of remote-debugging this against the development environment to see how each tomcat valve is handling the request).
For those having a Filter implementation. Instead of setting request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true) to each request in your Controller, you can do the same in doFilter() callback. This is how I did it in Kotlin:
import org.apache.catalina.Globals
import javax.servlet.Filter
import javax.servlet.FilterChain
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.annotation.WebFilter
import javax.servlet.http.HttpServletResponse
#Suppress("unused")
#WebFilter(urlPatterns = ["/v1/*"])
class RequestResponseFilter : Filter {
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, true)
val httpServletResponse = response as HttpServletResponse
httpServletResponse.setHeader("Cache-Control", "max-age=60")
chain.doFilter(request, response)
}
}

Spring integration http outbound gateway logging errors

I have an integration flow where I'm reading messages from a jms channel and sending them through a REST api call using http-outbound-gateway.
Now when I get a 5xx Http server error I'd like to log the the URL of the service along with the response message.
I see that the RestTemplate instance already logs the URL and calls a ResponseErrorHandler which can be a custom implementation.
My problem is: the ResponseErrorHandler only receives a ClientHttpResponse as a parameter and I can't know the URL from that instance. What I need is to log the server error like: 'Error URL - Response ...'
How can I do that? I can't override RestTemplate's error handling and the ResponseErrorHandler doesn't have enough info.
Thank you very much.
Well, what I see there.
The HttpRequestExecutingMessageHandler (<int-http:outbound-gateway>) catches all underlying exceptions:
catch (Exception e) {
throw new MessageHandlingException(requestMessage, "HTTP request execution failed for URI ["
+ (realUri == null ? uri : realUri.toString())
+ "]", e);
}
Including the result of that DefaultResponseErrorHandler from the RestTemplate.
And as you see we use here for the MessageHandlingException uri as well as the requestMessage.
Further this exception will be delegated to that jms channel.
Why isn't that enough for you?
From other side you can add <request-handler-advice-chain> to the <int-http:outbound-gateway> with ExpressionEvaluatingRequestHandlerAdvice to get deal with that MessageHandlingException just in place: http://docs.spring.io/spring-integration/docs/latest-ga/reference/html/messaging-endpoints-chapter.html#expression-advice

Spring mvc controller test + Json response error

I have in my spring mvc test controller:
#Test
public void consultaPorIdJson() throws Exception{
mockMvc.perform(get("/timesheet/consultaporidjson/{id}", 1L))
.andExpect(status().isOk())
.andExpect(content().contentType(TestSupport.APPLICATION_JSON_UTF8))
.andExpect(content().string("{\"id\":1,\"latitude\":\"30.448660206791608\",\"longitude\":\"-44.29684999999995\"}"));
When I trying to run my test I get an error:
java.lang.IllegalStateException: Cannot set error status - response is already committed
I think that is about 2k (I think) of response, but I don't know what must I do to fix it :-(
Does the url works outside of your test? I doubt it does not work as the error suggests you have issue in your implementation where the code is trying to change the status when something is already written in the response.

flex blazeds spring exception translator

I am using spring exception translator to wrap java exception into flex exception.
public void testException()throws Exception{
throw new Exception("my exception");
}
But for some reason, I am getting IllegalAccessError. The code sections are entering the testException and the Translator class.
Question:
Why it trying to get log target level? Can someone help me resolve this please.
Below is the lines from the log:
MyExceptionTranslatorImpl.translate()
class java.lang.IllegalAccessError
MyExceptionTranslatorImpl.translate()
java.lang.IllegalAccessError: tried to access method **flex.messaging.log.Log.getTargetLevel()S** from class flex.messaging.MessageException
MyExceptionTranslatorImpl.translate()
tried to access method
flex.messaging.log.Log.getTargetLevel()S from class flex.messaging.MessageException
[BlazeDS] tried to access method flex.messaging.log.Log.getTargetLevel()S from class flex.messaging.MessageException
[BlazeDS] Serializing AMF/HTTP response
This turned out to be mismatch in jars. Thank you Cornel Creanga for the initial response.
I also verified that throwing an java.lang.exception was enough to catch the error on the client side.

Resources