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.
Related
Need some help making my controllers DRY using the ASP.NET core
So, basically, I have an issue keeping my controllers non-fat and DRY.
Basically, let's say I have 4 controllers : controllerA, controllerB ... controllerD - the general structure of the code for each controller looks like this (pseudocode)
class controllerA
{
IActionResult Get(string ids)
{
//do validation on ids - if invalid return 404
// IEnumerable<obj> results= aservice.GetResource(ids)
-------------------------------------------------------------------------
//Logging of results
//logic which looks at results and determines whether to return a 404 or a 200
//return results
}
}
The logic after the dotted line is essentially repeated in 4 controllers - how can I make the controllers more DRY.
I have read about different solutions including using a DI service, a helper class, extension methods for the Controller type or an abstract class that these controllers can inherit from that extends the .NET controller class.
I am writing production code so would like to seek out a clean solution which is also best practice!
Thanks!
I would recommend to use MediatR nuget.
You can implement IPipelineBehavior<,> for each required step, in your example it will be:
Validation of ids
Logging
Your code will look like:
public async Task<IActionResult> Get(CustomCommand request)
{
var result = await _mediator.Send(request);
// return handled result
}
If you define command as parameter of action it will reduce code more, something like this
public async Task<IActionResult> Get(string ids)
{
// under the hood it will execute (depends on implementation):
// 1. validation of ids for the command
// 2. execute main logic which implemented in IRequestHandler<>
// 3. logging
// 4. return result
var result = await _mediator.Send(new CustomCommand(ids));
// return handled result
}
About results handling, there are two common ways functional programming or using exceptions.
Using exceptions
Create customer exceptions for your service, for example, NotFoundException, BadRequestException (better to call it something meaningful like ValidationException)
Throw them in cases where you need to stop execution and return unsuccessful result (for example, validation was failed)
Add global exception filter to asp.net which will handle this exceptions depends on it's type and it might use some custom data from it
Functional programming
Instead of simply returning response you will return Result object within the response, simple implemented of Result<T>:
public class Result<TValue>
{
public TValue Value { get; set; }
public bool IsSuccess { get; set; }
// you can add also something like 'ErrorCode' to specify how to handle
// failed result, for instance if ErrorCode is 400 (it can be readable
// string as well) you will return BadRequest()
}
Finally you can define in base controller class method to execute the logic and handle results
//... inside your base controller
public async Task<ActionResult<TResponse>> ExecuteAsync<TResponse>(IRequest<Result<TResponse>> request)
{
Result<TResponse> result = await Mediator.Send(request);
if(result.IsSuccess) return Ok(result.Value);
return result.ErrorCode switch
{
"some_error_code" => Conflict(result.ErrorMessage),
"other_code" => NotFound(),
_ => BadRequest(result.ErrorMessage)
};
}
//...
so the action code will be:
public Task<IActionResult> Get(CustomCommand request) => ExecuteAsync(request);
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/
I'm looking for some guidance on how to best handle exceptions in asp net core. Based on these docs from microsoft I've setup the UseStatusCodePagesWithRedirects middleware. This works for things such as 404's. However for API requests within my code that return exceptions this doesn't work. So following this doc I've setup an exception filter. This kinda works and this is where I'm looking for help.
I have the following code in a class library so it can be re used elsewhere. A mini wrapper for my api. I'm using Flurl to construct the request. My viewModelBuilder calls GetAll.
public async Task<List<TableDto>> GetAll(int branchId)
{
var result = await _baseUrl.AppendPathSegment("/v1/Table/GetAll").WithOAuthBearerToken(await _authenticationManager.GetToken()).GetJsonAsync<List<TableDto>>();
return result;
}
If GetAll throws an exception then the following exception filter comes into play.
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
// ex.Call.Response.StatusCode // This works with the original exception but not here
if (context.HttpContext.Response.StatusCode == (int)HttpStatusCode.Forbidden)
{
// Always 200
}
}
}
I would like to do different things based on the exception status code. So for a 401 or 404 I would like to show an access denied screen or a login screen and then for other errors maybe just a general error screen. However context.HttpContext.Response.StatusCode is always 200.
Is this the right way to go about doing this? The error handing documentation suggests that middleware is better but I'm not sure if its referring to this use case as I could only get it working for 404s.
How do I get the correct status code in a exception filter?
I know that if I put a try catch around the original GetAll as below
try
{
var result = await _baseUrl.AppendPathSegment("/v1/Table/GetAll").WithOAuthBearerToken(await _authenticationManager.GetToken()).GetJsonAsync<List<TableDto>>();
return result;
}
catch (FlurlHttpException ex)
{
if (ex.Call.Response != null)
{
if (ex.Call.Response.StatusCode == HttpStatusCode.Forbidden)
{
throw new ForbiddenException();
}
}
}
then in the exception filter I can do the following
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
if (context.Exception is ForbiddenException)
{
}
}
}
and then I can do specific things for specific exceptions however thing means I have to try catch every call and voids the point of the global exception filter. Any advice would be appreciated.
Thanks
M
EDIT
Based on this answer here I could write a custom middleware and I quickly did that but I'm still stuck with the same problem of not being able to check for a 401 or 403 and only being able to get access to custom exceptions that have been throw. If I can access the status code then I think both solutions will work
I was already involved in couple of MVC projects, and in almost all of them i saw similar logic on some actions.
We often return object like this:
public class HttpPrjNameResult<T> {
public PrjNameStatus Status { get; set; }
public string Message { get; set; }
public T data{ get; set; }
}
So i wonder :
is there any standart MVC feature for that?
If no - why?
Or may be i use wrong pattern to write code?
UDPATE:
I will update question little bit.
Let's say I'm creating web api, with method UpdateReports, which returns list of updated entity:
public HttpTestResult<List<Report>> UpdateReports(IEnumerable<Reports> reports){
try{
var res = SaveReports(reports);
return new HttpTestResult<List<Report>>{
Status = TestStatus.Success,
Data = res
}
}
catch(Exception e){
logger.Error(e);
return new HttpTestResult<Object>{
Status = TestStatus.Error,
Message = "Error while saving reports"
}
}
}
And i see such logic useful all over the project, i guess more in API style, not in pure mvc, but still.
The question is - am i doing something wrong so this is kinda reinvent wheel, and there is already built in features for that kind of logic
If I understand your question correctly, you are asking how to return either a success or failure result based on whether you encounter an exception?
You may wish to look at the IExceptionFilter (MVC) or IExceptionFilter (Http) interface. These filters will listen out for any exceptions, and perform some custom action that you define, for example (MVC example):
public void OnException(ExceptionContext filterContext)
{
//perform some custom action, e.g. logging
_logger.Log(filterContext.Exception);
//return a particular status
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
If you use an exception filter to handle any exceptions that occur, your controllers are free to concentrate on just being controllers:
[ResponseType(typeof (List<Report>))]
public IHttpActionResult UpdateReports(IEnumerable<Reports> reports)
{
var results = SaveReports(reports).ToList();
return Ok(results);
}
As i understand you basicly looking for Command pattern.
There is a good article that help you to understand one of the ways how you can implement it. I can't say that it's best practice but you can get some ideas from it.
The main idea is to create ICommandHandler and ICommandDispatcher interfaces that help you to place basic logic in one place.
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.