I want to make a controller action to return css.
The browser will only user the CSS if the response type is text/css.
Here is my action.
[HttpGet("{tenant}/stylesheet")]
[Produces("text/css")]
[ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status404NotFound)]
public async Task<ActionResult<BrandingDTO>> Stylesheet(string tenant)
{
Branding b = dbContext.Branding.SingleOrDefault(z => z.Tenant == tenant);
if (t == null)
{
return NotFound(); // Actually returns 406 unacceptable.
}
return Ok(Content($@"
nav.navbar {{
background-color: {b.PrimaryBrandingColour};
}}
", "text/css"));
}
In the not found case I don't need the response to be application/problem+json, but it happens that that's what it is.
I've had to create a custom output formatter.
/// <summary>
/// https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/custom-formatters?view=aspnetcore-6.0
/// </summary>
internal class CssOutputFormatter : TextOutputFormatter
{
public CssOutputFormatter()
{
SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/css"));
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);
}
protected override bool CanWriteType(Type? type)
{
return type == typeof(ContentResult) || type == typeof(string);
}
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
string buffer = context.Object.ToString();
ContentResult content = context.Object as ContentResult;
if(content != null)
{
buffer = content.Content;
}
await context.HttpContext.Response.WriteAsync(buffer, selectedEncoding);
}
}
The problem is that in the not found case the respose is actually 406 not acceptable; I would expect 404 and an empty body of type text/css or a non-empty body of type application/problem+json.
Can this work?
Or should I make do with the response being either 202 with text/css else broken?