ワンクリックで
content-negotiation
Use when configuring API response formats, custom formatters, or Accept header handling.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Use when configuring API response formats, custom formatters, or Accept header handling.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Use when adding caching to .NET APIs or optimizing response times with distributed cache, output cache, or ETags.
Use when building controller-based REST APIs with action results, model binding, or MediatR integration.
Use when creating RESTful API controllers with MediatR dispatch and ProblemDetails error responses.
Use when designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when building minimal API endpoints with route groups, filters, or TypedResults.
Use when setting up OpenAPI spec generation or Scalar API documentation UI.
| name | content-negotiation |
| description | Use when configuring API response formats, custom formatters, or Accept header handling. |
| metadata | {"category":"api","agent":"api-designer","when-to-use":"When configuring content type negotiation, custom formatters, or Accept headers"} |
Accept header to determine response formatapplication/json when no preference is specifiedapplication/problem+json for error responses (RFC 9457)// Program.cs — global JSON options
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.Converters.Add(
new JsonStringEnumConverter());
options.SerializerOptions.DefaultIgnoreCondition =
JsonIgnoreCondition.WhenWritingNull;
options.SerializerOptions.PropertyNamingPolicy =
JsonNamingPolicy.CamelCase;
});
// Controller-specific
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(
new JsonStringEnumConverter());
options.JsonSerializerOptions.DefaultIgnoreCondition =
JsonIgnoreCondition.WhenWritingNull;
});
builder.Services.AddControllers()
.AddXmlDataContractSerializerFormatters();
// Endpoints now respond to:
// Accept: application/json → JSON
// Accept: application/xml → XML
public sealed class CsvOutputFormatter : TextOutputFormatter
{
public CsvOutputFormatter()
{
SupportedMediaTypes.Add(
MediaTypeHeaderValue.Parse("text/csv"));
SupportedEncodings.Add(Encoding.UTF8);
}
protected override bool CanWriteType(Type? type)
{
return type is not null &&
(typeof(IEnumerable).IsAssignableFrom(type) ||
type.IsGenericType);
}
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context,
Encoding selectedEncoding)
{
var response = context.HttpContext.Response;
var items = (IEnumerable<object>)context.Object!;
var sb = new StringBuilder();
var properties = context.ObjectType!
.GetGenericArguments()[0]
.GetProperties();
// Header row
sb.AppendLine(string.Join(",",
properties.Select(p => p.Name)));
// Data rows
foreach (var item in items)
{
var values = properties.Select(p =>
EscapeCsvValue(p.GetValue(item)?.ToString() ?? ""));
sb.AppendLine(string.Join(",", values));
}
await response.WriteAsync(sb.ToString(), selectedEncoding);
}
private static string EscapeCsvValue(string value)
{
if (value.Contains(',') || value.Contains('"') ||
value.Contains('\n'))
return $"\"{value.Replace("\"", "\"\"")}\"";
return value;
}
}
// Registration
builder.Services.AddControllers(options =>
{
options.OutputFormatters.Add(new CsvOutputFormatter());
});
// Allow ?format=csv or /orders.csv
builder.Services.AddControllers(options =>
{
options.FormatterMappings.SetMediaTypeMappingForFormat(
"csv", "text/csv");
options.FormatterMappings.SetMediaTypeMappingForFormat(
"xml", "application/xml");
});
[HttpGet]
[FormatFilter]
[Route("/orders.{format?}")]
public async Task<ActionResult<List<OrderResponse>>> GetOrders() { }
// Minimal API — explicit content type
app.MapGet("/orders/{id}", async (Guid id, ISender sender) =>
{
var order = await sender.Send(new GetOrderQuery(id));
return order is not null
? Results.Ok(order)
: Results.NotFound();
}).Produces<OrderResponse>(StatusCodes.Status200OK, "application/json");
// Return specific content type
app.MapGet("/orders/export", async (ISender sender) =>
{
var csv = await sender.Send(new ExportOrdersCsvQuery());
return Results.Text(csv, "text/csv", Encoding.UTF8);
});
// Return file
app.MapGet("/orders/{id}/invoice", async (Guid id, ISender sender) =>
{
var pdf = await sender.Send(new GenerateInvoiceQuery(id));
return Results.File(
pdf, "application/pdf", $"invoice-{id}.pdf");
});
builder.Services.AddProblemDetails();
// Automatic problem+json responses for errors
app.UseExceptionHandler();
app.UseStatusCodePages();
// Custom problem details
app.MapGet("/orders/{id}", async (Guid id) =>
{
return Results.Problem(
title: "Order Not Found",
detail: $"No order exists with ID {id}",
statusCode: StatusCodes.Status404NotFound,
type: "https://tools.ietf.org/html/rfc9110#section-15.5.5");
});
Client sends:
Accept: application/json → JSON response
Accept: application/xml → XML response
Accept: text/csv → CSV response
Accept: */* → Default (JSON)
Accept: application/problem+json → Error responses
Server responds with:
Content-Type: application/json; charset=utf-8
application/problem+json for errorsAddJsonOptions or ConfigureHttpJsonOptions configurationOutputFormatter implementationsAddXmlDataContractSerializerFormatters calls[Produces] and [Consumes] attributes on controllersAddProblemDetails in Program.cs[Produces] attributes to document supported content types| Content Type | Use Case |
|---|---|
application/json | Default for API responses |
application/problem+json | Error responses (RFC 9457) |
text/csv | Data export |
application/xml | Legacy integrations |
application/pdf | Report generation |
application/octet-stream | File downloads |