Spring REST Exception Handling FileUploadBase$SizeLimitExceededException - spring-rest

I am using Spring Boot 1.5.3.RELEASE and using a Controller that takes a MultipartFile with some other information as arguments and returns a file.
Now I am facing the org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException when the file exceeds the maximum Sizes.
spring.http.multipart.maxFileSize=17728640
spring.http.multipart.maxRequestSize=17728640
This works well but i need a custom Response and actually the Exception is throwed only at server side before the method call.
Can anyone tell me how can I define a Custom Error Handler that handles this exception and response something like ResponseEntity.status(HttpStatus.CONFLICT).body("size_exceeded")
My Method:
#SuppressWarnings("rawtypes")
#RequestMapping(value = "/{Id}/attachments", method = RequestMethod.POST)
public ResponseEntity addTaskAttachment(#RequestParam("file") MultipartFile file, #PathVariable Long Id,
#CurrentUser User currentUser) {
// Some code here
ResponseEntity.status(HttpStatus.OK).body(attachmentAsByteArray);
}

You are correct in your observation that an Exception Handler with #RestControllerAdvice wouldn't work for multi part exceptions and reason being MultipartFile parsing & validation step preceding the mapping resolver step.
As advised in first accepted answer by geoand for this SO question here , you need to define and register an ErrorController.
Also, note that as already mentioned in that answer , Spring Boot already defines a BasicErrorController that you can extend to add new content types to return a JSON etc ( since default is text/html ) by adding a new public method with #RequestMapping & #Produces .

Related

Can Spring be directed to take a parameter from either the body or the URL?

