Spring - Best approach to provide specific error messages in a validator from a DAO? - spring

What is the best way to implement a validator in Spring that accesses a DAO object but needs to return different error messages based on the DAO error? Should the DAO method throw different exceptions that the validator turns into proper error messages? Should the DAO return an enumeration so the validator can handle each return type separately if necessary? I suppose the validator can pass the org.springframework.validation.Errors object to the DAO, but that seems to tie the two classes too closely together.
I believe the best approach is the enumeration approach to avoid the overhead of exceptions. Is there another way I should be considering?
Update
Actually, the enumeration would probably have to be a reference passed into the DAO as that method had to return the actual object. Is this still the best approach?
Update 2
The issue is I need to retrieve information from the database in this particular case, not store it. In the validation class, I was checking if the value already exists (which is why it needed the DAO), and if it already exists that is an error that I would show to the user on the page. I do need to validate that all fields on the form were filled in, so maybe that's the only thing I use the validator for. Then, how do I handle the error where the value already exists in the database? The DAO is the only thing that will know that - what is the best way to communicate that error from the DAO to the web layer?
The DAO method is currently returning the user object it is retrieving from the database - I can return null if there is an error, but that doesn't give me any granularity into the error details - it's essentially a boolean at that point indicating if the record was found or not, but not why.

Validator accessing DAO is valid.
I Would suggest throwing exception over passing enumeration.
If given one more option I would suggest not throwing exception but returning null.
You may design your DAO methods in such a way that they would return the populated object if available, if not just returns null.
Take following example:
DAO layer :
class UserInfoProvider {
public void createUser(User user) throws UserCreationException {
// throws UserCreationException when something goes wrong while updating database
}
public User findUser(String username) {
// return user object if found
// else just return null
}
}
Validation :
class UserValidator {
public void validate(command, errors) {
String username = command.getUsername();
UserInfoProvider userInfoProvider;
User user = userInfoProvider.findUser(username);
if (user == null) {
errors.rejectValue("username","User not found");
return;
}
}
}
You might consider using Spring security when you are using Spring MVC.

I suppose the validator can pass the org.springframework.validation.Errors object to the DAO, but that seems to tie the two classes too closely together.
I'm not understanding this. Why would you pass Errors to the DAO?
The point of validation is to prevent bad data from ever getting within sniffing distance of your database. You should be sending a response back to the source of the request informing them about any Errors you've encountered.
The only reason I can think of for passing such a thing to a database would be to track requests and responses as an auditing/tracking function.
If there are errors, the use case is done. The user needs to be informed, not your database.
UPDATE:
If you're checking something like a username that has to be unique in a database, by all means do that check. But I'd do it as an AJAX call, as soon as it was entered, and if it already existed I'd simply tell the user so.
DAO should not know about web tiers. Bad design - too coupled.
Let the web tier inquire through an intermediary. The intermediary will make the query and send back the appropriate result. Your web page should have a controller and/or service of some kind that can be that intermediary.

Checking the database in your validator is a valid thing to do.
I am a bit confused about what sort of errors you are expecting your DAO to return though. Typically it will either return the requested object or NULL, and it is up to the caller (your validator) to know the reason why.
If you need to check that multiple fields don't already exist in the database, then just make multiple DAO calls.

Related

Putting Spring WebFlux Publisher inside Model, good or bad practice?

I'm working on a code audit on a SpringBoot Application with Spring WebFlux and the team is putting Publisher directly inside the Model and then resolve the view.
I'm wondering if it is a good or bad practice because it seems to be working but in that case, which component is in charge of executing the Publisher ?
I think that it's the ViewResolver and it should not be its job. What do you think ?
Moreover, if the Publisher is not executed by the Controller, the classes annotated by #ControllerAdvice such like ExceptionHandler won't work if these Publisher return an error, right ?
Extract of the Spring WebFlux documentation :
Spring WebFlux, unlike Spring MVC, explicitly supports reactive types in the model (for example, Mono or io.reactivex.Single). Such asynchronous model attributes can be transparently resolved (and the model updated) to their actual values at the time of #RequestMapping invocation, provided a #ModelAttribute argument is declared without a wrapper, as the following example shows:
#ModelAttribute
public void addAccount(#RequestParam String number) {
Mono<Account> accountMono = accountRepository.findAccount(number);
model.addAttribute("account", accountMono);
}
#PostMapping("/accounts")
public String handle(#ModelAttribute Account account, BindingResult errors) {
// ...
}
In addition, any model attributes that have a reactive type wrapper are resolved to their actual values (and the model updated) just prior to view rendering.
https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-ann-modelattrib-methods
Doesn't come as a shock to me.
Actually seems to be a good trade off between complexity and efficiency when the Publisher is handling complex stuff.
It has the advantage of executing the Publisher only if and when needed.
Although it might be a problem if the ModelMap handler does not have the capacity to use it properly.
As for the exceptional cases, maybe you do not want it to be executed and just printed, thus failing faster.
As for the question about what is executing the Publisher, a specific ViewResolver can be used as it is the component responsible for the "rendering". IMHO that's it's job. I do not know if a standard ViewResolver can be used for detecting values vs publishers and handle those automagically, yet this seems completely doable and efficient.

Access data computed in filter for each request inside Spring Controller/Service Class

