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.
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);
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.
So far I tried searching for an answer, what I found was something query string related, custom model binders or something with the viewbag (irrelevant). Being still pretty new to C# and not very familiar with its internal workings such as this, so I don't feel I can make sense of the answers or translate them into this situation. (Like this) With this question I'm hoping to find an answer with comment or a few of explanation.
The situation:
I want to test if the model binding attributes are configured correctly. Main motivation is TDD, a specific configuration is required and needs to be test driven. Another motivation is that if someone messes with it and breaks it, the test will immediately tell where the problem is.
Example Controller with a single endpoint (the route has to be that way):
[ApiController]
[Route("/cats")]
public class CatsController : ControllerBase
{
[HttpGet]
[Route("cat-breed/{cat_breed}/cat-size/{cat_size}")]
public IActionResult GetMultipleCats([FromRoute] GetCatsRequestObject request)
{
//implementation is not important
return Ok();
}
}
A request object to encapsulate input fields (one of our standards):
public class GetCatsRequestObject
{
[FromRoute(Name = "cat_breed")] public string CatBreed { get; set; }
[FromRoute(Name = "cat_size")] public string CatSize { get; set; }
}
What I'm looking for is the test, where I somehow do a call with the url to whatever is doing model binding, and then in the controller I check if the values from the url are in the correct places in/and in my request object. Something like this:
private CatsController _controllerUnderTest = new CatsController();
[Test]
public void GivenSomeUrl_WhenControllerIsCalledWithIt_ThenParametersFromUrlGetBindedCorrectly()
{
//arrange
string urlpart = "/cats/cat-breed/British%20Shorthair/cat-size/Medium%20Large";
GetCatsRequestObject expectedResult = new GetCatsRequestObject() { CatBreed = "British Shorthair", CatSize = "Medium Large" };
// The rest of the setup I don't know how to do
//act
// somehow do a call with the url, rather than request object.
//assert
// somehow capture the model binding result and compare it with expected result.
}
Is this possible to test? How would you do this? I realize this is a lengthy post, so I thank you for taking your time to read this.
All samples about Web API and return Resource states on the server look like this:
var resource = repository.Get(id);
if(resource == null)
{
return NotFound();
}
return resource;
This works in the most simple architectures.
But having much business logic and services calling that repositories I do not see how I can still use this approach.
In our architecture every web api controller gets a service injected and each service gets one or many repositories or even other services injected.
Now my services do not have simple Get methods returning a resource.
These services could look like:
action(string materialNumber)
{
var pdf = service.CreateProductPDF(materialNumber);
return pdf;
}
class Service
{
public XXX CreateProductPDF(string materialNumber)
{
// Do repository call here but HOW do I let the action know that the materialNumber does not exist?
// Create product pdf from materialNumber
}
}
My question is inside the comment above.
Your domain should be able to transport a value object from the repository to service.
That value object can be called DomainResult and it should contain both the domain objects you're requesting along with domain validation information (i.e. broken rules).
For example:
// It's an OVERSIMPLIFICATION, but it should give you the hint
// to keep you in the right track...
// This code requires C# 6 as it's using expression-bodied properties!
public class DomainResult<TResult>
{
public TResult Result { get; set; }
public List<BrokenRule> BrokenRules { get; } = new List<BrokenRule>();
public bool IsSuccessful => BrokenRules.Count == 0;
}
For example, your method might look like this:
class Service
{
public DomainResult<PdfDocument> CreateProductPDF(string materialNumber)
{
DomainResult<PdfDocument> result = repo...;
// result object might contain a BrokenRule containing a code/message
// "Product couldn't be created because materialNumber wasn't found"
return result;
}
}
And then you can return a 404 in your Web API resource method depending on which rules have been broken during the domain transaction. Also, you should add these broken rule messages to the ModelState using ModelState.AddModelError within your API controller.
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.