Codebelt

Codebelt.Extensions.Carter.AspNetCore.Text.Json

System.Text.Json response negotiation for Carter endpoints.

.NET 10.0 MIT v1.0.4 553 downloads

Overview

Codebelt.Extensions.Carter.AspNetCore.Text.Json turns the core Codebelt.Extensions.Carter negotiation abstraction into a concrete JSON negotiator for Carter. It is the package to use when your modules should answer JSON requests through System.Text.Json and you want that negotiation logic registered as a Carter response negotiator instead of hand-written per endpoint.

The package stays narrow on purpose. It does not define endpoint modules or serializer options of its own, but binds Carter's negotiator contract to JsonFormatterOptions, a UTF-8 default encoding, and a JsonFormatter implementation.

Key APIs

JsonResponseNegotiator is the package's central type. It derives from ConfigurableResponseNegotiator<JsonFormatterOptions>, so the inherited CanHandle, GetEncoding, and Handle<T> flow becomes a ready-to-register negotiator for application/json responses.

JsonResponseNegotiator(IOptions<JsonFormatterOptions>) takes the formatter options from dependency injection, which lets the negotiator use the same JsonFormatterOptions configuration that the rest of the application uses.

JsonResponseNegotiator.GetFormatter returns a JsonFormatter built from the current options, which is the concrete serializer that writes the response body for negotiated requests.

Basic usage

using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Codebelt.Extensions.Carter.AspNetCore.Text.Json;
using Codebelt.Extensions.Xunit;
using Cuemon.Extensions.Text.Json.Formatters;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using Xunit;

namespace MyProject.Tests;

public class JsonNegotiationTest : Test
{
    public JsonNegotiationTest(ITestOutputHelper output) : base(output) { }

    [Fact]
    public async Task ShouldSerializeNegotiatedJsonResponse()
    {
        var context = new DefaultHttpContext();
        context.Response.Body = new MemoryStream();
        var negotiator = new JsonResponseNegotiator(Options.Create(new JsonFormatterOptions()));

        Assert.True(negotiator.CanHandle(MediaTypeHeaderValue.Parse("application/json")));
        await negotiator.Handle(context.Request, context.Response, new OrderStatus(42, "packing"), CancellationToken.None);

        context.Response.Body.Position = 0;
        var body = await new StreamReader(context.Response.Body).ReadToEndAsync();
        TestOutput.WriteLine(body);
        Assert.StartsWith("application/json", context.Response.ContentType);
        Assert.Contains("packing", body);
    }

    private sealed record OrderStatus(int Id, string State);
}

Use this pattern when you want a Carter negotiator that serializes response models with the built-in System.Text.Json stack. It matters because the negotiator keeps content negotiation, response encoding, and serializer selection in one reusable registration instead of scattering JSON response logic across endpoints.

Installation

dotnet add package Codebelt.Extensions.Carter.AspNetCore.Text.Json

Usage guidance

Adopt this package when JSON is your default wire format and you want Carter to negotiate responses through JsonResponseNegotiator with application-wide JsonFormatterOptions. If you need Newtonsoft.Json-specific behavior such as that serializer's converters or contract resolver model, use Codebelt.Extensions.Carter.AspNetCore.Newtonsoft.Json instead.

Family packages