I want to have the following flow:
Request -> 1) Validate JSON Body -> 2) Validate JSON for Security Concerns -> ...etc
And Throw exceptions / give appropriate JSON Responses in 1) or 2).
I have tried to use Interceptors and Filters.
Filters: I can modify the body by having a Request Wrapper and then passing it along the chain to the JSON Validation, however when I throw exceptions they are not intercepted by the #ControllerAdvice Exception Handler, which works for everything else. From what I have read this is by design...? Also I have tried to set a response manually, example below, but it seems spring boot changes the status code to 405.
response.getWriter().write("{\"test\" : \"test\"");
response.sendError(400);
Interceptors: I get an error because I am reading the body more than once, I can not see how to set the Custom HttpServletRequestWrapper I have made before the interceptors run.
I am after a way of implementing this scenario.
Any help would be greatly appreciated. Thanks!
I realised that because Filters are run first I can modify the request by using a Request Wrapper. Then I use an interceptor to run the security and json checks.
I would still like to know how to modify responses in a Filter though, I do not like how spring boot will change it even though you already have a response code.
Related
I am creating an controller where there is certain attributes in json which is an doing in postman a POST request like this if all attributes are posted then its fine
if one then is missing then it would look like this
i want this response when some attribute is missing how to implement this
This is normally implemented in two steps:
Implement a validation mechanism for the method that handles the incoming request. Normally you would throw an exception here if the input is incorrect, in your example a missing JSON key.
Implement a global error handler that will process the exception from point 1 and format the response as JSON.
For point 1 the usual choice is the Java Bean Validation framework because it's integrated with Spring Boot and allows to define validation constraints with annotations like #NotEmpty. You can take a look at this example.
For point 2 the usual choice is #RestControllerAdvice or #ControllerAdvice. You would have to understand your service web server setup to implement it properly e.g. it might behave differently if you use Spring WebFlux.
Does Spring throw HttpRequestMethodNotSupportedException when a request body is not valid and #Valid (or #Validated) is used? I really expected MethodArgumentNotValidException.
Details: I have a small REST server built on Spring-Boot version 2.2.4. One of the methods looks like this:
#PostMapping("/yapp")
public Yapp kickYapp(#Validated #RequestBody YappDescriptor yappDescriptor) {
logger.debug("kickYapp descriptor {}", yappDescriptor);
doWork(yappDescriptor);
}
The YappDescriptor has annotations like "required" but nothing for valid values, ranges, etc. When I POST a well-formed JSON object with values for all the required fields as defined in the YappDescriptor POJO, the Spring controller method is found and invoked as expected.
I tried a couple error scenarios:
1) If I POST a well-formed JSON object that has only null values for the expected fields, the method is found and entered as expected.
2) If I POST a well-formed JSON object with a key that does not match any of the POJO's fields, the method is NOT found and entered. In watching class org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler I see the exception is HttpRequestMethodNotSupportedException and the server answers 405 "Request method 'POST' not supported".
In this controller class, kickYapp is the only POST-mapped method at the specified path, so I think that answer is pretty confusing. Altho I'm definitely sending a bad request (unexpected data), I am surprised my POST-mapped method is not found and invoked.
This post Validating if request body in HTTP POST request is null in Spring Boot controller suggests I should be seeing HttpMessageNotReadableException which would be helpful, but I never get that exception.
Many other questions on SO seem to be about enabling validation of request bodies, like Spring 4.1.7 validate request body , but I seem to be past that.
Thanks in advance for helping me understand this behavior and maybe improve the server to help my users discover their errors more easily (which saves me time :). Thought I could maybe extend that method to accept a BindingResult parameter and report errors that way, but that's a non-starter if the controller method is never entered.
Update to respond to comments: yes I could have used #Valid. In my tests annotation #javax.validation.Valid and #org.springframework.validation.annotation.Validated have the same effect, both turned on validation of the RequestBody parameter.
why not use #Valid?
like so:
public ResponseEntity<SalaryDto> update(#Valid #RequestBody SalaryDto subject)
and don't forget to use javax.validation validation annotations in your request body object
I want to write an #ExceptionHandler so JSON requests will get an error response in JSON as well. For non-JSON requests, I want the servlet container to send its default HTML response.
To do this, I'll need to do some content negotiation. Spring MVC handles it for normal requests via annotations, but no such annotation is available for #ExceptionHandlers.
I am wondering how can I programmatically call the content negotiation code?
So apparently, content negotiation happens after the error handler is called, so I have to do most of the heavy lifting myself.
The method to use is ContentNegotiationManager.resolveMediaTypes(), which gives a list of types that one will have to go through and make a decision.
An example of how to do this can be found the source of ContentNegotiatingViewResolver.getMediaTypes()
I have a Spring rest service using Spring 3.1.0.RELEASE. Here is the relevant code for the service call in question:
#RequestMapping(value="/{var1}", method=RequestMethod.GET, produces="application/json")
#ResponseBody
public String getSomeStuff(#PathVariable final String var1) {
return myJsonString;
}
If I call this using the following curl command, it happily returns me my json string with a content-type of application/xml whereas I would expect a 406 based on the Spring 3.1 docs:
curl -v -H "Accept: application/xml" http://localhost:8080/MyServiceSite/myvalue
There is no extra configuration in my app for this service (no serialization), I am returning raw json with no post-processing for the service configured. I'm certain I have missed something, can anyone point out anything I may have missed?
Edit: Here is the documentation I was looking at when attempting to get this working. Specifically section 16.3.2.5. My code is very similar except that their code looks like it assumes config setup to let Spring handle serialization. Perhaps the produces does not work when bypassing the Spring serialization?
Edit: I changed my expectation for the response code. A 415 would indicate I was sending improper content in my request body whereas 406 is proper for having an accept header that doesn't jive with the content type of the server.
Anyway, I have changed this method do return a Map and added config for it to serialize to json and now if I send an invalid content type from the client I get the proper 406 response. It seems that maybe the "produces" setting is ignored when the output of the method is not being serialized.
The produces condition is new to Spring MVC 3.1 and is only supported with the RequestMappingHandlerMapping and related #MVC support classes, also new in Spring 3.1. My guess is that you're using the 3.0 #MVC support classes, which do not support the produces condition. Your code otherwise is correct and so are your expectations of what should happen.
The use of headers="Accept=application/json" is unnecessary in 3.1. That's exactly what the produces condition was introduced for.
What about the headers attribute for the #RequestMapping. You could set the Accept header in there. Something like:
#RequestMapping(value="/{var1}", method=RequestMethod.GET, produces="application/json", headers = "Accept=application/json")
#ResponseBody
public String getSomeStuff(#PathVariable final String var1) {
return myJsonString;
}
I don't know how Spring would handle a request to that path without a matching header. If it doesn't give what you want you might need to define a similar mapping without the headers and have it send back a ResponseEntity and set the response code or something, but I would hope it would handle it appropriately.
I have written a custom interceptor that does some parameter validation. I want to be able to return an error code and serialize a JAXB-annotated class as the response body.
If I throw a WebApplicationException, it doesn't have any special processing done to serialize the Response object inside (which makes sense; I assume that is done by another interceptor).
How should I go about stopping the interceptor chain but still have JAXB serialize the response entity?
Well, at least in the CXF JAX-RS interceptor flow, if you set:
message.getExchange().put(Response.class, response);
...then the actual service does not get invoked, while the other phases do get invoked. Haven't dug in to the CXF code to see where that logic kicks in.
So I built a response like this:
Response response = Response
.status(Response.Status.FORBIDDEN)
.entity(new ErrorEntity("This is a JAXB object with an error string"))
.build();
I also have some custom authentication running in a CXF JAX-RS filter and I only want to check the parameters when the authentication is alright, so I set my parameter interceptor class to run during the PRE_INVOKE phase.