【问题标题】:ASP.net core return a view() or json/xml based on requested contenttypeASP.net 核心根据请求的内容类型返回 view() 或 json/xml
【发布时间】:2017-07-31 00:05:05
【问题描述】:

question here 是关于我想要实现的目标。在控制器中,来自 repos 的所有数据都将放入 ViewModel 对象中。要求是:

if (requested content type = "json"){
    return convert-to-json(ViewModel);
else
    return view(ViewModel);

但是,question here(检查Request.Headers["Accept"].Contains("application/json"))中建议的解决方案看起来不太好。

Microsoft 的 content negotiation article 谈论标准内容协商(JSON/XML 等...)

我可以有一个单独的 WebAPI 项目,但在这种情况下,我不能证明编写另一组代码只是调用存储库并返回 JSON。

【问题讨论】:

  • 解决方案“看起来不太好”是什么意思?我觉得很好。
  • 您有框架内提供的解决方案,但不喜欢它的外观?然后编写一个辅助方法并“隐藏”“丑陋”的细节。
  • 当您想要返回相同的数据作为响应的一部分时,内容协商工作。您希望根据条件返回两种不同的类型,因此内容协商无法处理。
  • @user2058413:只是一个问题 - 您使用 Formatters 链接了文章 - 您是说您不想想要使用的东西吗?
  • 我不是 OP,但我想我理解这个问题的意图。开箱即用的内容协商不支持 text/html。链接问题上提供的答案让开发人员将代码添加到操作中,这并不理想,因为它必须在每个操作中重复,并且因为它不使用 ASP.NET 的内容协商功能。也就是说,这有点重复,所以我发布了对原始问题的新答案。

标签: asp.net-mvc asp.net-web-api asp.net-core


【解决方案1】:

不要试图重新发明轮子......

首先,我认为您应该认真考虑使用内容协商 ASP Core 采用的开箱即用策略(您链接了那篇文章)并使用不同的格式化程序,因为该框架通过为您查询 Accept headers 使用正确的 HTTP 标准。

如果你必须...

如果您需要其他东西,我认为您需要的解决方案是使用Filters 来“隐藏”您提到的脏位。您仍然需要检查 Accept headers MIME Type 的内容,但您可以使用基于属性的系统来执行此操作(如果您真的不想使用 Formatters)。

这是简单的属性。注意IActionFilter 接口,它要求我们在OnActionExecuted 和OnActionExecuting 方法中构建一些功能

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class FormatReponseFilterAttribute : Attribute, IActionFilter
{
    private enum FormatResponseType { Json, Xml, Unknown }
    private FormatResponseType _requestedType { get; set; }
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var _result = (ObjectResult)filterContext.Result;
        switch (_requestedType)
        {

            case FormatResponseType.Json:
                var data = new { Data = _result.Value };
                filterContext.Result = new JsonResult(data);
                break;
            case FormatResponseType.Xml:
                filterContext.Result = new XmlResult(_result.Value);
                break;
            case FormatResponseType.Unknown:
            default:
                throw new InvalidOperationException("Uknown Content Type ain Accept Header");
        }
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {

        var _contentType = filterContext.HttpContext.Request.Headers["Accept"];

        switch (_contentType)
        {
            case string s when (s.Contains("application/json")):
                _requestedType = FormatResponseType.Json;
                break;
            case string e when (e.Contains("/xml")):
                _requestedType = FormatResponseType.Xml;
                break;
            default:
                throw new ArgumentException("Unknown Accept Type - dont know how to handle: " + _contentType);
        }
    }
}
  • OnActionExecuting :在访问 Action 方法之前运行。这使我们能够保存有关请求本身的信息。这是我们检查标头以查看是否有我们识别的 Accept MIME 内容类型的地方。

  • OnActionExecuted :此方法在控制器的 Action 方法完成后执行。这将查询上下文的Result 并使用内置的JsonResult 或使用Wallsmedias XmlResult 对其进行更新。您需要在此处添加更多逻辑,例如在转换之前检查 Result 是否确实有某些内容。您可以在此处找到安装说明以及如何使用XmlResult:

    https://github.com/Wallsmedia/XmlResult

    然后你只需像这样用FormatResponseFilter 装饰你的控制器方法:

    // GET api/values
    [HttpGet]
    [FormatReponseFilter]
    public IActionResult Get()
    {
        TestModel model = new TestModel { Id = 24, Name = "Gareth" };
        return new ObjectResult(model);
    }
    

完成这项工作的最后一步是将 XML 格式化程序添加到可用的服务中,在 Startup.cs 内:

        services.AddMvc()
                .AddXmlFormaterExtensions();

这是一个很好的 Postman 显示 XML:

这是一个显示 JSON 的漂亮 Postman:

另外,如果您想使用我在此处发布到 Github 上的代码,其中包括 Postman 配置,以便您可以尽快起床并进行测试:

Source Code

【讨论】:

  • @user2058413:如果这有帮助,请告诉我。
  • 内置内容协商的问题在于它不支持开箱即用的 text/html。同样,此代码不支持 text/html 或使用视图引擎。我认为 OP 想要的是使用内置视图引擎的自定义格式化程序。我用一个类似的类回答了引用的问题,该类只处理带有 ViewResult 的文本/html。其他请求则留给内置内容协商处理。
猜你喜欢
  • 2012-08-02
  • 1970-01-01
  • 1970-01-01
  • 2017-12-15
  • 1970-01-01
  • 2012-10-14
  • 1970-01-01
  • 2015-06-16
  • 1970-01-01
相关资源
最近更新 更多