بنقرة واحدة
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 designing gRPC services, proto files, or adding gRPC-Web or JSON transcoding.
Use when writing async code, propagating CancellationTokens, or fixing async/await pitfalls.
Use when applying or enforcing C# coding style — namespaces, sealed classes, var usage, XML docs.
Use when applying modern C# idioms — records, pattern matching, primary constructors, collection expressions.
Use when registering services, choosing lifetimes, or implementing DI patterns like decorator or keyed services.
Use when selecting or implementing design patterns in C# — factory, builder, strategy, decorator, or mediator.
| 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 |