Java/Spring MaxUploadSizeExceededException post parameters - java

I have a form with a three text fields and a file upload field.
When I reach the MaxUploadSizeExceededException exception I can handle with a class that implements HandlerExceptionResolver.
I have my custom handler class with
resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception exception){ ... }
My problem is that I need a way to pass some variables to Exception handler (the values of other fields in the form) so I can return a ModelAndView that contains these variables. I don't want to redirect to an error page, I want to return to my Form, without losing inserted values.
I've also a "Validator" that validates other fields and it works, but I don't know how to integrate it with MaxUploadSizeExceededException exception.
My controller implements HandlerExceptionResolver
#Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception exception)
{
Map<String, Object> model = new HashMap<String, Object>();
if (exception instanceof MaxUploadSizeExceededException)
{
// this is empty!
Map<String,String[]> paramMap = request.getParameterMap();
// model.put("ticketForm", new TicketForm());
// ticketForm.setId();
model.put("err", exception.getMessage());
return new ModelAndView(inserisciticket", model);
} else
{
model.put("err", "Unexpected error: " + exception.getMessage());
return new ModelAndView("error", model);
}
}
This is the function that is called from the form:
#RequestMapping(value = "/inseriscinuovoticket", method = RequestMethod.POST)
#ResponseBody
public String inseriscinuovoticket(
#RequestParam(value = "idArgomento", required = true, defaultValue = "") String idArgomento,
#RequestParam(value = "oggetto", required = true, defaultValue = "") String oggetto,
#RequestParam(value = "descrizione", required = true, defaultValue = "") String descrizione,
#RequestParam(value = "fileuploaded", required = false) MultipartFile fileuploaded,
#ModelAttribute("ticket") TicketForm ticketForm, BindingResult result, Model model, HttpServletRequest request,
Locale locale) throws IOException { .... }
Can you help me?
------------- EDIT 2 --------------------
I tried the method suggested here
public class MultipartExceptionHandler extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (MaxUploadSizeExceededException e) {
handle(request, response, e);
} catch (ServletException e) {
if(e.getRootCause() instanceof MaxUploadSizeExceededException) {
handle(request, response, (MaxUploadSizeExceededException) e.getRootCause());
} else {
throw e;
}
}
}
private void handle(HttpServletRequest request,
HttpServletResponse response, MaxUploadSizeExceededException e) throws ServletException, IOException {
// null
TicketForm t = (TicketForm)request.getAttribute("ticket");
// null
String idArgomento = (String)request.getAttribute("idArgomento");
response.sendRedirect("inserisciticket");
}
}
But also in the handle and in the filter I CAN'T read form parameters (post data).
How can I do???
Thank you.

The following changes resolved the fileUpload size issues(MaxUploadSizeExceededException) in my application.
Do the following changes in "application.yml" to fix this issue. Setting -1 means, unlimited size allowed.
Spring:
servlet:
multipart:
max-file-size: -1
Spring:
servlet:
multipart:
max-request-size: -1

Related

Problems about comsuming request body for multiple times

I'm a new javaer. Recently I'm working on a new springboot project, and I want to print request body before it enter mvc controller. (To be exact, I want to print request body of post request with contentType:"application/json")
I use a requestWrapper as below.
public class MyRequestWrapper extends HttpServletRequestWrapper {
private byte[] cachedBody = new byte[]{};
private InputStream input = null;
public MyRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
if (request.getContentType() != null && (request.getContentType().contains("multipart/") ||
request.getContentType().contains("/x-www-form-urlencoded"))) {
cachedBody = new byte[]{};
input = request.getInputStream();
} else {
cachedBody = StreamUtils.copyToByteArray(request.getInputStream());
input = new ByteArrayInputStream(cachedBody);
}
}
#Override
public ServletInputStream getInputStream() {
return new ServletInputStream() {
#Override
public int read() throws IOException {
return input.read();
}
}
}
public String getBody() {
return new String(cachedBody);
}
Then, I use a filter to print the request content.
#WebFilter(filterName = "RequestResponseFilter", urlPatterns = "/*", asyncSupported = true)
public class RequestResponseFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
MyRequestWrapper requestWrapper = (MyRequestWrapper) request;
......
System.out.println(requestWrapper.getBody());
......
chain.doFilter(requestWrapper, response);
}
}
Below is my controller.
#PostMapping(value="/test")
public ResponseData<String> test(
#RequestParam("id") String id,
#RequestParam("value") String value) {
ResponseData<String> result = new ResponseData<>();
result.setData(id + value);
result.setCode(Constants.CODE_SUCCESS);
return result;
}
However, when I use postman to test my code, it didn't work well. If I use post method and pass param with content-type:"application/x-www-form-urlencoded", it throws "org.springframework.web.bind.MissingServletRequestParameterException".
What confuse me is that, if I pass param with content-type:"multipart/form-data", it work well.
Besides, I have tried CachedBodyHttpServletRequest which provided by spring. But it couldn't get request content until the request enter controller.
Why the mvc controller failed to get param with annotation #RequestParam? And how can I fix it?
u can get param & body like this
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// param
request.getParameterMap().forEach((k, v) -> System.out.println(k + " : " + v[0]));
// body
byte[] array = StreamUtils.copyToByteArray(request.getInputStream());
System.out.println(new String(array, StandardCharsets.UTF_8));
}
but if u upload file with multipart/form-data then file content can't cast to String, u need tools to resolve it, something like this
if (contentType.startsWith("multipart/form-data")) {
StandardServletMultipartResolver resolver = new StandardServletMultipartResolver();
StandardMultipartHttpServletRequest req = (StandardMultipartHttpServletRequest)resolver.resolveMultipart((HttpServletRequest) request);
System.out.println(req.getMultiFileMap());
System.out.println(req.getParameterMap());
}