In our application, all rest apis are of the form:
http://{context}/{product_id}/{rest_url_path}
I have to verify the {product_id} inside a Spring Security Filter/SpringMVC interceptor, by fetching the ProductDetails from DB for the product_id. The ProductDetails will be used inside Spring Controllers/Service classes.
I don't want to fetch the ProductDetails again inside Controllers/Service. So I want to store the ProductDetails object somewhere for that RequestScope.
I have 3 approaches in mind. But each have their pros and cons. Please let me know which one better out of the 3. Also suggest any alternative approach.
Approach-1:
Save the ProductDetails object inside request attribute.
Inside Controller, i can easily get the HttpRequest. Inside Service, I can get HttpRequest by:
#Autowired
HttpServletRequest request;
or
RequestAttributes attribs = RequestContextHolder.getRequestAttributes();
HttpServletRequest request = null;
if (attribs instanceof ServletRequestAttributes) {
request = ((ServletRequestAttributes) attribs).getRequest();
}
But I don't want to have HTTP request dependency inside Service to make to code more independent from WebLayer logic.
Approach-2:
Use any in memory cache based on product_id to stored ProductDetails
But this I think a over kill only for this use case. Increasing unnecessary dependencies of a cache.
Approach-3:
Store the Object in a ThreadLocal variable to store request scope data.
But not sure if it correct this way.
Let me know an efficient approach to solve this problem
1st and 3rd are suitable for your problem statment but first one is more elegant as data will stored only for current request scope and will get automatically lost when server send response. You can also use threadLocal but you have to be cautious ,if you forget to remove object it will hang around in an environment that uses a thread pool.
The first approach you mentioned is more efficient way to access the same data in both filter and controller even though you have to inject the dependency of HttpservletRequest in SpringController.
If the data is very user specific like other user will not have access to those data in that case you should use ThreadLocal.

ArgumentResolvers within single transaction?

I am wondering if there is a way to wrap all argument resolvers like for #PathVariables or #ModelAttributes into one single transaction? We are already using the OEMIV filter but spring/hibernate is spawning too many transactions (one per select if they are not wrapped within a service class which is be the case in pathvariable resolvers for example).
While the system is still pretty fast I think this is not necessary and neither consistent with the rest of the architecture.
Let me explain:
Let's assume that I have a request mapping including two entities and the conversion is based on a StringToEntityConverter
The actual URL would be like this if we support GET: http://localhost/app/link/User_231/Item_324
#RequestMapping("/link/{user}/{item}", method="POST")
public String linkUserAndItem(#PathVariable("user") User user, #PathVariable("item") Item item) {
userService.addItem(user, item);
return "linked";
}
#Converter
// simplified
public Object convert(String classAndId) {
return entityManager.find(getClass(classAndId), getId(classAndId));
}
The UserService.addItem() method is transactional so there is no issue here.
BUT:
The entity converter is resolving the User and the Item against the database before the call to the Controller, thus creating two selects, each running in it's own transaction. Then we have #ModelAttribute methods which might also issue some selects again and each will spawn a transaction.
And this is what I would like to change. I would like to create ONE readonly Transaction
I was not able to find any way to intercept/listen/etc... by the means of Spring.
First I wanted to override the RequestMappingHandlerAdapter but the resolver calls are well "hidden" inside the invokeHandleMethod method...
The ModelFactory is not a spring bean, so i cannot write an interceptor either.
So currently I only see a way by completely replacing the RequestMappingHandlerAdapter, but I would really like to avoid that.
And ideas?
This seems like a design failure to me. OEMIV is usually a sign that you're doing it wrong™.
Instead, do:
#RequestMapping("/link/User_{userId}/Item_{itemId}", method="POST")
public String linkUserAndItem(#PathVariable("userId") Long userId,
#PathVariable("itemId") Long itemId) {
userService.addItem(userId, itemId);
return "linked";
}
Where your service layer takes care of fetching and manipulating the entities. This logic doesn't belong in the controller.

Check preconditions in Controller or Service layer

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

Best practice for using #SessionAttributes

I am trying to share data between two controllers in a Spring mvc application.
In Controller A I have annotated the class with #SessionAttributes({"mymodel1"}) and in the method which receives the first GET request I add it to the ModelMap:
model.addAttribute("mymodel1", MyModel1);
I now want to read myModel1 from Controller B.
In this Controller I have the following method which intercepts the POST requests and already has a different model in its parameters:
public String processSubmit(#ModelAttribute("mymodel2") MyModel2 mymodel2, BindingResult result, SessionStatus status, HttpServletRequest httpRequest)
Up to this point everything works fine and I am able to read mymodel2 from processSubmit however if I now want to add another #ModelAttribute("mymodel1") MyModel1 mymodel1 to this method signature I would have expected to be able to read the value I was setting in Controller A however I'm getting exceptions that the first model is no longer recognised.
So my question is: how can I read mymodel2 from Controller B?
You can't do that with #SessionAttributes :
Session attributes as indicated using this annotation correspond to a specific handlers model attributes, getting transparently stored in a conversational session. Those attributes will be removed once the handler indicates completion of its conversational session. Therefore, use this facility for such conversational attributes which are supposed to be stored in the session temporarily during the course of a specific handlers conversation.
For example I use this annotation when I want to validate elements with Hibernate validation, and after I submit the page and SOME elements are invalid I want the rest to be still on the page, but this is not your case. I think that the only way to do it would be with:
HttpSession.getAttribute()
The javadoc excerpt above is the most typical way #SessionAttributes is used. However, what Joly is describing should also work. Session attributes are stored via DefaultSessionAttributeStore, which by default does not prefix attribute names when it stores them in the session. That means if ControllerA and ControllerB both list an attribute called "mymodel1", they're actually referring to the same session attribute. You'll need to provide a little more information on the error you're getting and the actual controller code.

Resources