I am trying to use Async controller and am not able to figure out how would one validate the user input.
Following are the two async methods defined in my controller. Should I check for ModelState.IsValid in the SearchAsync method or SearchCompleted method. If SearchAsync then how will return the view result as its return type is void. If SearchCompleted then how will the method know about searchForm parameter.
[HttpPost]
[ValidateAntiForgeryToken]
public void SearchAsync(BusinessSearchForm searchForm)
{
AsyncManager.OutstandingOperations.Increment();
new Thread(() =>
{
var suggestions = _searchSvc.GetSuggestions(searchForm.BusinessName, searchForm.StreetAddress, searchForm.City, searchForm.PostalCode);
AsyncManager.Parameters["suggestions"] = suggestions;
AsyncManager.OutstandingOperations.Decrement();
}).Start();
}
public ActionResult SearchCompleted(IEnumerable<BusinessSuggestionBase> suggestions)
{
return View(suggestions);
}
The following seems to work for me. I end up checking for modelstate in both methods. Added the initial model as a param to the completed method. Asp.net Mvc seemed to persist the modelstate between the two methods
[HttpPost]
[ValidateAntiForgeryToken]
public void SearchAsync(BusinessSearchForm searchForm)
{
if (ModelState.IsValid)
{
AsyncManager.OutstandingOperations.Increment();
new Thread(() =>
{
if (ModelState.IsValid)
{
var suggestions = _searchBusinessSvc.GetSuggestions(searchForm.BusinessName, searchForm.StreetAddress, searchForm.City, searchForm.PostalCode);
AsyncManager.Parameters["suggestions"] = suggestions;
}
AsyncManager.Parameters["searchForm"] = searchForm;
AsyncManager.OutstandingOperations.Decrement();
}).Start();
}
}
public ActionResult SearchCompleted(BusinessSearchForm searchForm,IEnumerable<BusinessSuggestionBase> suggestions)
{
if (ModelState.IsValid)
{
TempData["suggestions"] = suggestions;
return RedirectToAction("SearchResult");
}
return View(searchForm);
}
You can use
AsyncManager.Parameters['ModelIsValid'] = false;
in the Async method, and
if(AsyncManager.Parameters['ModelIsValid'] == false) { ... }
in the Completed method to check and see if there was a validation issue. Simply do not increment the outstanding operations, and do not perform any further logic. The Completed method will fire, and you can check the the value.
Related
I have a .net core 3.1 WEB Api service where I use OData (V4)
I have an endpoint where every query through GET METHOD is working fine, and with the same endpoint I can use POST METHOD and insert new records to database.
The problem is, the DELETE and PATCH METHODs are not working (or maybe they are, but if I try to use update or delete I always get a 404 Not Found error). I try to call them ( DELETE, PATCH) from POSTMAN, but I get the same 404 error, however the methods are in the controller.
MyController:
[ApiController]
[Route("[controller]")]
public class UsersController : ODataController
{
[HttpGet]
[EnableQuery()]
public IEnumerable<User> Get()
{
return new Context().Userek;
}
[HttpPatch]
[EnableQuery]
public async Task<IActionResult> Patch([FromODataUri] int id, Delta<User> user)
{
var ctx = new Context();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var entity = await ctx.Userek.FindAsync(id);
if (entity == null)
{
return NotFound();
}
user.Patch(entity);
try
{
await ctx.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (id != 23)
{
return NotFound();
}
else
{
throw;
}
}
return Updated(entity);
}
public async Task<IActionResult> Delete([FromODataUri] int id)
{
var ctx = new Context();
var user = await ctx.Userek.FindAsync(id);
if (user == null)
{
return NotFound();
}
ctx.Userek.Remove(user);
await ctx.SaveChangesAsync();
return StatusCode(404);
}
}
Thank you for any help!
The rule is that controllers shouldn't have business logic, instead they should delegate it to the services. But when we do that, we can't handle all possible cases and return appropriate HTTP response.
Let's look at an example. Let's say that we are building some kind of a social network, and we need to create an endpoint for rating (liking or disliking) a post.
First let's take a look at an example where we delegate the logic to the service, this is our controller action:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
PostRating newPostRating = _postsService.Rate(postId, ratingType, user);
return Created(newPostRating);
}
Do you see a problem in this? What if there is no post with the given id, how would we return a not found response? What if user has no permissions to rate a post, how would we return a forbidden response?
PostsService.Rate can only return a new PostRating, but what about other cases? Well, we could throw an exception, we would need to create a lot of custom exception, so that we can map them to the appropriate HTTP responses. I don't like to use exceptions for this, I think there is a better way to do handle these cases instead of exceptions. Because I think that cases when post doesn't exist and when user has no permissions aren't exceptional at all, they're just normal cases just like rating a post successfully.
What I propose, is handling that logic in a controller instead. Because in my opinion, that should be a controllers responsibility anyway, to check all of the permissions before commiting an action. So this is how I would do it:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
var post = _postsRepository.GetByIdWithRatings(postId);
if (post == null)
return NotFound();
if (!_permissionService.CanRate(user, post))
return Forbidden();
PostRating newPostRating = new PostRating
{
Post = post,
Author = user,
Type = ratingType
};
_postRatingsRepository.Save(newPostRating);
return Created(newPostRating);
}
This is the way it should be done in my opinion but I bet that someone would say that this is too much logic for the controller, or that you shouldn't use a repository in it.
If you don't like using a repository in controller than where instead would you put a method that gets or saves posts? In service? So there would be PostsService.GetByIdWithRatings and PostsService.Save that would do nothing else but just call PostsRepository.GetByIdWithRatings and PostsRepository.Save. This so unnecessary and only causes boilerplate code.
Update:
Maybe someone will say to check the permissions using PostsService and then call PostsService.Rate. This is bad because it involves more unnecessary trips to database. For an example, it would probably be something like this:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
if(_postsService.Exists(postId))
return NotFound();
if(!_postsService.CanUserRate(user, postId))
return Forbidden();
PostRating newPostRating = _postsService.Rate(postId, ratingType, user);
return Created(newPostRating);
}
Do I even need to explain any further why this is bad?
There's a number of ways to handle this, but the closest thing to a "best practice" method is probably using a result class. For example, if your service method creates a rating and then returns that rating it created, you instead return an object that encapsulates the rating along with other relevant information, such as success status, error messages, if any etc.
public class RateResult
{
public bool Succeeded { get; internal set; }
public PostRating PostRating { get; internal set; }
public string[] Errors { get; internal set; }
}
Then, your controller code would become something like:
public IActionResult Rate(long postId, RatingType ratingType)
{
var user = GetCurrentUser();
var result = _postsService.Rate(postId, ratingType, user);
if (result.Succeeded)
{
return Created(result.PostRating);
}
else
{
// handle errors
}
}
What I did (just now) is created new class ApiResult
public class ApiResult
{
public int StatusCode { get; private set; } = 200;
public string RouteName { get; private set; }
public object RouteValues { get; private set; }
public object Content { get; private set; }
public void Ok(object content = null)
{
this.StatusCode = 200;
this.Content = content;
}
public void Created(string routeName, object routeValues, object content)
{
this.StatusCode = 201;
this.RouteName = routeName;
this.RouteValues = routeValues;
this.Content = content;
}
public void BadRequest(object content = null)
{
this.StatusCode = 400;
this.Content = content;
}
public void NotFound(object content = null)
{
this.StatusCode = 404;
this.Content = content;
}
public void InternalServerError(object content = null)
{
this.StatusCode = 500;
this.Content = content;
}
}
And a controller base class with a single method TranslateApiResult
public abstract class CommonControllerBase : ControllerBase
{
protected IActionResult TranslateApiResult(ApiResult result)
{
if (result.StatusCode == 201)
{
return CreatedAtAction(result.RouteName, result.RouteValues, result.Content);
}
else
{
return StatusCode(result.StatusCode, result.Content);
}
}
}
And now in controller I do:
[ApiController]
[Route("[controller]/[action]")]
public class MyController : CommonControllerBase
{
private readonly IMyApiServcie _service;
public MyController (
IMyApiServcie service)
{
_service = service;
}
[HttpGet]
public async Task<IActionResult> GetData()
{
return TranslateApiResult(await _service.GetData());
}
}
In your services you inject repositories and other dependencies:
public class MyApiServcie : IMyApiServcie
{
public async Task<ApiResult> GetData()
{
var result = new ApiResult();
// do something here
result.Ok("success");
return result;
}
}
Now, the reason for Api prefix before the Service is that this service is not meant to be the final service containing all logic.
At this point I would split the business logic into different domains so the services (or facades) would end up without Api prefix in them just to differentiate between i.e. CarService. Preferably these services will not know of anything related to API responses, statuses etc. It's up to you how implement it, though.
I'm using FluentValidation with WebAPI in DotNet core 2. I've written tests for the validator successfully, but now I'm trying to mock the validator for my controller. Controller as follows:
[Route("[controller]")]
public class SecurityController : Controller {
private readonly IValidator<AuthenticateRequest> _authenticateRequestValidator;
public SecurityController(IValidator<AuthenticateRequest> authenticateRequestValidator) {
_authenticateRequestValidator = authenticateRequestValidator;
}
[HttpPost]
[AllowAnonymous]
[Route("auth")]
public async Task<IActionResult> AuthenticateAsync([FromBody] AuthenticateRequest req) {
// Validate
var validator = await _authenticateRequestValidator.ValidateAsync(req);
if(!validator.IsValid) {
return BadRequest();
}
// ...snip
}
}
AuthenticateRequest looks like this:
public class AuthenticateRequest {
public string Username { get; set; }
public string Password { get; set; }
}
And the validator is as follows:
public class AuthenticateRequestValidator : AbstractValidator<AuthenticateRequest> {
/// <summary>
/// Provides a validator for <see cref="AuthenticateRequest" />
/// </summary>
public AuthenticateRequestValidator() {
RuleFor(x => x.Username)
.NotNull()
.NotEmpty()
.WithMessage("Username is required");
RuleFor(x => x.Password)
.NotNull()
.NotEmpty()
.WithMessage("Password is required");
}
}
It's being injected into the controller with dot net core's standard DI. Not posting code as it's not relevant to this issue, as its a testing issue.
I'm testing with xunit, Moq and AutoFixture. Here are two tests:
public class SecurityControllerTests {
private readonly IFixture Fixture = new Fixture().Customize(new AutoMoqCustomization {ConfigureMembers = true});
private readonly Mock<IValidator<AuthenticateRequest>> authenticateRequestValidatorMock;
public SecurityControllerTests() {
authenticateRequestValidatorMock = Mock.Get(Fixture.Create<IValidator<AuthenticateRequest>>());
}
[Fact]
public async Task Authenticate_ValidatesRequest() {
// Arrange
var request = Fixture.Create<AuthenticateRequest>();
authenticateRequestValidatorMock
.Setup(x => x.ValidateAsync(It.Is<AuthenticateRequest>(v => v == request), default(CancellationToken)))
.Returns(() => Fixture.Create<Task<ValidationResult>>())
.Verifiable();
var controller = new SecurityController(authenticationServiceMock.Object, tokenisationServiceMock.Object, authenticateRequestValidatorMock.Object);
// Act
await controller.AuthenticateAsync(request);
// Assert
authenticateRequestValidatorMock.Verify();
}
[Fact]
public async Task Authenticate_Returns400_WhenUsernameValidationFails() {
// Arrange
var request = Fixture.Create<AuthenticateRequest>();
var validationResultMock = new Mock<ValidationResult>();
validationResultMock
.SetupGet(x => x.IsValid)
.Returns(() => true);
authenticateRequestValidatorMock
.Setup(x => x.ValidateAsync(It.Is<AuthenticateRequest>(v => v == request), default(CancellationToken)))
.Returns(() => new Task<ValidationResult>(() => validationResultMock.Object));
var controller = new SecurityController(authenticationServiceMock.Object, tokenisationServiceMock.Object, authenticateRequestValidatorMock.Object);
// Act
var result = await controller.AuthenticateAsync(request);
// Assert
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.IsType<SerializableError>(badRequestResult.Value);
}
}
I need to mock ValidationResult so I can ignore the actual validator logic (which is tested elsewhere) and test the controller logic. There are many other dependencies injected, and much more code, but the pasted code is the crux of the problem and produces the same results when everything else is stripped out.
First test passes, second runs forever when it hits the var validator = await _authenticateRequestValidator.ValidateAsync(req); line in the controller.
Its worth noting that ValidationResult.IsValid is a virtual readonly property.
What is wrong with the second test?
Did you try Asp.Net Core - FluentValidation integration? By this way you dont need to pass Validator depedencies to constructor.
https://github.com/JeremySkinner/FluentValidation/wiki/i.-ASP.NET-Core-integration
FluentValidation fills ModelState in case of validation error and you use it like ;
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
For testing it you set ModelState of you Controllers Mock
var controller = new SecurityController(authenticationServiceMock.Object, tokenisationServiceMock.Object, authenticateRequestValidatorMock.Object);
controller.ModelState.AddModelError("test", "test");
// Act
IActionResult actionResult = await controller.AuthenticateAsync(request);
var badRequestObjectResult = actionResult as BadRequestObjectResult;
Assert.NotNull(badRequestObjectResult);
var serializableError = badRequestObjectResult.Value as SerializableError;
// Assert
Assert.NotNull(result);
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
var serializableError = assert.IsType<SerializableError>(badRequestResult.Value)
Assert.True(((string[])serializableError["test"])[0] == "test");
Leaving ModelState empty would be enough to ignore the actual validator logic i think.
Also FluentValidation have built-in testing api. You can test your validation logic separately.
https://github.com/JeremySkinner/FluentValidation/wiki/g.-Testing
How do I return an HttpStatus code from API methods in my ASP.NET Core 1.0 if there's a problem?
If the method is supposed to return a particular object type, when I try return an Http status code, I get an error saying I can't convert my object to status code.
[HttpPost]
public async Task<SomeObject> Post([FromBody] inputData)
{
// I detect an error and want to return BadRequest HttpStatus
if(inputData == null)
return new HttpStatusCode(400);
// All is well, so return the object
return myObject;
}
Return an IActionResult from your controller action instead:
public async Task<IActionResult> Post([FromBody] InputData inputData)
{
if(inputData == null)
{
return new HttpStatusCodeResult((int) HttpStatusCode.BadRequest);
}
//...
return Ok(myObject);
}
If you instead want to remove such null checks from the controller you could define a custom attribute:
public class CheckModelForNullAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.ActionArguments.Any(k => k.Value == null))
{
context.Result = new BadRequestObjectResult("The model cannot be null");
}
}
}
This way we dont have to bother with the model being null in the action.
[HttpPost]
[CheckModelForNull]
public async Task<SomeObject> Post([FromBody]InputData inputData)
{
// My attribute protects me from null
// ...
return myObject;
}
Using mvc3, is there some variable to inform if I came from a Create to Edit action?
If the routing is used as well in a GET I want a certain behavior, otherwise if the CreateAction has been fired I want to open a different context Edit.
Failed first solution: Creating a extra Action Edit, fires: The current request for action 'Edit' on controller type 'XController' is ambiguous between the two action methods.
More important, is there a misinterpretation of MVC? Does this solution sound weird to anyone? I'm facing nice doubts with mvc. :)
You can either add an optional parameter to your Edit action that flags whether or not you came from the Add action or you could create an entirely new action (with a unique name from Edit).
The former would look something like:
public ActionResult Edit(int id, bool? fromCreate = false)
{
if(fromCreate)
{
// do your special processing
}
}
And the latter, obviously, would be:
public ActionResult Edit(int id)
{
}
public ActionResult EditNewlyCreated(int id)
{
}
In your create view or create controller action (no razor markup if its in the action):
#{ TempData["FromCreate"] = true; }
And then in your edit get action:
public ActionResult Edit()
{
if( TempData.ContainsKey("FromCreate") )
{
var fromCreate = (bool)TempData["FromCreate"];
TempData.Remove("FromCreate");
if( fromCreate )
{
//insert logic
}
}
}
If you have something linke this in MyView view:
#model ModelClass
#Html.BeginForm()
{
if(Model.Id > 0)
{
#Html.HiddenFor(m => m.Id);
}
...
}
And in controller:
public ActionResult Edit(int id)
{
var model = modelRepository.GetById(id);
Return View("MyView", model);
}
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var model = modelRepository.GetById(id);
TryUpdateModel(model)
{
modelRepository.Save(model);
}
}
public ActionResult Create()
{
var model = new ModelClass();
Return View("MyView", model);
}
[HttpPost]
public ActionResult Create(ModelClass model)
{
...
}
In view, if Model.Id > 0 it means that we entered view using Edit action and when form posts, if there will be Id field in post parameters (hidden for id) then Edit(with HttpPost) will be called, else if there will not be Id parameter then Create action will be called
If you have 2 Edit methods, they must have different method inputs to differentiate them.
public ActionResult Edit(int id)
{
return View(db.GetWidget(id));
}
public ActionResult Edit(int id, string username)
{
ViewBag.Username = username;
return View(db.GetWidget(id));
}
Or, just make the method into one with an optional parameter
public ActionResult Edit(int id, string username = "")
{
ViewBag.Username = username;
return View(db.GetWidget(id));
}
I would also recommend painting a method attribute such as [HttpGet], [HttpPost], etc.