Say I have a class Dog with a field, numberOfLegs, validated to be 4 or less.
class Dog {
#Max(4)
int numberOfLegs;
}
This is a parameter to a controller method, and if validation fails, then Spring sends the relevant error message in the response.
However, the request actually sends up a DogRestDto.
class DogRestDto {
#Max(4)
int legs;
}
I use a converter to change this into a Dog for my controller to use, but the validation is done on Dog rather than DogRestDto, and the error message talks about dog.numberOfLegs rather than dog.legs.
Is there a simple way to tell Spring do the validation on the REST dto prior to conversion, so that the error message makes more sense to the client?
I've achieved this by overriding the mvcUriComponentsContributor method in my DelegatingWebMvcConfiguration to replace the RequestResponseBodyMethodProcessor in the requestMappingHandlerAdapter's argumentResolvers with a implementation that validates the REST DTO. It obtains the REST DTO from a request-scoped bean that I've made to hold the source object currently being converted, which is populated by my implementation of the GenericConversionService.
It's not very pretty. I'd love it if Spring gave me a cleaner way to do this.
Related
I am working on a spring base web application where, we have a few RestControllers and some Request DTO classes. Request DTO contains a token field which needs some validation. So I used spring validators to validate that. After validation, we want to send that field to an external system using another REST API (Just for some kind of analytics logging). The same field is repeated in multiple DTO objects and their controllers. So, I am easily able to define annotations for validators and reuse them across the DTOs. But I am not sure how to process that field after validation succeeds (i.e. call analytics API to consume that field post validation), without mixing it with the core logic of controllers.
Approaches I could think of:
Implement a filter/interceptor and process the field there. But then
there is a limitation that request body can be read only once so I
need to use some alternate ways by creating request wrappers.
Repeat the logic in every controller and it is very error prone as for
every new controller we need to remember to write that code.
But non of these approaches look cleaner. Can someone recommend a better way to achieve that?
Thanks in advance.
You can create a BaseController and implement the method there. Extend this BaseController wherever you need this logging service. Like below.
BaseController.java
class BaseController {
protected void remoteLogging(String name,String token) {
//Calling the remote log services}
}
AppController.java
#Controller
#RequestMapping("register")
public class LeaseController extends BaseController {
#PostMapping("new")
public String new(#Valid #ModelAttribute("registration") Registration registration,BindingResult result){
if(rest.hasErrors(){
remoteLogging("name","token");
}
}
The way to use a Validator interface for Spring #RestController based is to create a validator custom class, implement Validator interface. In the controller class call InitBinder to register the validator class. Then when the REST req comes, the validator for that class is called. This still has a problem for me. Say I have a PersonValidator class that does one type of validation for POST and another type for PUT. Since both these handlers exist in the same REST controller class, how can I run different validations?
Say in the same rest controller class, i want to use PostPersonValidator for POST and PutPersonValidator for PUT. I do not know how to do it.
Just follow this article: http://howtodoinjava.com/2015/02/12/spring-mvc-custom-validator-example/
You basically have to create two separate validators - just as you said (one for POST, one for PUT). Then just call them inside your proper POST/PUT handling methods. Should be pretty straightforward if you follow the linked example.
I'm using Google's Preconditions class to validate user's input data.
But I'm worried about where is the best point of checking user's input data using Preconditions class.
First, I wrote validation check code in Controller like below:
#Controller
...
public void register(ProductInfo data) {
Preconditions.checkArgument(StringUtils.hasText(data.getName()),
"Empty name parameter.");
productService.register(data);
}
#Service
...
public void register(ProductInfo data) {
productDao.register(data);
}
But I thought that register method in Service layer would be using another Controller method like below:
#Controller
...
public void register(ProductInfo data) {
productService.register(data);
}
public void anotherRegister(ProductInfo data) {
productService.register(data);
}
#Service
...
public void register(ProductInfo data) {
Preconditions.checkArgument(StringUtils.hasText(data.getName()),
"Empty name parameter.");
productDao.register(data);
}
On the other hand, the method of service layer would be used in just one controller.
I was confused. Which is the better way of checking preconditions in controller or service?
Thanks in advance.
Ideally you would do it in both places. But you are confusing two different things:
Validation (with error handling)
Defensivie Programming (aka assertions, aka design by contract).
You absolutely should do validation in the controller and defensive programming in your service. And here is why.
You need to validate for forms and REST requests so that you can send a sensible error back to the client. This includes what fields are bad and then doing localization of the error messages, etc... (your current example would send me a horrible 500 error message with a stack trace if ProductInfo.name property was null).
Spring has a solution for validating objects in the controller.
Defensive programming is done in the service layer BUT NOT validation because you don't have access to locale to generate proper error messages. Some people do but Spring doesn't really help you there.
The other reason why validation is not done in the service layer is that the ORM already typically does this through the JSR Bean Validation spec (hibernate) but it doesn't generate sensible error messages.
One strategy people do is to create their own preconditions utils library that throws custom derived RuntimeExceptions instead of guava's (and commons lang) IllegalArgumentException and IllegalStateException and then try...catch the exceptions in the controller converting them to validation error messages.
There is no "better" way. If you think that the service is going to be used by multiple controllers (or other pieces of code), then it may well make sense to do the checks there. If it's important to your application to check invalid requests while they're still in the controller, it may well make sense to do the checks there. These two, as you have noticed, are not mutually exclusive. You might have to check twice to cover both scenarios.
Another possible solution: use Bean Validation (JSR-303) to put the checks (preconditions) onto the ProductInfo bean itself. That way you only specify the checks once, and anything that needs to can quickly validate the bean.
Preconditions, validations, whether simple or business should be handled at the filter layer or by interceptors, even before reaching the controller or service layer.
The danger if you check it in your controller layer, you are violating the single responsibility principle of a controller, whose sole purpose is to delegate request and response.
Putting preconditions in service layer is introducing cross cutting concerns to the core business.
Filter or inceptor is built for this purpose. Putting preconditions at the filter layer or in interceptors also allow you to “pick and match” rules you can place in the stack for each servlet request, thus not confining a particular rule to only one servlet request or introduce duplication.
I think in your special case you need to to check it on Service layer and return exception to Controller in case of data integrity error.
#controller
public class MyController{
#ExceptionHandler(MyDataIntegrityExcpetion.class)
public String handleException(MyDataIntegrityExcpetion ex, HttpServletRequest request) {
//do someting on exception or return some view.
}
}
It also depend on what you are doing in controller. whether you return View or just using #ResponseBody Annotation. Spring MVC has nice "out of the box" solution for input/dat validation I recommend you to check this libraries out.
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/validation.html
I use Jersey framework to communicate (marshalling and unmarshalling object and xml) with REST api. I send data (object has lot attributes) this way:
.
.
ClientResponse response = webResource.type("application/xml").post(ClientResponse.class, object);
.
I would like to ask how can I validate some object attributes (for example private String code in Object should be in format of two numbers etc.)
aYou mean in the service that receives the object? How have you tried? It comes in as an object, or whatever you want it to come in as. We frequently take in Map<String,Object> and then do validation on that map (if we need to decide what subtype to create from the post for example). If you have Jersey unmarshall your request into the POJO for you, you can then do whatever validation you want and return a Response object with the validation error information to your client if the object doesn't pass.
So in other words, the validation is up to you. There are a few validation frameworks out there that you could try to help, such as javax.validation but IMHO it's usually easier to just test each property you need to validate yourself using conditionals, regexps, whatever.
In my opinion the validation comes with a webframework like struts, wicked, jfc... to name some. There the user inputs his data in a form to create the object he wants to post to a service. The webframeworks already got components to validate this data. When there was a positive validation you make the post call to your service.
I'm working on simple Spring-MVC application and I love new Spring REST features. I'd like to use the same method to process regular form and JSON data. It seems to be a little tricky, however. For example, method
public #ResponseBody String process(#RequestBody Bean bean);
will work for JSON request (Content-type: application/json), and
public #ResponseBody String process(Bean bean);
will match request with Content-type: application/x-www-form-urlencoded.
These methods are obviously will have almost the same content, so I'd prefer to avoid such duplication. With Jersey it's possible with #Consumes annotations, but I can't figure out how to do it with Spring.
First, the above declaration won't compile, because you are having duplicate signature.
Btw, #Consumes wouldn't help, I think, because it only designates what content type the method can handle.
In spring you can specify the content-type with
#RequestMapping(headers="Content-Type=application/json")
Just add #RestController annotation for the controller class.