28

I would like to respond via a JsonResult from a piece of Asp.Net Core middleware but it's not obvious how to accomplish that. I have googled around alot but with little success. I can respond via a JsonResult from a global IActionFilter by setting the ActionExecutedContext.Result to the JsonResult and that's cool. But in this case I want to effectively return a JsonResult from my middleware. How can that be accomplished?

I framed the question with regard to the JsonResult IActionResult but ideally the solution would work for using any IActionResult to write the response from the middleware.

RonC
  • 31,330
  • 19
  • 94
  • 139

4 Answers4

21

Middleware is a really low-level component of ASP.NET Core. Writing out JSON (efficiently) is implemented in the MVC repository. Specifically, in the JSON formatters component.

It basically boils down to writing JSON on the response stream. In its simplest form, it can be implemented in middleware like this:

using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;

// ...

public async Task Invoke(HttpContext context)
{
    var result = new SomeResultObject();
    var json = JsonConvert.SerializeObject(result);
    await context.Response.WriteAsync(json);
}
Henk Mollema
  • 44,194
  • 12
  • 93
  • 104
  • 3
    @Hank, thanks for your response. I'm aware I could use Response.WriteAsync to send the raw json but such an approach is prone to overlooking things like forgetting to set the `content-type` repsonse header to "application/json; charset=utf-8". :-) That's why I specifically asked how to respond via a JsonResult rather than simply how to send a json response. Also, I framed the question with regard to the `JsonResult` `IActionResult` but ideally the solution would work for using any `IActionResult` to write the response from the middleware. I'll update my question to indicate this. – RonC Mar 30 '17 at 12:20
  • @RonC `IActionResult` and `JsonResult` are MVC primitives. Middleware is far lower in the layering of ASP.NET Core. – Henk Mollema Mar 30 '17 at 13:55
  • @Hank, I know. So the question kinda boils down to how to we reach higher up into the stack to leverage a feature of MVC and do it from a place _much_ lower in the stack, ie. the middleware. – RonC Mar 30 '17 at 13:57
  • It doesn't work that way, unfortunately. Getting something to work will be extremely hacky I think. Maybe wrap a TestServer inside your middleware and return what it gives you (via MVC inside the TestServer - like I said, hacky.) – ssmith Mar 31 '17 at 00:20
18

For others that may be interested in how to return the output of a JsonResult from middleware, this is what I came up with:

  public async Task Invoke(HttpContext context, IHostingEnvironment env) {
        JsonResult result = new JsonResult(new { msg = "Some example message." });
        RouteData routeData = context.GetRouteData();
        ActionDescriptor actionDescriptor = new ActionDescriptor();
        ActionContext actionContext = new ActionContext(context, routeData, actionDescriptor);
        await result.ExecuteResultAsync(actionContext);
    }

This approach allows a piece of middleware to return output from a JsonResult and the approach is close to being able to enable middleware to return the output from any IActionResult. To handle that more generic case the code for creating the ActionDescriptor would need improved. But taking it to this point was sufficient for my needs of returning the output of a JsonResult.

RonC
  • 31,330
  • 19
  • 94
  • 139
  • 2
    This is an excellent answer and worked for me. The voodoo here is the ExecuteResultAsync is writing to the response stream for you. – BinaryPatrick Apr 12 '21 at 14:36
  • @BinaryPatrick It's software development not something yucky. It turns out that even in a typically controller, when `return View(this);` is executed it returns a `ViewResult` that does not output anything immediately. Then later in the pipeline, the .NET framework calls `ExecuteResultAsync` to render that `Result` to the response stream. So the approach of this answer simply uses what I found from digging into the .NET Core source code. Glad it was helpful. – RonC Apr 12 '21 at 15:02
8

As explained by @Henk Mollema, I have also made use of Newtonsoft.Json JsonConvert class to serialize the object into JSON through SerializeObject method. For ASP.NET Core 3.1 I have used JsonConvert inside the Run method. Following solution worked for me:

Startup.cs

using Newtonsoft.Json;

// ...

public class Startup
{
    public void Configure(IApplicationBuilder app) 
    {
        app.Run(async context =>
        {
            context.Response.StatusCode = 200;
            context.Response.ContentType = "application/json";
            await context.Response.WriteAsync(JsonConvert.SerializeObject(new
            {
                message = "Yay! I am a middleware"
            }));
        });
    }
}
Zaki Mohammed
  • 969
  • 14
  • 24
5

As of ASP.NET Core 5.0, you can use WriteAsJsonAsync to write JSON to the response stream. This method handles serialization and setting the content type header appropriately.

For example

using Microsoft.AspNetCore.Http;

// ..

public async Task InvokeAsync(HttpContext context)
{
    var result = new SomeResultObject();
    await context.Response.WriteAsJsonAsync(result);
}
deymundson
  • 51
  • 1
  • 3