I have been trying to get exception handling working in my simple Spring 3 based ReST web services. Based on everything I have seen, there is a bug that prevents this from working automatically with the #ResponseBody and #ExceptionHandler annotations
https://jira.springsource.org/browse/SPR-6902
So given that it isn't supported until Spring 3.1 or 3.0.6, what is the current best method for doing exception handling? I have seen numerous posts but haven't found a clear answer that has worked for me. An ideal solution would be one that automatically provides support for both xml and json
Do I have to manually define the entire marshalling setup? Won't this remove the need for the annotations that make using Spring 3 rest support worth it?
Seems in order to manually define marshalling (i.e. Jaxb2Marshaller) I need to add a new dependency on spring-ws which is a bit of a pain
Is it easier to just define a 'Response' object that all my methods return and wrap all functions in try/catch blocks?
You can redirect on error and then return something in #ResponseBody:
#ExceptionHandler(Exception.class)
public ModelAndView handleMyException(Exception exception) {
return new ModelAndView("redirect:errorMessage?error="+exception.getMessage());
}
#RequestMapping(value="/errorMessage", method=RequestMethod.GET)
#Responsebody
public String handleMyExceptionOnRedirect(#RequestParameter("error") String error) {
return error;
}
Little ugly, but this is just work around till the fix will be available.
This is a good workaround, but with one addition. The #ExceptionHandler(Exception.class)
should be #ExceptionHandler(MyException.class, YourException.class) as you can get into a loop using the general Exception class.
You can then test for (ex instanceof Myexception) to determine the message to display if need be.
Related
I'm using Project Reactor with Spring Integration to read from Kafka and write to MongoDB, and I the Kafka consume works well, but the .handle(MongoDb.reactiveOutboundChannelAdapter(mongoFactory)) stucks. I've seen that the internal code of this function is new ReactiveMongoDbStoringMessageHandler(mongoFactory)), so I've tried the following (I have a transform() method that converts from ConsumerRecord to Mono<String>, with the #Transformer annotation):
public IntegrationFlows writeToMongo() {
return IntegrationFlows.from(kafkaChannel)
.transform(this)
.handle(new ReactiveMongoDbStoringMessageHandler(mongoFactory))
.get();
}
The code follows the docs https://docs.spring.io/spring-integration/reference/html/mongodb.html#mongodb-reactive-channel-adapters.
The error I get is:
java.lang.IllegalArgumentException: Found ambiguous parameter type [class java.lang.Void] for method match: and then a very long list of functions. Any reason this could happen?
You cannot do new ReactiveMongoDbStoringMessageHandler(mongoFactory) if you are not going to subscribe to the returned Mono. A .handle(MongoDb.reactiveOutboundChannelAdapter(mongoFactory)) is the right way to do since it wraps that ReactiveMongoDbStoringMessageHandler into a ReactiveMessageHandlerAdapter for automatic subscription.
However I think your real problem is with the .transform(this). I believe you have a lot of methods in this class, so be more specific with method name. And this has nothing to do with Project Reactor. Not sure though why would one try to convert to Mono before sending to that ReactiveMongoDbStoringMessageHandler... You probably have problem in providing the payload (ConsumerRecord ?) which is not MongoDB mapped entity for saving into the collection.
Happened to me either. Solution was (notice the ReactiveMessageHandlerAdapter):
public IntegrationFlows writeToMongo() {
return IntegrationFlows.from(kafkaChannel)
.handle(new ReactiveMessageHandlerAdapter(new ReactiveMongoDbStoringMessageHandler(mongoFactory)))
.get();
}
This can be replaced by handleReactive() when this issue will be resolve.
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.
I can define a GET method in two ways:
public ResponseEntity<Pet> getPetById(Long id);
and
public Pet getPetById(Long id);
They seem to be equivalent, except that the first one involves more boilerplate code.
So, what is the reason to use ResponseEntity and what advantages it brings?
The difference is quite easy to explain. When you use ResponseEntity, you have full control about the contents of your response. You can change your headers, status code, ...
When you don't use ResponseEntity as the return type of a controller method, spring will "automagically" create a default ResponseEntity.
So the biggest advantage in using ResponseEntity is that you have full control. The disadvantage is that it is more verbose than letting Spring work its magic.
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
Posted in spring forum with no response.
I have the following code snippet (from here), which is part of my pet project.
#Controller
#RequestMapping("/browse")
public class MediaBrowser {
...
#RequestMapping("/**")
public final ModelAndView listContents(final HttpServletRequest request) {
String folder = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
...
}
I access the following url:
http://localhost:8080/myapp/browse
In spring 3.0.6.RELEASE, I got the folder variable as null, which is the expected value.
In spring 3.1.RC1, the folder variable is /browse.
Is this a bug or has something changed in spring-3.1?
As skaffman said, you probably shouldn't use PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE. Take a look at How to match a Spring #RequestMapping having a #pathVariable containing "/"? for an example of using AntPathMatcher to accomplish what you are trying
This looks very much like an internal implementation detail of the framework, one that you should not be relying on.
The javadoc for PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE says:
Note: This attribute is not required to be supported by all HandlerMapping implementations. URL-based HandlerMappings will typically support it, but handlers should not necessarily expect this request attribute to be present in all scenarios.
I wouldn't be surprised if the behaviour changed slightly between 3.0 and 3.1.