c# - 我可以在 ASP.NET Core 中使用 Content Negotiation 向浏览器返回 View 并向 API 调用返回 JSON 吗?

标签 c# asp.net-mvc asp.net-core

我有一个返回客户列表的非常基本的 Controller 方法。我希望它在用户浏览时返回 ListView ,并将 JSON 返回给在 Accept header 中具有 application/json 的请求。

在 ASP.NET Core MVC 1.0 中这可能吗?

我已经试过了:

    [HttpGet("")]
    public async Task<IActionResult> List(int page = 1, int count = 20)
    {
        var customers = await _customerService.GetCustomers(page, count);

        return Ok(customers.Select(c => new { c.Id, c.Name }));
    }

但默认情况下返回 JSON,即使它不在接受列表中。如果我在浏览器中点击“/customers”,我会得到 JSON 输出,而不是我的 View 。

我想我可能需要编写一个处理 text/html 的 OutputFormatter,但我不知道如何从OutputFormatter,因为这些方法在 Controller 上,我需要知道要呈现的 View 的名称。

有没有我可以调用的方法或属性来检查 MVC 是否能够找到要呈现的 OutputFormatter?类似于以下内容:

[HttpGet("")]
public async Task<IActionResult> List(int page = 1, int count = 20)
{
    var customers = await _customerService.GetCustomers(page, count);
    if(Response.WillUseContentNegotiation)
    {
        return Ok(customers.Select(c => new { c.Id, c.Name }));
    }
    else
    {
        return View(customers.Select(c => new { c.Id, c.Name }));
    }
}

最佳答案

我认为这是一个合理的用例,因为它可以简化从单个 Controller 返回 HTML 和 JSON/XML/等的 API 的创建。这将允许渐进增强以及其他一些好处,尽管在 API 和 Mvc 行为需要截然不同的情况下它可能无法很好地工作。

我已经使用自定义过滤器完成了此操作,但有以下注意事项:

public class ViewIfAcceptHtmlAttribute : Attribute, IActionFilter
{
    public void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.HttpContext.Request.Headers["Accept"].ToString().Contains("text/html"))
        {
            var originalResult = context.Result as ObjectResult;
            var controller = context.Controller as Controller;
            if(originalResult != null && controller != null)
            {
                var model = originalResult.Value;
                var newResult = controller.View(model);
                newResult.StatusCode = originalResult.StatusCode;
                context.Result = newResult;
            }
        }
    }

    public void OnActionExecuting(ActionExecutingContext context)
    {

    }
}

可以添加到 Controller 或操作:

[ViewIfAcceptHtml]
[Route("/foo/")]
public IActionResult Get(){ 
        return Ok(new Foo());
}

或者在 Startup.cs 全局注册

services.AddMvc(x=>
{ 
   x.Filters.Add(new ViewIfAcceptHtmlAttribute());
});

这适用于我的用例,并实现了从同一 Controller 支持 text/html 和 application/json 的目标。我怀疑这不是“最佳”方法,因为它避开了自定义格式化程序。理想情况下(在我看来),这段代码只是另一个 Formatter,如 Xml 和 Json,但它使用 View 渲染引擎输出 Html。不过,该界面有点复杂,这是目前最简单的方法。

关于c# - 我可以在 ASP.NET Core 中使用 Content Negotiation 向浏览器返回 View 并向 API 调用返回 JSON 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40300616/

相关文章:

c# - Xamarin.Forms - 应用程序级别的全局点击事件

c# - "a field initializer cannot reference non static fields"在 C# 中是什么意思?

asp.net-mvc - 手动 Elmah 日志记录

asp.net - 如何使用 JWT 不记名身份验证获取有关 google 身份验证的用户数据?

reactjs - 带有 React SPA 和 Asp.net Core 后端的 IdentityServer4 实现

c# - 使用 wpf mvvm 在同一 View 上的 View 模型之间切换

c# - 如何获取证书支持的算法列表

jquery - 从 Asp.net-mvc Controller 操作返回 File 或 ErrorMessage 的最佳方法是什么?

jquery - 使用 jquery 解析带双引号的 json 响应(JavaScriptSerializer)

c# - Entity Framework 核心 : Fluent api many-to-many