You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 3.1控制器返回CSS或404异常问题排查

问题描述

我需要实现一个控制器Action来返回CSS样式表,只有当响应类型为text/css时浏览器才会使用该CSS。当前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 (b == null)
    {
        return NotFound(); // 实际返回406 Not Acceptable
    }

    return Ok(Content($@"
nav.navbar {{
    background-color: {b.PrimaryBrandingColour};
}}
", "text/css"));
}

我还创建了自定义输出格式化器:

/// <summary>
/// 自定义CSS输出格式化器
/// </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);
    }
}

现在的问题是:当租户不存在返回NotFound()时,实际响应是406 Not Acceptable,而我期望返回404状态码,同时要么返回text/css类型的空响应体,要么返回application/problem+json类型的ProblemDetails响应。想知道这种需求能否实现?

解决方案

你的需求完全可以实现,核心是解决406的触发逻辑,并自定义404场景下的响应格式:

1. 406错误的原因

[Produces("text/css")]限制了该Action只能输出text/css类型响应,但默认NotFound()返回的是ProblemDetails对象,而你的自定义CssOutputFormatter仅支持ContentResult和string类型,框架找不到匹配的格式化器,因此返回406 Not Acceptable。

2. 实现404 + text/css空响应

直接构造ContentResult返回空CSS内容并指定404状态码即可:

if (b == null)
{
    return new ContentResult
    {
        Content = string.Empty,
        ContentType = "text/css",
        StatusCode = StatusCodes.Status404NotFound
    };
}

这样浏览器会收到404状态码,同时响应类型为text/css,符合要求。

3. 实现404 + application/problem+json响应

如果需要返回标准的ProblemDetails,可按以下步骤调整:

  • 修改[Produces]特性,允许输出多种响应类型:
    [Produces("text/css", "application/problem+json")]
    
  • 手动构造ProblemDetails并返回:
    if (b == null)
    {
        var problem = new ProblemDetails
        {
            Status = StatusCodes.Status404NotFound,
            Title = "租户不存在",
            Detail = $"未找到租户 {tenant} 的品牌配置"
        };
        return NotFound(problem);
    }
    

ASP.NET Core默认包含JSON格式化器,此时会自动将ProblemDetails序列化为application/problem+json类型响应,状态码保持404。

4. 优化自定义格式化器(可选)

如果需要让格式化器兼容ProblemDetails类型,可调整CssOutputFormatter:

protected override bool CanWriteType(Type? type)
{
    return type == typeof(ContentResult) || type == typeof(string) || type == typeof(ProblemDetails);
}

public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
    string buffer;
    if (context.Object is ProblemDetails problem)
    {
        buffer = JsonSerializer.Serialize(problem);
        context.HttpContext.Response.ContentType = "application/problem+json";
    }
    else if (context.Object is ContentResult content)
    {
        buffer = content.Content;
    }
    else
    {
        buffer = context.Object?.ToString() ?? string.Empty;
    }

    await context.HttpContext.Response.WriteAsync(buffer, selectedEncoding);
}

内容的提问来源于stack exchange,提问作者Richard Barraclough

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 02:10:39