question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using ModelState and Other ControllerBase Properties Outside of a Controller

See original GitHub issue

I’m working on moving my API logic in my PATCH endpoint to a Mediatr Command. When applying my patch document, I usually check the model state like below. Normally, I’m doing this from a controller so there is no issue, but when moving this into a RequestHandler, I no longer have access to the model state property since I’m outside of the controller. How would you recommend going about this?

The model state logic I’d like to use outside of the controller:

updatePartialValueToReplaceCommand.PatchDoc.ApplyTo(valueToReplaceToPatch, ModelState); // apply patchdoc updates to the updatable valueToReplace

if (!TryValidateModel(valueToReplaceToPatch))
{
    return ValidationProblem(ModelState);
}

The rest of the code for context:

Patch Endpoint


        [HttpPatch("{valueToReplaceId}")]
        public IActionResult PartiallyUpdateValueToReplace(int valueToReplaceId, JsonPatchDocument<ValueToReplaceForUpdateDto> patchDoc)
        {
            var query = new UpdatePartialValueToReplaceCommand(valueToReplaceId, patchDoc);
            var result = _mediator.Send(query);

            switch (result.Result.ToUpper())
            {
                case "NOTFOUND":
                    return NotFound();
                case "NOCONTENT":
                    return NoContent();
                default:
                    return BadRequest();
            }
        }

UpdatePartialValueToReplaceCommand

public class UpdatePartialValueToReplaceCommand : IRequest<string>
    {
        public int ValueToReplaceId { get; set; }
        public JsonPatchDocument<ValueToReplaceForUpdateDto> PatchDoc { get; set; }

        public UpdatePartialValueToReplaceCommand(int valueToReplaceId, JsonPatchDocument<ValueToReplaceForUpdateDto> patchDoc)
        {
            ValueToReplaceId = valueToReplaceId;
            PatchDoc = patchDoc;
        }
    }

(BROKEN) UpdatePartialValueToReplaceHandler

    public class UpdatePartialValueToReplaceHandler : IRequestHandler<UpdatePartialValueToReplaceCommand, string>
    {
        private readonly IValueToReplaceRepository _valueToReplaceRepository;
        private readonly IMapper _mapper;

        public UpdatePartialValueToReplaceHandler(IValueToReplaceRepository valueToReplaceRepository
            , IMapper mapper)
        {
            _valueToReplaceRepository = valueToReplaceRepository ??
                throw new ArgumentNullException(nameof(valueToReplaceRepository));
            _mapper = mapper ??
                throw new ArgumentNullException(nameof(mapper));
        }

        public async Task<string> Handle(UpdatePartialValueToReplaceCommand updatePartialValueToReplaceCommand, CancellationToken cancellationToken)
        {
            if (updatePartialValueToReplaceCommand.PatchDoc == null)
            {
                return "BadRequest";
            }

            var existingValueToReplace = _valueToReplaceRepository.GetValueToReplace(updatePartialValueToReplaceCommand.ValueToReplaceId);

            if (existingValueToReplace == null)
            {
                return "NotFound";
            }

            var valueToReplaceToPatch = _mapper.Map<ValueToReplaceForUpdateDto>(existingValueToReplace); // map the valueToReplace we got from the database to an updatable valueToReplace model
            updatePartialValueToReplaceCommand.PatchDoc.ApplyTo(valueToReplaceToPatch, ModelState); // apply patchdoc updates to the updatable valueToReplace -- THIS DOESN'T WORK IN A MEDIATR COMMAND BECAUSE I DON'T HAVE CONTROLLERBASE CONTEXT

            if (!TryValidateModel(valueToReplaceToPatch))
            {
                return ValidationProblem(ModelState);
            }

            _mapper.Map(valueToReplaceToPatch, existingValueToReplace); // apply updates from the updatable valueToReplace to the db entity so we can apply the updates to the database
            _valueToReplaceRepository.UpdateValueToReplace(existingValueToReplace); // apply business updates to data if needed

            _valueToReplaceRepository.Save(); // save changes in the database

            return "NoContent";
        }
    }

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:11 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
d1820commented, May 15, 2020