An argument to a Spring MVC method can be declared as RequestBody or RequestParam. Is there a way to say, "Take this value from either the body, if provided, or the URL parameter, if not"? That is, give the user flexibility to pass it either way which is convenient for them.
You can make both variables and check them both for null later on in your code
like this :
#RequestMapping(value = GET_SOMETHING, params = {"page"}, method = RequestMethod.GET)
public
#ResponseBody
JSONObject getPromoByBusinessId(
#PathVariable("businessId") String businessId, #RequestParam("page") int page,
#RequestParam("valid") Boolean valid,
#RequestParam("q") String promoName) throws Exception {}
and then use a series if if-else to react to requests.
I wrote it to work with any of the three params be null or empty, react to all different scenarios.
To make them optional, see :
Spring Web MVC: Use same request mapping for request parameter and path variable
HttpServletRequest interface should help solve this problem
#RequestMapping(value="/getInfo",method=RequestMethod.POST)
#ResponseBody
public String getInfo(HttpServletRequest request) {
String name=request.getParameter("name");
return name;
}
Now, based on request data coming from body or parameter the value will be picked up
C:\Users\sushil
λ curl http://localhost:8080/getInfo?name=sushil-testing-parameter
sushil-testing-parameter
C:\Users\sushil
λ curl -d "name=sushil-testing-requestbody" http://localhost:8080/getInfo
sushil-testing-requestbody
C:\Users\sushil
λ

How to pass single parameter to contoller in Spring MVC?

I have some questions from a design point of view in Spring Web MVC.
Is it good practice to use Request Object in controller? If not, then what is alternative way to pass pass one text fields value to controller? Do I need to create one new from bean for this single fields?
It depends of the situation, in a few cases I used the HttpServletRequest; for example for writing a file to the output stream.
If you want to get the Request Parameters you can use the annotation #RequestParam, that it´s more easy to get the parameters from the request.
Depends that you want to handle, for example for a form you can use #ModelAttribute and this attribute can be in a session or in the request.
For example:
#Controller
public class YourController {
#RequestMapping(value = "someUrl", method = RequestMethod.GET)
public String someMethod(#RequestParam("someProperty") String myProperty)
{
// ... do some stuff
}
}
Check the documentation here:
#RequestParam
#ModelAttribute
#PathVariable

Spring MVC handler returns String with extra quotes

I'm using Spring 3.1 and I have a handler that should return a String value.
Here's how my handler looks like:
#RequestMapping(value = TEST_HANDLER_PATH, method = RequestMethod.POST)
public ResponseEntity<String> handleTest(HttpServletRequest request,
#RequestParam("parma1") String param) throws Exception {
String ret = ...
...
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "text/plain;charset=utf-8");
return new ResponseEntity<String>(ret, headers, HttpStatus.CREATED);
}
I also tried annotating method with #ResponseBody with return ret; at the end.
In both cases, when I hit the service, I get extra quotes around String value (e.g. "This is a test").
I'm guessing this is due to message conversion. That's why I tried defining Content-Type header, to hit StringHttpMessageConverter explicitly, to no avail.
Had the same problem.
Just make sure you register a org.springframework.http.converter.StringHttpMessageConverter as well as your Jackson one so that Strings are treated literally and not attempted to be converted to JSON (with extra quotes).
Just instantiate with default constructor or constructor with your preferred Charset. The media types should be set for you with the standard internal defaults. If you're configuring via code extending WebMvcConfigurerAdapter then you just add the converters in the configureMessageConverters(List<HttpMessageConverter<?>> converters) method.
In my case, I had over-engineered =)
Had introduced a converter for bean's toString Operations like this:
class SerializableToString implements Converter<Serializable, String>
restricting that (only to my beans), resolved the issue X)
Note: debugging with a breakpoint # org.springframework.core.convert.support.GenericConversionService.getConverter helped.
In a related scenario, I had an IntegrationFlow for a GET that incorrectly requested a transform. Basically the target service would receive the #PathVariable as a quote escaped string
return IntegrationFlows.from("getThing")
.transform(Transformers.toJson())
.handle(
The .transform(Transformers.toJson()) was forcing the strings to be escaped in the URI, so simply removing it - it shouldn't have been there - fixed the issue.
Turns out there was a JSON message converter registered in one of the imports.

How to handle SizeLimitExceededException from CommonsMultipartResolver in Spring WebFlow?

I have the following situation. I have a CommonsMultipartResolver bean configured the following way.
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="2100000" />
And I have a few file upload fields in a Spring Web Flow view state jsp.
Everything works fine if the file is under the limit, but if the file exceeds the limit of 2MB-s I have to add a validation error to the binding result on my form.
My problem is that the multipart file resolver throws a org.apache.commons.fileupload.FileUploadBase.SizeL imitExceededException exception when the file limit is exceeded and I can't find a way to catch this in Spring Web Flow and add my FieldError to the form.
I tried using the on-exception attribute of the transition tag, but if I understand correctly it only works for exceptions that are thrown within Spring Web Flow.
I've also tried to use SimpleMappingExceptionResolver in spring mvc, but I do not want to redirect to a page, I want to handle this exception.
I also found this: https://jira.springsource.org/browse/SWF-158
But it's from version 1.0 and I'm assuming that this has been incorporated since or that a better way was found to handle these situations.
Any ideas on how to deal with this would be greatly appreciated.
Thanks.
In your SimpleMappingExceptionResolver you should be able to override the resolveException method, determine the exception type being caught and handle appropriately.
I've found some old code in our project that seems to be a solution to a similar exception;
public class GeneralMappingExceptionResolver extends SimpleMappingExceptionResolver {
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {
if(exception instanceof MaxUploadSizeExceededException) {
MaxUploadSizeExceededException maxe = (MaxUploadSizeExceededException)exception;
String errorMessage = "Max filesize exceeded, please ensure filesize is too large.");
HashMap<String, Object> model = new HashMap<String, Object>(2);
model.put("errorMessage", errorMessage);
return new ModelAndView("verification/psv/consent", model);
} else {
return super.resolveException(request, response, handler, exception); // Do whatever default behaviour is (ie throw to error page).
}
}
Note that the "verification/psv/consent" is the flow where this exception would have been thrown from and where it needs to return to. We only have the one page that has a file upload.
Obviously the errorMessage is just a parameter passed into the view so will need to be handled and displayed like an error message. You may also need to re-populate any other form fields that were submitted. Hopefully this is a point in the right direction though.

How do I omit ModelAttribute parameters from the ModelAndView returned by a Spring 3 MVC action?

I have a Spring MVC controller with an action that's called using AJAX.
#SessionAttributes({"userContext"})
public class Controller
{
...
#RequestMapping(value = "/my-url", method= { RequestMethods.POST })
public ModelAndView doSomething(#ModelAttribute("userContext") UserContext context,
SessionStatus sessionStatus)
{
BusinessObject obj = doSomeBusinessLogic(context.getUserName());
sessionStatus.setComplete();
ModelAndView mav = new ModelAndView("jsonView");
mav.addObject("someInt", obj.getId());
return mav;
}
}
When I run this action, I get the following exception:
net.sf.json.JSONException: There is a cycle in the hierarchy!
at t.sf.json.util.CycleDetectionStrategy$StrictCycleDetectionStrategy.handleRepeatedReferenceAsObject(CycleDetectionStrategy.java:97)
at net.sf.json.JSONObject._fromBean(JSONObject.java:833)
at net.sf.json.JSONObject.fromObject(JSONObject.java:168)
at org.springframework.web.servlet.view.json.writer.jsonlib.PropertyEditorRegistryValueProcessor.processObjectValue(PropertyEditorRegistryValueProcessor.java:127)
at net.sf.json.JSONObject._fromMap(JSONObject.java:1334)
Truncated. see log file for complete stacktrace
After doing some debugging I found out that Spring is placing the UserContext object onto the ModelAndView that I am returning. If I hard-code my user name and remove the context object from the method's parameters, the action runs successfully. Is there a way to configure Spring to omit the ModelAttribute-annotated parameters from the returned ModelAndView? As you can see, sessionStatus.setComplete() has no effect.
I've had similar problems in the past with #SessionAttributes. By declaring #SessionAttributes({"userContext"}) you're telling Spring that you want "userContext" to always be available in the model, and so Spring has no choice but to send your UserContext object out to the model, just in case you're going to be redirecting or doing something else which might end up at another Controller.
The "solution" (and I didn't like it much, but it worked) was to omit the #SessionAttributes annotation on the controller, add an HttpSession parameter to the necessary methods and "manually" manage what's in it.
I'm interested to see if there's a better way, because it seems #SessionAttributes has tremendous potential to tidy up controller-level code.
I registered a WebArgumentResolver to get to my session variable. This allowed me to keep this session variable out of the response while keeping my action unit testable.
Along with #ModelAttribute, pass #ModelMap as a method argument.
Based on business logic, error conditions -- if you do not need the attribute for certain scenarios, then remove it from the map.
public ModelAndView foo(#ModelAttribute("userContext") UserContext, #ModelMap map){
if(success){
return success.jsp
}
else{
map.remove("userContext");
return "error.jsp"
}
}
Not totally satisfied with having to pass the ModelMap as well, but I did not find any other easier way of doing it.
Cheers!!

Resources