Spring5 has introduced ResponseStatusException, which has put me in a dilemma as to in what scenario I can use a ResponseStatusException and ControllerAdvice as both of them are quiet similar.
Can anyone help me with this.
Thanks in advance.
Lets first understand what is ResponseStatusException and ControllerAdvice
ResponseStatusException is a programmatic alternative to #ResponseStatus and is the base class for exceptions used for applying a status code to an HTTP response.
#GetMapping("/actor/{id}")
public String getActorName(#PathVariable("id") int id) {
try {
return actorService.getActor(id);
} catch (ActorNotFoundException ex) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "Actor Not Found", ex);
}
}
The #ControllerAdvice annotation allows us to consolidate multiple, scattered #ExceptionHandlers into a single, global error handling component.
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value
= { IllegalArgumentException.class, IllegalStateException.class })
protected ResponseEntity<Object> handleConflict(
RuntimeException ex, WebRequest request) {
return ResponseEntity<Object>;
}
}
Coming back to your questions of when to use what:
If you want to provide a unified and global way of exception handling make use of
ControllerAdvice. It also eliminates code duplication which might be caused by ResponseStatusException.
In order to throw different error code and responses for the same exception, don't want to create custom exception classes and to avoid tight coupling make use of ResponseStatusException.
References:
Spring ResponseStatusException
Error Handling for REST with Spring
Overall it is better to use #ControllerAdvice if you are looking for a more unified solution but ResponseStatusException is also handy too in case you don't want to make different Exception classes and want to keep it simple.
for examples and more info you can refer to the following articles:
Spring Boot Exception Handling — #ControllerAdvice
Spring Boot Exception Handling — ResponseStatusException
Related
in this post I found it that how to send my Serilog enrichment properties to consumers. when I logging my informations, then every thing is correct. but when throws an exception in the consumers, I don't see my Serilog properties in the log file. as you can see:
ERR - - -
2022-01-03 12:25:40.346 - myApp - MassTransit.ReceiveTransport - ERR - - - => R-FAULT "rabbitmq://localhost/MyConsumer" "c8100000-568d-0050-407a-08d9ce96c99c"
well, I guess the exception logging occurred in another context. I guess the exception logging occurred in the ExceptionConsumeContext class.
well I Implemented a filter for ExceptionConsumeContext:
public class IntegrationEventExceptionConsumeFilter<T> : IFilter<ExceptionConsumeContext <T>> where T : class {
public IntegrationEventPublishFilter()
{
}
public Task Send(ExceptionConsumeContext <T> context, IPipe<ExceptionConsumeContext <T>> next)
{
return next.Send(context);
}
public void Probe(ProbeContext context)
{
}}
now I need to add this filter to MassTransit configuration:
cfg.UseConsumeFilter(typeof(IntegrationEventExceptionConsumeFilter<>), context);
well, I got the exception:
The scoped filter must implement GreenPipes.IFilter<MassTransit.ConsumeContext<MyEvent>> (Parameter 'scopedType')
well, I couldn't find any conumer filter registeration for type of ExceptionConsumeContext.
and Since the I saw ExceptionConsumeContext inherited from ConsumeContext, I guess can be register this filter as a ConsumeFilter!. but don't work this way.
public interface ExceptionConsumeContext : ConsumeContext, PipeContext, MessageContext, IPublishEndpoint, IPublishObserverConnector, ISendEndpointProvider, ISendObserverConnector{}
now, I don't know what I do!
There is no scoped filter registration for ExceptionConsumeContext. You would need to add your additional filter to the receive pipeline.
cfg.ConfigureError(x =>
{
x.UseFilter(new GenerateFaultFilter());
x.UseFilter(new IntegrationEventExceptionConsumeFilter());
x.UseFilter(new ErrorTransportFilter());
});
Note that there is no message-specific generic version of ExceptionConsumeContext.
For my REST api I'm using jersey and ExceptionMapper to catch global exceptions.
It works well all the exception my app throws but I'm unable to catch exception thrown by jackson.
For example one of my endpoint accept an object that contains an enum. If the Json in the request has a value that is not in the enum jersey throw this exception back
Can not construct instance of my.package.MyEnum from String value 'HELLO': value not one of declared Enum instance names: [TEST, TEST2]
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#5922e236; line: 3, column: 1] (through reference chain: java.util.HashSet[0]->....)
Even though I have created this mapper
#Provider
#Component
public class JacksonExceptionMapper implements ExceptionMapper<JsonMappingException> {
#Override
public Response toResponse(JsonMappingException e) {
....
}
}
The code never reach this mapper.
Is there anything we need to do in order to catch these exceptions?
EDIT
Note: I have jus tried being less general and instead of JsonMappingException I use InvalidFormatException in this case the mapper is called. But I still don't understand because InvalidFormatException extends JsonMappingException and should be called as well
Had the same problem.
The problem is that JsonMappingExceptionMapper kicks in before your mapper. The actual exception is of class com.fasterxml.jackson.databind.exc.InvalidFormatException and the mapper defines com.fasterxml.jackson.jaxrs.base.JsonMappingException, so it's more specific to the exception.
You see, Jersey's exception handler looks to find the most accurate handler (see org.glassfish.jersey.internal.ExceptionMapperFactory#find(java.lang.Class, T)).
To override this behavior, simply disable the mapper from being used:
Using XML:
<init-param>
<param-name>jersey.config.server.disableAutoDiscovery</param-name>
<param-value>true</param-value>
</init-param>
Using code: resourceConfig.property(CommonProperties.FEATURE_AUTO_DISCOVERY_DISABLE, true); where resourceConfig is of type org.glassfish.jersey.server.ServerConfig.
You can also write your own specific mapper:
public class MyJsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException>
But I think it's an over kill.
Hi it seems to exits an alternative answer now that does not require to disable Jersey AUTO_DISCOVERY feature.
Just annotate your own exception mapper with a #Priority(1) annotation. The lower the number, the higher the priority. Since Jackson's own mappers do not have any priority annotation, yours will be executed:
#Priority(1)
public class MyJsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException>
Starting in version 2.29.1 [1], if you're registering the JacksonFeature, you can now do so without registering the exception mappers [2]:
register(JacksonFeature.withoutExceptionMappers());
[1] https://github.com/eclipse-ee4j/jersey/pull/4225
[2] https://eclipse-ee4j.github.io/jersey.github.io/apidocs/2.34/jersey/org/glassfish/jersey/jackson/JacksonFeature.html#withoutExceptionMappers--
Per title, exceptions thrown from a ParamConverter are NOT handled the way I expect.
With an ExceptionMapper:
#Provider
public class MyExceptionMapper implements ExceptionMapper<MyException> {
#Override
public Response toResponse(MyException exception) {
return Response.serverError().entity( "It triggered" ).build();
}
}
and ParamConverter:
#Provider
(boilerplate junk)
#Override
public DateTime fromString(String value) {
throw new MyException("convert");
}
It does NOT return the "It triggered" text in a 500 error, but rather a 404.
Anticipated question : Are both providers registered?
Yes - If I throw "MyException" from a resource (within 'regular' code) it works as expected. I can also convert see the stacktrace with the "convert" message.
Is there any way to make exceptions from ParamConverters be handled by the ExceptionMapper?
I am using jersey 2.3.1, along with spring-jersey, launched in a jetty container 9.1.0.RC0
Seem from reading this, the JAX-RS spec says the implementor should wrap unhandled exceptions in a NotFoundException (404) for #QueryParam and #PathParam, and from what I tested a 400, (I'm guessing BadRequestException) for #FormParam.
"if the field or property is annotated with #MatrixParam, #QueryParam or #PathParam then an implementation MUST generate an instance of
NotFoundException (404 status) that wraps the thrown exception and no entity"
A couple ways I can see handling the exception, is to
Just handle it in the ParamConverter, e.g.
return new ParamConverter<T>() {
#Override
public T fromString(String string) {
try {
return (T)new MyObject().setValue(string);
} catch (MyException ex) {
Response response = Response.serverError().entity("Boo").build()
throw new WebApplicationException(response);
}
}
#Override
public String toString(T t) {
return t.toString();
}
};
Or just have your exception extend WebApplicationException, and return the Response there. e.g.
public class MyException extends WebApplicationException {
public MyException(String message) {
super(Response.serverError().entity(message).build());
}
}
I experienced the same behavior in Jersey 2.26.
Any Exception that extends RuntimeException gets mapped to a ParamException, which is itself a sublcass of WebApplicationException.
Assuming your MyException extends RuntimeException, it's not getting caught because your ExceptionMapper only handles MyException.
Regarding the Jersey docs saying to throw a NotFoundException: I would argue a 404 does not apply when a queryParam can't be converted. A BadRequestException seems more appropriate. And also, I can't see anything unique in the Jersey frame work when a NotFoundException is thrown besides setting the response code
To get exceptions thrown from a ParamConverter end up in an ExceptionMapper, you'll have to have your ExceptionMapper catching a more global exception, like Throwable.
Another answer suggests returning a WebApplicationException. This should be a fine solution but will NOT work if the Response object has an entity. See here: https://github.com/jersey/jersey/issues/3716
Let's assume we have the following method in the business layer. What's the best practice to tell the UI layer that something went wrong and give also the error message? Should the method return an empty String when it was OK, otherwise the error message, or should it throw another exception in the catch code wrapping the caught exception? If we choose the second variant then the UI should have another try,catch which is too much try,catch maybe. Here is a pseudocode for the first variant.
public String updateSomething()
{
try
{
//Begin transaction here
dataLayer.do1();
dataLayer.do2();
dataLayer.doN();
//Commit transaction code here
}
catch(Exception exc)
{
//Rollback transaction code here
return exc.message;
}
return "";
}
Is this a good practice or should I throw another exception in the catch(then the method will be void)?
I like to return a standard contract to my UI layer from my business layer.
It looks like this:
public class ServiceOperationResult<T>
{
public bool Successful
{
get;
set;
}
public ServiceErrorType ErrorType
{
get;
set;
}
public string ErrorMessage
{
get;
set;
}
public T ReturnData
{
get;
set;
}
}
I use generics so that every service can define what it sends back, and the standard error flags tell the client app what type of error occurred (these are a meta-type, like "Internal error", "External party error", "Business rule validation error") and the app can then react in a standard fashion to these error types.
For instance, business errors are displayed in a red error label, while internal errors get redirected to an error page (in a web app) or close the form (in a windows app)
My pet hate is seeing a red label on a web site (where I expect to see validation errors) and seeing something like "The database server refused your connection" This is the risk that you run by only using a string to return error data.
The best way is wrap exception in some more general type and rethrow it. So updateSomething() must declare that it can throw some sort of Exception (for example: UpdateFailedException) and in catch block you should wrap exception.
public String updateSomething() {
try {
[...]
} catch ( SQLException e ) {
// rollback;
throw new UpdateFailedException(e);
}
}
But catching abstract Exception type is not a good idea. You should wrap only those things which semantic you know. For example: SQLException, DataAccessException (Spring DAO) etc.
If you wrap Exception you easily could wrap InterruptedException of NullPointerException. And this can broke your application.
It's a little unusual to return a String like this (but there's no real reason not too). More usual methods would be:
return a boolean value, and have some method of setting the error message, either by logging it, setting some global "last error" value, or having a pointer to an error construct passed in to your method which you update;
have a void method which throws an exception on failure, and handle it in the calling code (as you suggest)
I have see both of the above used extensively. It's hard to say which is "best". Try to be consistent with the idioms and conventions of the language you are working in and/or the existing code set/libraries you are working with if any.
Probably the best way is to have a custom exception classes specific to layers, once you catch the exception in a particular layer throw the custom exception to the calling layer, having this will have you the following advantage.
you will get the better modular approach to deal with the exception.
the maintenance of the code will be easy when your code complexity increases
you will be having more control on the exception scenarios
for example you catch a exception in the business layer and want to inform Presentation layer
public string DummyFunction
{
try
{
}
catch(Exception ex)
{
throw new businessException();
}
}
I'd like my Grails web-app to send an e-mail for each exception that reaches the end-user.
Basically I'm looking for a elegant way to achieve something equivalent to:
try {
// ... all logic/db-access/etc required to render the page is executed here ...
}
catch (Exception e) {
sendmail("exception#example.com", "An exception was thrown while processing a http-request", e.toString);
}
Turns out this exact question was answered on the Grails mailing list a couple of days ago.
The solution is to add the following to the log4j-section of Config.groovy:
log4j {
...
appender.mail='org.apache.log4j.net.SMTPAppender'
appender.'mail.To'='email#example.com'
appender.'mail.From'='email#example.com'
appender.'mail.SMTPHost'='localhost'
appender.'mail.BufferSize'=4096
appender.'mail.Subject'='App Error'
appender.'mail.layout'='org.apache.log4j.PatternLayout'
appender.'mail.layout.ConversionPattern'='[%r] %c{2} %m%n'
rootLogger="error,stdout,mail"
...
// rootLogger="error,stdout" (old rootLogger)
}
Plus adding sun-javamail.jar and activation.jar to the lib/-folder.
Assuming you can do this from groovy, you'll want to use a logging framework such as log4j for this, which has loggers that can append log data to a database, send email, etc.
You could also take a look at exceptionHandler mechanism provided by Grails; I find it very simple; yet powerful enough to take care of all my custom & clean exception handling needs. Haven't tested this approach with 1.1 so far; but works very well with 1.0.3.
class BootStrap {
def exceptionHandler
def init = { servletContext ->
exceptionHandler.exceptionMappings =
[ 'NoSuchFlowExecutionException' :'/myControler/myAction',
'java.lang.Exception' : '/myController/generalAction']
}
def destroy = { }
}
Detailed blog here :
http://blog.bruary.net/2008/03/grails-custom-exception-handling.html