ASP.NET MVC formatters
This library provides MessagePack-based formatters for ASP.NET MVC, offering significant performance improvements over the default JSON protocol.
Benefits
- Smaller Payloads: MessagePack produces significantly smaller payloads compared to JSON
- Faster Serialization: Binary serialization is typically faster than text-based formats
- Type Safety: Leverages MessagePack's type-safe serialization system
- NativeAOT Compatible: Works seamlessly with .NET Native AOT compilation
Installation
Install the NuGet package:
Add MessagePackInputFormatter and/or MessagePackOutputFormatter to your input and/or output formatter collections respectively, as demonstrated in the configuration sample below:
void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddMvcOptions(option =>
{
option.OutputFormatters.Clear();
option.OutputFormatters.Add(new MessagePackOutputFormatter(Witness.GeneratedTypeShapeProvider));
option.InputFormatters.Clear();
option.InputFormatters.Add(new MessagePackInputFormatter(Witness.GeneratedTypeShapeProvider));
});
}
// Generate shapes for types commonly used in communicating errors in ASP.NET Core MVC.
[GenerateShapeFor<ProblemDetails>]
[GenerateShapeFor<SerializableError>]
[GenerateShapeFor<string[]>]
partial class Witness;
Usage
Server
Add [Produces("application/x-msgpack")] to the action or controller that returns msgpack-encoded data.
[Route("api/v1/[controller]")]
[Produces("application/x-msgpack")]
[ApiController]
public partial class PersonController : ControllerBase
{
public ActionResult<IEnumerable<Person>> Get()
{
return this.Ok(new Person[]
{
new(1, "Person 1"),
new(2, "Person 2"),
});
}
// GET: api/v1/person/{slug}
[HttpGet("{id}")]
public ActionResult<Person> Get(int id)
{
if (id > 10)
{
this.ModelState.AddModelError("id", "ID must be 10 or less.");
return this.BadRequest(this.ModelState);
}
return this.Ok(new Person(id, $"Person {id}"));
}
[GenerateShape]
public partial record Person(int Id, string Name);
// Add an attribute for each top-level type that must be serializable
// That does not have have its own [GenerateShape] attribute on it.
// Here, we add `int` because it's taken as a parameter type for an action.
// Although strictly speaking `int` is already covered implicitly because it
// also appears as a property on Person.
[GenerateShapeFor<Person[]>]
[GenerateShapeFor<int>]
partial class Witness;
}
Client
The JavaScript client should send data with application/x-msgpack as the HTTP Content-Type header.
The Accept header should include this same content-type so that the server will utilize the MessagePack formatter to send the optimized data format back to the client.
<html>
<head>
<script src="https://unpkg.com/msgpack-lite@0.1.26/dist/msgpack.min.js"></script>
</head>
<body>
<h1>Welcome to ASP.NET MVC!</h1>
<p>This is a sample view.</p>
<input type="text" id="personId" placeholder="Enter person ID (optional)">
<button id="fetchBtn">Fetch Person Data</button>
<div id="errorMessage" style="color: red;"></div>
<div id="result"></div>
<script>
document.getElementById('fetchBtn').addEventListener('click', async () => {
const id = document.getElementById('personId').value.trim();
const url = id ? `/api/v1/person/${id}` : '/api/v1/person';
document.getElementById('errorMessage').textContent = ''; // Clear previous error
try {
const response = await fetch(url, {
headers: { 'Accept': 'application/x-msgpack' }
});
const buffer = await response.arrayBuffer();
const data = msgpack.decode(new Uint8Array(buffer));
if (!response.ok) {
// Assuming data is ModelState with errors, e.g., { id: ['ID must be 10 or less.'] }
const errorMsg = data?.id?.[0] || 'Unknown error';
document.getElementById('errorMessage').textContent = errorMsg;
document.getElementById('result').innerHTML = '';
} else {
document.getElementById('result').innerHTML = '<pre>' + JSON.stringify(data, null, 2) + '</pre>';
}
} catch (error) {
document.getElementById('errorMessage').textContent = 'Error: ' + error.message;
document.getElementById('result').innerHTML = '';
}
});
</script>
</body>
</html>