0

I'm trying to achieve something using ASP.NET Core MVC which seems like it should be trivial but from what I've researched it doesn't seem to be an easy task.

I would like to return both a ViewResult and JSON from a single endpoint (potentially other formats as well but I'll keep it simple for now). Ideally whether I return a ViewResult or JSON would be based on the built-in content negotiation.

Of course one could simply check the accept header inside the controller, if HTML has been requested return the ViewResult, otherwise return the JSON result, but this would be silly as the number of endpoints and the supported formats grew.

I assumed this would be possible to do using custom middleware, I could simply await the response of the endpoint, check what the accept header was that the user agent requested, and then either wrap the result up in a ViewResult or JSONResult. This is the ideal solution as it would work for all endpoints and save a massive amount of code duplication, but it doesn't seem like it is possible.

My overall goal is to have my endpoints set up in such a way that users can use the exact same endpoint whether in a browser or through postman and they'll get the response format they would expect, i.e. either HTML or JSON. Has anybody been able to achieve this result in a way that doesn't require hacky solutions?

4
  • To be clear , do you want to add a middleware so that you can return the response as per the requested data type ? if you want a middleware than may be the following link can help : stackoverflow.com/questions/37395227/… Commented Jun 7, 2020 at 18:17
  • Do you actually mean HTML and not XML? Supporting JSON and HTML from one controller action seems confusing since one is a pure data format and the other is a more "presentation-aware" format. If not XML, could you show an example response in both JSON and HTML? Commented Jun 7, 2020 at 19:04
  • To be clear, yes I want to return a HTML (not XML) representation of a particular endpoint whilst also allowing consumers to return a JSON representation. If you imagine an endpoint that return a list of items, users that went to that endpoint in the browser would see HTML with CSS etc, but users who simply requested that data through postman or what have you would get a JSON data structure of those items. I think it's perfectly feasible, and if not a superior, way of designing an API. Commented Jun 8, 2020 at 6:59
  • Here is a link to a github issue that discusses this problem in more depth. github.com/dotnet/aspnetcore/issues/3891 Commented Jun 8, 2020 at 7:03

1 Answer 1

1

You can implement MVC Action filter to easily achieve such functionality.

using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Net.Http.Headers;

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class ViewJsonActionFilterAttribute : Attribute, IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        //result must be OkObjectResult
        if (context.Result is OkObjectResult okResult)
        {
            var accept = context.HttpContext.Request.Headers[HeaderNames.Accept];
            switch (accept)
            {
                //changing result to ViewResult
                case "text/html":
                    var controller = (Controller)context.Controller;
                    context.Result = controller.View(okResult.Value);
                    break;
                case "application/json":
                default:
                    //do nothing
                    break;
            }
        }
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {
        //do nothing
    }
}

Then apply the attribute to the specific action/controller or globally. Always return Ok from controller as a successful result so filter can process it as expected.

//important: controller should be derived from Controller class to contain View method
public class TestController : Controller
{
    [HttpGet]
    [ViewJsonActionFilter]
    public IActionResult TestViewJson()
    {
        var model = new ViewJsonTestViewModel
        {
            Id = 55,
            Name = "I'm a test model"
        };

        return Ok(model);
    }
}

If you set request Accept header to text/html you will get html, otherwise it will return json.

Sign up to request clarification or add additional context in comments.

1 Comment

This looks interesting and seems to be along the lines I was thinking.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.