We can validate input parameters via #Valid annotation before any param, like this:
#RestController // new spring 4 annotation
public class ApplicationServiceApiV1Controller {
#RequestMapping(value = "/response")
public SomeResponse getResponse(#Valid SampleInputParam request) {
return new SomeResponse();
}
}
SampleInputParam own fields can be annotated with any javax.validation.constraints.* annotation and those values will be validated. If validation fail, client receives 400 or 406 http status. Is it possible to validate response as described above? I didn't find that usages with spring-mvc.
Related
In the spring docs here, it is suggested that adding Spring Retry as a dependency will allow us to enable it on a Spring Feign client.
However, when I try the below code:
#Component
#FeignClient(value = "myFeign", url = "${my.url}")
public interface ingestionInterface{
#Retryable(
value = {FeignException.class},
backoff = #Backoff(delay=50000)
)
#RequestMapping(
produces = "application/json",
method = RequestMethod.POST)
Optional<ResponseEntity<Response>> postRequest(#Valid #RequestBody Request request);
}
My application won't compile, with the error:
Bean named 'blah.blah.ingestionInterface' is expected to be of type 'blah.blah.ingestionInterface' but was actually of type 'com.sun.proxy.$Proxy143'
If this is not the way to use Spring Retry with Spring Feign, how is it meant to be used?
I have a Payment entity in my spring boot application. Considering all possible CRUD operations, I'm using spring data rest for read and want to implement a custom create operation. Also delete and update are not allowed for this entity.
So this is my desired URLs and resoponsible component for each one:
GET /payments : PaymentRepository
GET /payments/{id} : PaymentRepository
POST /payments : PaymentController
This is my repository:
#RepositoryRestResource
public interface PaymentRepository extends PagingAndSortingRepository<Payment, Long> {
// disable create and update
#Override
#RestResource(exported = false)
Payment save(Payment entity);
// disable delete
#Override
#RestResource(exported = false)
void delete(Payment entity);
}
And this is my controller:
#RepositoryRestController
#RequestMapping("/payments")
public class PaymentController {
#PostMapping("")
#ResponseBody
public Payment create() {
// some code...
}
}
If I map create operation to a url like POST /payments/create, everything works fine, but If I use the above code and map create to POST /payments, the GET /payments url does not work any more and I get 405 Method Not Allowed error. (GET /payments/{id} is still working)
It seems in this case presence of #PostMapping("") annotation, cause the PaymentController to responsd the GET /payments request and it fails.
I hope my explanations were clear. How can I solve this problem?
The Spring Data REST reference states that:
Sometimes you may want to write a custom handler for a specific resource. To take advantage of Spring Data REST’s settings, message converters, exception handling, and more, use the #RepositoryRestController annotation instead of a standard Spring MVC #Controller or #RestController.
It is not explicitly mentionned, but annotating your controller with #RepositoryRestController also allows you to define a custom behavior for one endpoint while keeping all the other endpoints that Spring automatically generates... On one condition: the #RequestMapping annotation can only be used at the method level (this is actually what is done in the example of the reference documentation).
Your example becomes:
#RepositoryRestController
public class PaymentController {
#PostMapping("/payments")
#ResponseBody
public Payment create() {
// some code...
}
}
With this, you get your custom endpoint mapped to POST /payments requests, plus all endpoints automatically generated by Spring, minus the ones annotated with #RestResource(exported = false).
#BasePathAwareController
#RepositoryRestController
public class PaymentController {
#PostMapping("/payments")
#ResponseBody
public Payment create() {
// some code...
}
}
You should modify your controller in the above way. #BasePathAwareController enables the custom REST URI's to get registered under your base URI.
With the above modification : both API's can work fine.
I have the following setup
#ControllerAdvice
public class AppControllerAdvice extends ResponseEntityExceptionHandler {
#ExceptionHandler({UserInputValidationException.class})
public ResponseEntity<UserInputValidationResponseBody> handleBadInputException(UserInputValidationException ex, WebRequest request) {
return new ResponseEntity<>(
new UserInputValidationResponseBody().setFieldErrors(ex.getFieldErrors()),
HttpStatus.BAD_REQUEST
);
}
}
This is roughly the #RestController that throws well formatted validation exceptions
#RestController
#RequestMapping("api")
public class MyController {
/**
per the answer, BindingResult must immediately follow the #RequestBody or the item being found
*/
#PostMapping
public ResponseEntity<?> foo(#Valid #RequestBody FormPOJO formBody, Principal principal, BindingResult bindingResult) {
// if bindingResult has errors, throw a UserInputValidationException
}
}
And POJOs that I want to bind have JSR-303 validation annotations on them, Spring correctly validate them at Bind time during request parameter binding
However ... while I got this setup to work for a while - then Spring randomly started to bypass the #RestController and #ControllerAdvice
It appears that now I am receiving org.springframework.web.bind.MethodArgumentNotValidException ... i.e. the request is getting short circuited
I am running Spring Boot 1.5.4.RELEASE ...
EDIT following suggestion from another thread, I added
#Order(Ordered.HIGHEST_PRECEDENCE)
to the controller advice ... it only served to make matters worse. now there is absolutely no validation errors - the client only receives a blank message (which was the symptom of the problem for a while before the current issue surfaced without any code changes)
Ok it turns out
An Errors/BindingResult argument is expected to be declared immediately after the model attribute, the #RequestBody or the #RequestPart arguments to which they apply: public org.springframework.http.ResponseEntity com.remo.api.portfolios.PortfolioController.put(java.security.Principal,org.springframework.validation.BindingResult,com.remo.api.portfolios.Portfolio
tl;dr Please go ahead and make sure #RequestBody is declared IMMEDIATELY before BindingResult
In a controller, I can access all the #RequestHeaders using the following code
#RestController
public class MyController {
#RequestMapping(value = "/mypath", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity moveEnrollment(#RequestHeader Map<String, String> headers) {
..invoke business logic
}
}
How do I inject the headers into a Spring service bean which is not a controller? Otherwise i need to pass on this hashmap all over the place.
I know that I can inject HttpServletRequest and then get the headers but it would be easier if it can be injected directly.
What you are missing is that the HttpServletRequest is an instance of one request coming to your web app. It's not a global bean that you can inject in your other classes. It's a new instance with every request.
The same goes with your headers, they are only valid in the context of a request. You can't globally inject them anywhere.
In your controllers they are passed to your controller methods and as far I as I remember they are not available in your non-controller instances.
There are other types of handlers such as ExceptionHandler and Controller advice that you have access the request in the methods but not on arbitrary classes. It has to be in the context of a request.
I have a question about data binding in Spring MVC.
I have a Controller which accepts a JSON request in the form of #RequestBody. I have all the JSR 303 validations in place and it works like a charm.
JSON Request
public class TestJSONRequest {
#Size(min=10,message="{invalid.demo.size}")
String demo;
int code;
}
Controller
#Controller
#RequestMapping("/test")
public class TestController {
public void testEntry(#RequestBody TestJSONRequest jsonRequest,ModelMap map)
Set<ConstraintViolation<TestJSONRequest>> violationList = validator.val(jsonRequest);
....
....
TestJSONResponse response = // Do complex Logic.
modelMap.addattribute("TestJSONResponse",response);
}
}
But JSR 303 validations kick in once the incoming JSON data is bound to the Request object.
If I send ab in the code field of the input JSON request, binding would itself fail.
How do I handle that?
I want to catch those data binding errors and do some kind of generalized error handling in my controller.
Could you please help me out on this?
P.S - I am using Spring 3.0.3
According to the current Spring documentation (V3.1) :
Unlike #ModelAttribute parameters, for which a BindingResult can be used to examine the errors, #RequestBody validation errors always result in a MethodArgumentNotValidException being raised. The exception is handled in the DefaultHandlerExceptionResolver, which sends a 400 error back to the client.
Now you can to tell Spring that you'd like to handle this, by creating a new method, as follows:
#ExceptionHandler(MethodArgumentNotValidException.class)
public String handleValidation(MethodArgumentNotValidException e, ModelMap map) {
List<ObjectError> errors = e.getBindingResult() .getAllErrors();
// your code here...
return "path/to/your/view";
}
Finally, have a read of the Spring docs wrt #ExceptionHandler. There's most likely some useful information there.