Search code examples
c#asp.net-coreasp.net-core-mvc

How to retrieve HTTP Code and Content from IActionResult?


I've 7 actions in my controllers. I've refactored them an was able to isolate the common portion.

public IActionResult Get()
{
    var response = CommonCode();
}

public IActionResult Get(guid id)
{
    var response = CommonCode();
}

public IActionResult Post(ViewModel vm)
{
    var response = CommonCode();
}

This is where I refactored the common code.

provate IActionResult CommonCode()
{
   if(userHasNoPermission())
     return Forbid();

   if(IdProvidedDoesntExist())
     return BadRequest();

   //...
}

When I look inside the response, I see only one method: ExecuteResultAsync().

Is there a way to retrieve the HTTP code that I sent inside the helper method?

I'd like for instance to stop the processing if it's 500, retrieve the message to add to the ModelState* if it's 400, but proceed if it's OK.


Solution

  • The return of CommonCode is simply some type of IActionResult. The "result" is not actually a "response". That comes later when the action is fully returned back into the request pipeline (which has not occurred yet when you're calling the method directly in code). As result, concepts like HTTP status code aren't even relevant yet. If you return something like StatusCodeResult, that's technically only a suggested status code. If there's an exception later in the request pipeline or some piece of middleware explicitly changes the status code for some reason, it will be different.

    Long and short, you're trying to conflate two unrelated things together. I think you simply want to know what happened in CommonCode, and think the HTTP status is the best way to determine that. In actuality, you'd be better served by returning a tuple or doing something like pattern matching:

    With a tuple, you can essentially return more than one thing from your CommonCode method. For example, you could do something like:

    private (int, IActionResult) CommonCode()
    {
       if(userHasNoPermission())
         return (403, Forbid());
    
       if(IdProvidedDoesntExist())
         return (400, BadRequest());
    
       //...
    }
    

    Then:

    public IActionResult Get()
    {
        (var status, var response) = CommonCode();
        // branch on `status`
    }
    

    Or, with pattern matching:

    public IActionResult Get()
    {
        var response = CommonCode();
        switch (response)
        {
            case ForbidResult forbid:
                // do something for forbidden
                break;
            case BadRequestResult badRequest:
                // do something for bad request
                break;
        }
    }