My service class has many methods, which call other service and this service has specified exceptions. I want to throw my exception when method catch this specified exception.
I.e.
try
{
// call other service
}
catch(ServiceXxxException serviceEx)
{
throw new MyException(...);
}
but I have many such methods and I don't want to grow code. Is it possible to create exception attribute like ExceptionFilterAttribute for ASP.NET MVC/Core ?
You can create general filter to handle any exception may occur and you can use filter attribute to handle, you can use it within the controller or action, something like this :
CustomExceptionFilter]
public class HomeController:Controller
{
//......
}
//Over the Action
[CustomExceptionFilter]
public ActionResult Index()
{
//.......
}
please follow this article :
https://www.c-sharpcorner.com/UploadFile/0ef46a/exception-filters-in-mvc/
Related
I'm trying to create custom exceptions for a Web-API program, if the name, code or whatever is null, it should send me a custom message saying x input is empty or null error, I'm not sure how to finish it so I'd appreciate some help.
public Product CreateProduct(Product p)
{
if (p.Name.Trim() == "")
{
throw new InvalidProductDataException("El producto no tiene nombre");
}
return p;
}
I'm assuming CreateProduct is an action method on a Controller class, and what you are actually trying to do is return errors to a client that is calling your API. Therefor, the easiest way to return errors to an HTTP client is by using the IActionResult return type instead of returning your actual type. This will give you control over the HTTP status code.
class ProductController : Controller {
public IActionResult CreateProduct(Product p)
{
if (String.IsNullOrWhitespace(p.Name))
{
return BadRequest("El producto no tiene nombre");
}
// Perform create?
return Ok(p);
}
}
For more information on controller action return types see: https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-5.0
Any kind of exception thrown from a controller action is going to result in an opaque HTTP 500 response sent to the API client, which probably isn't what you want for this kind of error.
An alternative is to introduce Middleware that will capture certain types of exception and convert them to HTTP responses.
Let's define a custom exception named LoginException that will be used to throw an error exception in login processes.
public class LoginException : System.Exception
{
//todo
}
Now let's make the necessary constructor definitions for use in the application.
public class LoginException : System.Exception
{
public LoginException()
: base()
{ }
public LoginException(String message)
: base(message)
{ }
public LoginException(String message, Exception innerException)
: base(message, innerException)
{ }
protected LoginException(SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
As seen above, we have defined the constructors of LoginException and made the constructor redirects for the System.Exception class, so we made the LoginException class ready for use.
void Login(string userName, string password)
{
try
{
if (userName != "canert" && password != "qwerty123")
throw new LoginException("Invalid login operation");
}
catch (LoginException loginException)
{
Response.Write(loginException.Message);
}
}
If you want to throw a custom type of exception called InvalidProductDataException then you need to declare a class with that name and it needs to be a subclass of System.Exception:
public class InvalidProductDataException : System.Exception {
public InvalidProductDataException(String message) : base(message) {
}
// This constructor overload is necessary to allow for your Exception to be deserialized. This is a best practice when implementing custom Exception types.
protected InvalidProductDataException(SerializationInfo info, StreamingContext context) :
base(info, context) {
// If you exception contained custom properties, you would want to deserialize them here
}
}
For more information on creating custom Exception classes see: https://learn.microsoft.com/en-us/dotnet/standard/exceptions/how-to-create-user-defined-exceptions
Note: It is not necessary to create a custom Exception class just to be able to specify the message that you want. For example you could simply throw new Exception("your message"). However it does make sense to create a custom Exception class if you want to be able to catch that specific exception elsewhere in your code.
So lets say we have a AccountController
And has a method like this for registering:
[HttpPost("register")]
public ActionResult Register(RegisterDto user)
{
try
{
accountService.Register(user);
return Ok();
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
}
For registering, your EmailAddress must not be used and Password must meed a criteria
The validation exists inside accountService and does something like this:
public void Register(RegisterDto user)
{
accountValidator.ValidateRegistration(user);
accountHandler.Register(user);
}
Notice the Register method is void, so it does not return anything, and the method ValidateRegistration looks like this
public void ValidateRegistration(RegisterDto user)
{
if (accountRepository.UserExists(user.Email))
{
throw new ExistingAccountAssociatedWithThisEmailAddressException();
}
if(!Regex.IsMatch(user.Password, passwordRegex))
{
throw new PasswordDoesNotMeetCriteriaException();
}
}
I am throwing exception with specific name so I can pass this back to the view, but I am wondering what is the best way to do something like this ? Creating specific Exceptions and throwing them ? I dont want to pass a string back
Using specific exceptions is a good practice, in my opinion, as they explain better a developer's intentions.
I can recommend two ways that may help refine your code:
Let all input-related exceptions inherit from a base exception named for example "BadInputException" (or "DomainException", from the perspective of clean architecture). This way you can catch the base exception then return BadRequest back to client.
Use asp.net core's middleware to handle such exception (here's an example), instead of try/catch block in every action. This way you would make your code cleaner.
How can I log all handled exceptions?
I want that whenever I catch an exception I should be able to log it
I want it to work globally and not that i should have to write it each time I catch
I tried subscribing to AppDomain.CurrentDomain.FirstChanceException and it did work but I did not have the full stack trace and it called multiple times for each exception (I don't know why)
I also tried wrapping my controller with ActionFilterAttribute like below and it worked on all exception from the controller only and not if the exception was caught in a service that was called from the controller
public class ExceptionLoggingHandler : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if(filterContext.Exception !=null)
{
System.Diagnostics.Debug.WriteLine(filterContext.Exception.Message);
}
base.OnResultExecuted(filterContext);
}
}
In ASP.NET MVC, you can add your filter as a global filter in the RegisterGlobalFilters method inside FilterConfig.cs. It should then catch all exceptions in all controller actions, and in any methods called from those actions - unless of course those methods already have catch blocks inside them which swallow the exception. In that case the caught exception (unless it's then re-thrown) will inevitably go undetected higher up the stack, which is, naturally, the whole point of catching exceptions.
e.g.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new ExceptionLoggingHandler());
}
}
Also, your attribute should inherit from HandleErrorAttribute, not ActionFilterAttribute.
Something like this:
public class ExceptionLoggingHandler : HandleErrorAttribute
{
public ExceptionLoggingHandler() : base()
{
}
public override void OnException(ExceptionContext context)
{
System.Diagnostics.Debug.WriteLine(context.Exception.Message);
context.ExceptionHandled = true;
//.... continue to produce a suitable response
}
}
(In the .... area you can continue to develop the handler to log more sophisticated data, and return a suitable response, perhaps along the lines of this one (other examples are also available online.)
I am using asp.net web api.
[Route("api/employee")]
[HttpPost]
public dynamic GetData(EmployeeModel model)
{
EmployeeService emp = new EmployeeService();
emp.GetData(model);
}
This is how I am handling error globally:
public class ExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
//Code to log the exception goes here:
}
}
in the WebApiConfig.cs file, I am registering the filter:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
....
config.Filters.Add(new ExceptionFilter());
}
}
Whenever there is an exception, the exception filter is able to catch the exception & It can log the exception. All this is working fine.
What I want: With every exception, I want to log specific details, those details are available in respective methods but how do I pass them to exception filter? The only work around I see is, add try catch block in individual methods and log the exception along with specific details.
Another thing that I want to capture is the request object of each request. In this case it will be ExployeeModel. Even if I somehow get the request object, how to I type cast into correct type. One endpoint can expect EmployeeModel other can expect DepartmentModel.
Maintenance Edit
After using this approach for a while I found myself only adding the exact same boilerplate code in every controller so I decided to do some reflection magic. In the meantime I ditched using MVC for my views - Razor is just so tedious and ugly - so I basically use my handlers as a JSON backend. The approach I currently use is to decorate my queries/commands with a Route attribute that is located in some common assembly like this:
[Route("items/add", RouteMethod.Post)]
public class AddItemCommand { public Guid Id { get; set; } }
[Route("items", RouteMethod.Get)]
public class GetItemsQuery : IQuery<GetItemsResponse> { }
// The response inherits from a base type that handles
// validation messages and the like
public class GetItemsResponse : ServiceResponse { }
I then implemented an MVC host that extracts the annotated commands/queries and generates the controllers and handlers for me at startup time. With this my application logic is finally free of MVC cruft. The query responses are also automatically populated with validation messages. My MVC applications now all look like this:
+ MvcApp
+- Global.asax
+- Global.asax.cs - Startup the host and done
+- Web.config
After realizing I really don't use MVC outside the host - and constantly having issues with the bazillion dependencies the framework has - I implemented another host based on NServiceKit. Nothing had to be changed in my application logic and the dependencies are down to System.Web, NServiceKit and NServiceKit.Text that takes good care of the model binding. I know it's a very similar approach to how NServiceKit/ServiceStack does their stuff but I'm now totally decoupled from the web framework in use so in case a better one comes along I just implement another host and that's it.
The situation
I'm currently working on an ASP.NET MVC site that's implementing the businesslogic-view separation via the IQueryHandler and ICommandHandler abstractions (using the almighty SimpleInjector for dependency injection).
The Problem
I've got to attach some custom validation logic to a QueryHandler via a decorator and that's working pretty well in and of itself. The problem is that in the event of validation errors I want to be able to show the same view that the action would have returned but with information on the validation error of course. Here is a sample for my case:
public class HomeController : Controller
{
private readonly IQueryHandler<SomeQuery, SomeTransport> queryHandler;
public ActionResult Index()
{
try
{
var dto = this.queryHandler.Handle(new SomeQuery { /* ... */ });
// Doing something awesome with the data ...
return this.View(new HomeViewModel());
}
catch (ValidationException exception)
{
this.ModelState.AddModelErrors(exception);
return this.View(new HomeViewModel());
}
}
}
In this scenario I have some business logic that's handled by the queryHandler that is decorated with a ValidationQueryHandlerDecorator that throws ValidationExceptions when it is appropriate.
What I want it to do
What I want is something along the lines of:
public class HomeController : Controller
{
private readonly IQueryHandler<SomeQuery, SomeTransport> queryHandler;
public ActionResult Index()
{
var dto = this.queryHandler.Handle(new SomeQuery { /* ... */ });
// Doing something awesome with the data ...
// There is a catch-all in place for unexpected exceptions but
// for ValidationExceptions I want to do essentially the same
// view instantiation but with the model errors attached
return this.View(new HomeViewModel());
}
}
I've been thinking about a special ValidationErrorHandlerAttribute but then I'm losing the context and I can't really return the proper view. The same goes with the approach where I just wrap the IQueryHandler<,> with a decorator... I've seen some strange pieces of code that did some string sniffing on the route and then instantiating a new controller and viewmodel via Activator.CreateInstance - that doesn't seem like a good idea.
So I'm wondering whether there is a nice way to do this ... maybe I just don't see the wood from the trees. Thanks!
I don't think there's a way to make the action method oblivious to this, since the action method is in control of the returned view model, and in case of a validation exception you need to return a view model with all the actual data (to prevent the user from losing his changes). What you might be able to do however to make this more convenient is add an extension method for executing queries in an action:
public ActionResult Index()
{
var result = this.queryHandler.ValidatedHandle(this.ModelState, new SomeQuery { });
if (result.IsValid) {
return this.View(new HomeViewModel(result.Data));
}
else
{
return this.View(new HomeViewModel());
}
}
The ValidatedHandle extension method could look like this:
public static ValidatedResult<TResult> ValidatedHandle<TQuery, TResult>(
this IQueryHandler<TQuery, TResult> handler,
TQuery query, ModelStateDictionary modelState)
{
try
{
return new ValidatedResult<TResult>.CreateValid(handler.Handle(query));
}
catch (ValidationException ex)
{
modelState.AddModelErrors(ex);
return ValidatedResult<TResult>.Invalid;
}
}
Do note that you should only catch such validation exception if the validation is on data that the user has entered. If you send a query with parameters that are set programmatically, a validation exception simply means a programming error and you should blog up, log the exception and show a friendly error page to the user.