27

In webApi2 i could write a custom ActionResult by inheriting IHttpActionResult.

Sample :

public class MenivaActionResult : IHttpActionResult
    {
        private readonly HttpRequestMessage _request;
        private readonly ServiceResponseBase _response;

        public MenivaActionResult(HttpRequestMessage request, ServiceResponseBase response)
        {
            _request = request;
            _response = response;
        }


        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken token)
        {

            HttpResponseMessage httpresponseMessage = _response.Exception != null
                ? _request.CreateErrorResponse((HttpStatusCode)_response.HttpCode,
                    _response.Exception.Message+":"+_response.Exception.HelpCode)
                : _request.CreateResponse((HttpStatusCode)_response.HttpCode, _response.Data);
            return Task.FromResult(httpresponseMessage);
        }
    }

The ServiceResponseBase class object holds all the exception and data from service layer..

How i can port this code to asp.net core. I tried but i dont know how create a response message from HttpRequestMessage object in .net core. The IActionResult only have Task ExecuteResultAsync(ActionContext context) method. how i can modify the response from this method

Binson Eldhose
  • 993
  • 3
  • 14
  • 35

3 Answers3

38

Here is an example:

public class TestResult
{
    public Exception Exception { get; set; }
    public object Data { get; set; }
}

public class TestActionResult : IActionResult
{
    private readonly TestResult _result;

    public TestActionResult(TestResult result)
    {
        _result = result;
    }

    public async Task ExecuteResultAsync(ActionContext context)
    {
        var objectResult = new ObjectResult(_result.Exception ?? _result.Data)
        {
            StatusCode = _result.Exception != null
                ? StatusCodes.Status500InternalServerError
                : StatusCodes.Status200OK
        };

        await objectResult.ExecuteResultAsync(context);
    }
}

ObjectResult is the type your results are converted if you return a non-IActionResult from an action. It will do content negotiation for you.

You could also inherit from ObjectResult and setup the status code and data to be written in the constructor.

More on content negotiation in ASP.NET Core: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/formatting#content-negotiation

juunas
  • 54,244
  • 13
  • 113
  • 149
  • But i need to pass objects than a simple string ,i need to to use the default content negotiation feature so cannot convert the object in to any form of string representation like json or xml. _request.CreateResponse((HttpStatusCode)_response.HttpCode, _response.Data); this line in my webapi2 automatically negotiate the data – Binson Eldhose Mar 04 '17 at 11:01
  • @BinsonEldhose See my updated example. That would be the job of `ObjectResult`. – juunas Mar 04 '17 at 11:10
  • nice example man, but what about folder structure for custom actionresults like this ? – David Noreña Jun 12 '17 at 00:19
  • 1
    You mean what should the folder name be for this class? That's up to you and your team :) I typically use a Results folder. – juunas Jun 12 '17 at 05:02
  • How do you unit test TestActionResult? I am curious to make sure the ActionContext is mocked and assert for the expected results; – George Taskos Dec 18 '17 at 18:44
  • Try yourself. If you have a specific problem, write a new question. – juunas Dec 18 '17 at 19:15
  • @juunas I need to pass extra information like a message, requestId along with the result. What I did is inherit a class from `ObjectResult`, populated the custom object result via the ctr and did this `await customObjectResult.ExecuteResultAsync(context);` but the response json only had data no other values. Is it because `ObjectResult` ctr takes data and the output is only data? If so how can I achieve the intended result? – JB's Aug 04 '18 at 17:16
  • Fantastic! Thanks! – Web Dev Mar 22 '19 at 00:02
3

Despite @juunas answer is technically correct, what you really try to achieve is a custom error handling. And the better way is to decouple this logic using Action Filter, Middleware filter or the separate Middleware (depends on what scope you what to cover).

The main idea is that controller should not be responsible for handling exceptions. It should return expected result with OK status or say that something wrong (throw exception) and delegate to other parts of pipeline what to do with problem.

Look into SO: Error handling in ASP.NET Core 1.0 Web API (Sending ex.Message to the client) for examples. Also Error Handling in ASP.NET Core doc may be useful.

Community
  • 1
  • 1
Set
  • 47,577
  • 22
  • 132
  • 150
1

Why not just use the StatusCode method? Something like this as an example:

public async Task<IActionResult> ExamleEndpoint()
{
    // ... some code here which concatenates result string and selects a status code
    var statusCode = HttpStatusCode.OK;
    var message = "Some string response here but StatusCode also accepts Object value";
    return StatusCode((int) statusCode, message);
}
KEMBL
  • 536
  • 6
  • 10
  • 1
    Not sure why this was downvoted. Worked great for a local web api returning a message in an unsuccessful result without including the exception detail. – Jason Apr 06 '22 at 03:18