This isn't so much a programming problem with Spring but a request for advice.
I have a form on a JSP requiring a user to enter their ID number.
The form on the page calls a method in a controller. A validator is called from the controller.
If the user enters a non-numeric value, I believe I can check for this using:
typeMismatch.java.lang.Integer=You have input a non-numeric value into a field expecting a number...
But my question relates to the checking of the user's ID in the database, i.e. if the ID doesn't exist, then input should be rejected.
So, can services be used in validator classes? Are there any considerations against this?
In a demo application you can do what looks simpler. But you also asked i there are considerations against calling service methods form validation bean.
IMHO the main problem is that you will enter two times in the service layer once for validation and once for actual treatment. And this is where the transactions are generally managed. So you could end up opening 2 different transactions in the same request instead of only one. If your application or database can be heavily loaded, this should be considered.
Of course, if you have a transaction during all along the request such with the Open Session In View pattern, this is no longer a concern.
It depends on whether you consider this validation rule as part of your business logic or not. I can see the following options:
The validation rule is considered to be part of your business logic. Then throw an exception on the service layer if the user's ID already exists. In this way you will handle everything in a single transaction.
The validation rule is not consider to be part of your business logic. The use a service method on your validation bean. This will provide you a common place for all validations.
Related
I have an endpoint handler method in controller:
#PutMapping("/users/{userId}")
public UserDto updateUser(#PathVariable UUID userId, #RequestBody UserRequestDto updateRequest) {
...
}
Inside the UserRequestDto object I have an email field.
I would like to validate if the email value is not already in use.
It is not a problem for the creation as I only need to check if the email is not in the database. I've created a simple ConstraintValidator and everything is working.
But for the update, I need to check if the updated email address is not used by someone else and omit the currently update user. So I need to operate on both userId and updateRequest at once. Is it doable to create custom ConstraintValidator to handle this situation?
Since you posted code from a #RestController, I assume we are talking about validation at the controller level. I can share a few observations:
1) Note that you are trying to put business logic related validation into a ConstraintValidator. In your case it is validating fields from a data transfer object (i.e. UserRequestDto) that holds request attributes. At the controller level we should only be validating the request itself (i.e. "was all necessary information provided so I can actually start performing the operation?").
2) Because the understanding of "valid" will most likely differ from business use case to business use case, business logic related validation should go into a #Service bean where use case specific validation can be performed (such as "is there any other user with the same username or email?"). Most of the time this is done by manually checking different conditions, such as performing database queries or consulting other applications. At this level ConstraintValidators are counterproductive.
3) ConstraintValidators are most of the time used to perform a syntactical validation (i.e. "are all necessary values present in the expected format?") but not so much for semantical validation (i.e. "is the information correct according to the my business logic (which might involve database queries and consulting other applications)?").
So in your case, validating your UserRequestDto object using a custom ConstraintValidator is absolutely fine if you need to check whether all required attributes have been passed by the client in a specific format. However, checking if a username/email is already taken by another user should be performed by the logic layer in a use case specific manner - outside of a ConstraintValidator.
You can annotate your controller (class-level) with Spring's #Validated and then #NotBlank #PathVariable String something will work as well.
Spring will create an AOP proxy around all your methods and will look for JSR 303 annotations on every parameter but it will not touch #RequestBody, so for the body, you still must add #Valid which will be inspected by the standard means.
Also, see this issue https://github.com/spring-projects/spring-framework/issues/26219
Two approaches I could think of:
Approach 1:
Put a unique constraint on email id in your database schema. If you
try to save/update an already used email id, a constrain violation
exception will be thrown.
Approach 2:
While researching on the above topic I came across the link. You
can refer it once and see if your need is sufficed.
Maybe you can use Class-Level Constrains:
Last but not least, a constraint can also be placed on the class level. In this case not a single property is subject of the validation but the complete object. Class-level constraints are useful if the validation depends on a correlation between several properties of an object.
You can access more details from https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/?v=6.1#validator-usingvalidator-classlevel
We got this design:
Rest Controller -> Calling Service -> Calling JPA DAOs
Let say we save employee. And we have a requirements to validate employee name.
IEmployeeService.saveEmployee(Employee emp)
EmployeeController.saveEmployee(Employee emp)
Option 1: I can inject JSR303 annotations on Employee and Let Rest Controller validate it as part of automatic validation.
Option 2: I validate in service method and raise exception and let controller to return proper JSON of that exception.
Seems like service should have validation anyways... but in presence of JSR303 annotations controller is doing everything, hence there seem to be duplication of logic if service does those checks as well.
How do you approach? Everybody's comments welcome and will be appreciated.
Thanks
Babar
IMHO, the standard way is to use both Opt 1 and Opt 2.
But, you have to define what's going to be validated in each layer.
This is my favorite approach:
Validate field constraints in Controller. This include: Date format, string length, min, max, null... validation
Validate business logic constraint in Service. Include: Unique, exist check, from date < to date, max items per group of entities...
One side note: Entity should never been exposed to outside world. We should have some kind of converter logic to convert entity to output JSON/model
Ideally your web facing part should have your validation logic implemented. The service layer should be purely for the business part of project. It is also security best practice before it reach to your service layer code. Nowadays people do mix-up layer and want to do everything in same layer. The jsr-303 annotation are bean level validation. So normally it is applied at a place where model comes into picture.
So you can do that at
controller layer: to validate your input and other min/max range and basic validation.
service layer: To have any specific validation at service layer by Autowiring javax.validation.Validator.
I would do both.
In your example you seem to have both methods refer to Employee, which might be possible in very simple scenarios, but in practical scenarios this is not.
It is more likely that you are going to have an EmployeeDTO which has JSON properties mapped to Java object fields. You might even have more specific DTOs to specific employee operations (for example change password), depending on what forms your UI is exposing. This will be received by your controller, and JSR303 can help do syntactic validations, like checking the string is not empty, that a name does not contain numbers etc.
The DTO should not bleed in to the service, but its data should be translated to whatever the service is expecting, which should be decoupled from the inputs of the controller.
Your Service will then receive an Employee. This could be a JPA Entity if it makes sense, or some other intermediate domain object related to the operation being performed. The creation of the Employee should ideally already enforce some simple checks (for example non null checks), to ensure that the object is consistent after construction. The service itself should then validate what it is receiving, independently of what the controller validates. The service could be used by other controllers, or invoked by other services in the future, so you should always program defensively. The service could also do more logical checks (for example does another employee with the same ID but different details exist? etc.) This will do business logic validation that will enforce further robustness.
If you see a lot of common code between the DTO and the Entity, or intermediate objects, there are also mapper utility libraries which can help you avoid repetition, like MapStruct.
I'm planning to create a custom validation model that depends on the Pre and Post Listeners of Hibernate.
Each Validation class should contain a business rule (not input checking) that needs to be checked before doing an insert, update or delete.
That's why it depends on Listeners like PreInsertEventListener, PreUpdateEventListener, PreInsertDeleteListener.
A validation class sometimes needs to check something in the DB. I've read in other posts that it is not advised to insert or update something from a Listener? But I'm planning to do only read-only calls.
To resume:
Validation class is related to one specific entity;
Validaton class is invoked by a Hibernate Listener;
Validation class might do some 'read-only' calls in DB;
Validation class is executed within same Hibernate Session and Transaction;
When a rule is not satisfied, some RuntimeException will be thrown causing a rollback of the current Transaction;
The advantage of this concept is that all business rules can be put in Validation classes which are specifically related to one entity. This avoids business rules to be spread over different Services.
What do you think of this approach? Are there any disadvantages/risks (specifically Hibernate related) wich I need to take care of?
I think the important thing here is predicting that your validation rules will only be specific to the entity being persisted. Its quite possible that you will run into situations that require data from parent/child entities to validate or to fetch related data.
The business rules spread over different services is a real world case, where usually to validate an entity you need to invoke different services. For example, if you want to persist a debit transaction for an account, you want to validate if the account holder exists, account is in a valid state & account has sufficient balance.
Suppose the architecture is such that there is
Struts framework or jersey resource
Facade layer
Business layer
Data access object layer DAO
Though the Input field validation is to be done at presentation layer i.e Struts Action class or jersey resource class
But my question is where should business level validation to be done and same outcome to be passed to UI.
I.e suppose resource is
employee/{employeeId} method DELETE
Now first need to verify that employeeId exist or not , so for that validation should be done at resource level, facade level or business level and how it should be any best practice is highly appreciated.
Also please note that this business validation require DAO layer access since to check in DB if employeeId really exist.
Thanks in advance
There are as many arguments as approaches for what you're asking. I prefer to leave validation to the business layer and let the service layers above that more or less just handle routing and error reporting. Good luck!
Depends on the architectures and frameworks you have choosen.
In example: If you have one Database but a server-farm, the Validation should be more near the Database. If you can lock/unlock the Database in the DAO, you shall lock the employee-row first before you validate.
Also it depends on the configuration:
If you use optimistic or pessimistic locking.
If you have a #version field on the entitys.
many many more.
I suggest you to write logic related database access at dao layer, which returns the result to service layer and it returns to the Action class. And you should validate it in your action class.
For quite some time I try to figure out where validation of user input should take place in a Spring MVC application. In many online blogs and tutorials I basically read that a controller should validate the users input and, if invalid, respond to the user by showing a page containing the error message. My current understanding of the Spring and Spring MVC layering system, however, is that a Controller is a only shallow interface between the application logic (service layer) and the "web world", allowing usage of the service layer from the web. Also, as far as I can see, Spring MVC does only provide reasonable tools for validation in a Controller.
If now validation takes place in a Controller, if at some later point I want to untie the application logic from the "web world", validation logic must be reimplemented in the new environment (e.g. a desktop application using Swing). In my opinion, the ability to decide which operations are "valid" on domain objects, and what "valid" states such objects may have, is core part of the service layer, and not the concern of some other part of the application (e.g. Controllers).
In this context, why is it "good practice" to place input validation logic in the controller layer and not the service layer?
A common approach is to do validation on both places. But if you are talking about #Valid, from my experience it is nicer to put on Controllers level.
It also depends what kind of validation logic we are talking about. Let's say you have a bean:
#Data
public class MyBean {
#NotNull private UUID someId;
#NotEmpty private String someName;
}
It would make sense for this bean to be annotated with #Valid on the controller level so it doesn't even reach the service. There is no benefit to putting the #Valid on the service method, because why would you propagate it further while you can immediately in the controller decide if it is that kind of valid or not.
Then there is a second type of validation: business logic validation. Let's say for the same bean that the someId property is a timeUUid and its timestamp needs to be at most 2 days after some event occurred, in other case, the bean should be discarded by the service.
That seems like a business logic validation case, because by just looking at the bean, you wouldn't be able to validate it, unless you apply some logic to it.
Since both approaches to validation actually validate different things, it is obvious to see that each of your MVC components - Model, View and Controller, do their own validation and it should be reasonable about what it validates without introducing dependency to the other components.
As for showing the error to the user, yes, the Errors object is indeed intended to be used for bean validation at controller level, but you can design some filter that catches exceptions on any level and then pretty formats it for the user. There are many approaches to it, and I am not sure if Spring prescribes that any is better than the other.
Depending on different resolve mechanism (as in, for example, jstl or jackson or something else), you would probably be inclined to deal with validation in a different way. For example, a traditional jstl view resolver would nicely work with a contraption that uses Errors, while a jackson resolver would probably work nicer with a combination of #ResponseBody and some filter that catches errors and puts them in a predefined error part of the response object.
In one of our previous projects, we had huge forms with very complex logic which meant a lot of validating code. So we used a third kind of solution. For every controller, we autowired a helper class.
Example:
myview <-> MyController <- MyService <- MyDAO
^
|
MyHelper
Controllers handled the view resolving.
Services handled mapping from dto-s to model objects for view and vice versa,
DAO-s handled database transactions and,
Helpers handled everything else including validation.
If now someone would have wanted to change the frontend from web to something else, it would have been a lot easier and at the same time, we didn't over-bloat the service implementation classes.