off the cuff would look something like this.

in controller

var response = await mediator.Send(UpdatePartialValueToReplaceCommand);
if(response == CustomValidationCode)
{
 return ValidationProblem(ModelState);
}else{
 return StatusCode(response);
}


public UpdatePartialValueToReplaceCommand(int valueToReplaceId, JsonPatchDocument<ValueToReplaceForUpdateDto> patchDoc,
            Func<bool> validator)
        {
            ValueToReplaceId = valueToReplaceId;
            PatchDoc = patchDoc;
            Validator = validator;
        }


public async Task<IActionResult> Handle(UpdatePartialValueToReplaceCommand updatePartialValueToReplaceCommand, CancellationToken cancellationToken)
        {
            if (updatePartialValueToReplaceCommand.PatchDoc == null)
            {
                    throw new CustomValidationException("message") // this would handled in exception handler, see documents for example
 OR
return HttpStautsCode.BadRequest;
            }

            var existingValueToReplace = _valueToReplaceRepository.GetValueToReplace(updatePartialValueToReplaceCommand.ValueToReplaceId);

            if (existingValueToReplace == null)
            {
                throw new CustomValidationException("message") // this would handled in exception handler, see documents for example
 OR
return HttpStautsCode.BadRequest;
            }

            var valueToReplaceToPatch = _mapper.Map<ValueToReplaceForUpdateDto>(existingValueToReplace); // map the valueToReplace we got from the database to an updatable valueToReplace model
            updatePartialValueToReplaceCommand.PatchDoc.ApplyTo(valueToReplaceToPatch, updatePartialValueToReplaceCommand.Controller.ModelState); // apply patchdoc updates to the updatable valueToReplace

            if (!updatePartialValueToReplaceCommand.Controller.TryValidateModel(valueToReplaceToPatch))
            {
return CustomValidationCode //int
             
            }

            _mapper.Map(valueToReplaceToPatch, existingValueToReplace); // apply updates from the updatable valueToReplace to the db entity so we can apply the updates to the database
            _valueToReplaceRepository.UpdateValueToReplace(existingValueToReplace); // apply business updates to data if needed

            _valueToReplaceRepository.Save(); // save changes in the database

            return HttpStatusCode.OK;
        }

I have a whole entire Controller builder somewhere that was create to moq and test all aspects of a .net controller. If i get some time i can get a gist together of it.

0reactions
pdevito3commented, May 16, 2020

@d1820, that gist is fantastic. Very much appreciated.

I started looking at it last night and got the basics working, but I’m going to need to tweak it to get the tryvalidatemodel() method to work as well as the Link.Url part for my get collection query, but this is a great base to work from. Thanks again for taking the time.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Get ModelState outside of controller and ActionFilter
You can get it by using the IActionContextAccessor : var actionContextAccessor = _serviceProvider.
Read more >
Model Validation in ASP.NET Web API - ASP.NET 4.x
It is up to the controller action to check the model state and respond appropriately. If model validation fails, this filter returns an...
Read more >
How to Use ModelState Validation in ASP.NET Core Web API
When we talk about ModelState , we mean ModelState property of the ControllerBase abstract class in the Microsoft.AspNetCore.Mvc namespace.
Read more >
Should you unit-test API/MVC controllers in ASP.NET Core?
In this post I discuss unit testing of API/MVC controllers in ASP. ... Controllers should typically check the ModelState property before ...
Read more >
From MVC to Minimal APIs with ASP.NET Core 6.0 - Ben Foster
ASP.NET 6.0 introduces an alternative way to build HTTP APIs, using the aptly named “Minimal APIs”. This post provides a step-by-step guide ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found