ControllerAdvcie doesn't handle all exception with GenericFilterBean

I made a Filter using GenericFilterBean.
public class JwtFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String jwt = resolveToken(httpServletRequest);
String requestURI = httpServletRequest.getRequestURI();
try {
System.out.println(man);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
Authentication authentication = tokenProvider.getAuthentication(jwt);
SecurityContextHolder.getContext().setAuthentication(authentication);
} else {
}
} catch(Exception e) {
System.out.println("yoyo");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid token.");
return ;
}
filterChain.doFilter(servletRequest, servletResponse);
}
And I have ExceptionHanlder to catch every exception.
#ControllerAdvice
#RestController
public class CustomizedResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler{
#Autowired
private WebhookService webhookService;
#ExceptionHandler(Exception.class)
public final ResponseEntity<Object> handleAllException
(Exception ex, WebRequest request){
webhookService.sendMessage(ex.getMessage());
ExceptionResponse exceptionResponse = new ExceptionResponse(HttpStatus.INTERNAL_SERVER_ERROR.value(), false, ex.getMessage());
return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
And if you see in filter, there is an error " System.out.println(man);" since there isn't man variable.
But when the error occurs, it gives the response of spring boot default.
I defined response.sendError , plus, #ExceptionHandler(Exception.class) just in case.
Both two method doesn't work at all.
How can I control the customized response in this case?
Thank you for reading it.

Why Spring change the type of exception

I have a filter for JWT which can throw a JWTDecodeException
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
final JwtService jwtService;
public JwtAuthenticationTokenFilter(JwtService jwtService) {
this.jwtService = jwtService;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = jwtService.extractToken(request);
if (StringUtils.hasLength(token)) {
DecodedJWT decodedJWT = jwtService.validateToken(token);
JwtAuthenticationToken authentication = new JwtAuthenticationToken(decodedJWT);
authentication.setAuthenticated(true);
SecurityContextUtils.setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
}
Also I have a single error handler which handles all exceptions
#RequestMapping("/error")
#ExceptionHandler(Exception.class)
public ResponseEntity<Object> test(Exception ex, WebRequest request) {
if (ex instanceof AccessDeniedException || ex instanceof JWTDecodeException) {
System.out.println(123);
}
return handleExceptionInternal(ex, "test", new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
}
But I can't catch JWTDecodeException because for some reason the 'ex' param store simple Exception. I trace it with debug and saw when my JWTDecodeException becomes Exception, but I can't understand how to avoid it.
Moreover I can use JWTDecodeExceptiont = (JWTDecodeException)request.getAttribute("javax.servlet.error.exception",0); in handler and it will return JWTDecodeException.
Is there a way to get JWTDecodeException not from 'request' but from 'ex'?

How to add Location header to the http response?

I have a Java project and I'm using Servlet in order to handle http requests.
I also using Spring
When I receive a request to create a new object (for example an account), I would like also to return the “location” header with the GET URL of the newly created object.
for example: location: /accounts/1000
I understand the header are added to the Servlet filter (correct me if Im wrong)
public class ApiLogFilter implements Filter {
private static final Logger LOGGER = LoggerFactory.getLogger("apilogger");
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpServletResponse httpServletResponse = ((HttpServletResponse) servletResponse);
httpServletResponse.addHeader( "Location","the location value");
try {
filterChain.doFilter(servletRequest, servletResponse);
} finally {
String queryString = httpServletRequest.getQueryString() != null ? httpServletRequest.getQueryString() : "N/A";
String logMessage = "URL: " + httpServletRequest.getRequestURL() + ", Query String: " + queryString + ", Response Status: " + httpServletResponse.getStatus() ;
LOGGER.info(logMessage);
}
}
#Override
public void destroy() {
}
}
But I don't understand how to get the location value from the API
#RequestMapping("/accounts")
public class IgnoreRuleController {
private AccountService accountService;
public void setIgnoreRuleService(IgnoreRuleService ignoreRuleService) {
this.accountService = ignoreRuleService;
}
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public String createAccount(#RequestBody Account account) {
return new Gson().toJson(accountService.createAccount(account));
}
}
I found solution here
http://learningviacode.blogspot.com/2013/07/post-with-location.html
you didn't need to do anything with the filter.
in the api itself:
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<String> createIgnoreRule(#RequestBody IgnoreRule ignoreRule) {
String response = new Gson().toJson(ignoreRuleService.createIgnoreRule(ignoreRule));
final URI location = ServletUriComponentsBuilder
.fromCurrentServletMapping().path("/ignore_rules/{id}").build()
.expand(ignoreRule.getId()).toUri();
final HttpHeaders headers = new HttpHeaders();
headers.setLocation(location);
final ResponseEntity<String> entity = new ResponseEntity<>(response, headers, HttpStatus.CREATED);
return entity;
}
It's very simple, you can pass the header directly throw your method signature:
#RequestMapping(value="/create-account", method = RequestMethod.POST)
#ResponseBody
public String createAccount(#RequestHeader HttpHeaders httpHeader, #RequestBody Account account) {
var s = httpHeader.get("Location");
System.out.println(s.get(0));
return ...
}
In fact you can pass the whole request also which contains everything (Headers, Body, ...):
#RequestMapping(value="/create-account", method = RequestMethod.POST)
#ResponseBody
public String createAccount(HttpServletRequest httpRequest, #RequestBody Account account) {
var s = httpRequest.getHeader("Location");
System.out.println(s);
return ....
}

Log Request and response in Spring API

I want to implement Rest logging for API using Spring. I tried this:
public static String readPayload(final HttpServletRequest request) throws IOException {
String payloadData = null;
ContentCachingRequestWrapper contentCachingRequestWrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (null != contentCachingRequestWrapper) {
byte[] buf = contentCachingRequestWrapper.getContentAsByteArray();
if (buf.length > 0) {
payloadData = new String(buf, 0, buf.length, contentCachingRequestWrapper.getCharacterEncoding());
}
}
return payloadData;
}
public static String getResponseData(final HttpServletResponse response) throws IOException {
String payload = null;
ContentCachingResponseWrapper wrapper =
WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
if (wrapper != null) {
byte[] buf = wrapper.getContentAsByteArray();
if (buf.length > 0) {
payload = new String(buf, 0, buf.length, wrapper.getCharacterEncoding());
wrapper.copyBodyToResponse();
}
}
return payload;
}
#PostMapping(value = "/v1", consumes = { MediaType.APPLICATION_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaType.APPLICATION_XML_VALUE,
MediaType.APPLICATION_JSON_VALUE })
public PaymentResponse handleMessage(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest requestCacheWrapperObject = new ContentCachingRequestWrapper(request);
requestCacheWrapperObject.getParameterMap();
.raw_request(readPayload(requestCacheWrapperObject))
.raw_response(getResponseData(response))
}
But I get NULL for request and response.
Do you know what is the proper way to get the payload from the request and the response?
So you just need to have your own interceptor.
#Component
public class HttpRequestResponseLoggingInterceptorAdapter extends HandlerInterceptorAdapter {
#Autowired
private LoggingUtils loggingutils;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
loggingutils.preHandle(request, response);
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
#Nullable ModelAndView modelAndView) {
try {
loggingutils.postHandle(request, response);
} catch(Exception e) {
System.out.println("Exception while logging outgoing response");
}
}
}
Once that is done, you need to bind your new interceptor to existing interceptors.
#Configuration
public class InterceptorConfig implements WebMvcConfigurer {
#Autowired
private HttpRequestResponseLoggingInterceptorAdapter httpRequestResponseLoggingInterceptorAdapter;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(httpRequestResponseLoggingInterceptorAdapter);
}
}
Once that is done, your incoming requests for handlemessage method will be intercepted, and can do whatever pre/post processing you want to have.
Logging in this case.
Let me know if this helps.
Sounds like your usecase would be best suited with a class extending spring's org.springframework.web.servlet.handler.HandlerInterceptorAdapter.
Custom interceptors can override preHandle and postHandle - both of which it sounds like you are inclined to use.
EDIT:
// add to wherevere your source code is
public class CustomInterceptor extends HandlerInterceptorAdapter {
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO: use 'request' from param above and log whatever details you want
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// TODO: use 'response' from param above and log whatever details you want
}
}
// add to your context
<mvc:interceptors>
<bean id="customInterceptor" class="your.package.CustomInterceptor"/>
</mvc:interceptors>

Categories

Resources