【问题标题】:Web API OData Controller Returns "406 - Not Acceptable" When Mapping Results映射结果时 Web API OData 控制器返回“406 - 不可接受”
【发布时间】:2016-03-30 06:12:18
【问题描述】:

我查看了所有关于此的 SO 帖子,但没有成功。我可以调用OData 控制器并查看检索、映射和打包的数据。控制器方法退出,我得到一个406 - Not Acceptable。我只使用 System.Web.OData 命名空间(OData 版本 v4.0.30319),并且使用 Postman,我手动将 ContentAccept 标头设置为 'application/ json' 没有运气

在过去的 2 个小时里,我可能错过了一些关于类似问题的帖子。任何指针将不胜感激。

更新: 问题似乎出在 Mapper 代码 (Automapper) 中,如下 Igor 所指出。看起来有承诺返回数据库(EF)实体,而不是映射类。有了这些知识,我找到了这个 SO 帖子,但它没有提供解决方案:ApiController vs ODataController when exposing DTOs。我们是否不得不返回数据库实体或者可以映射结果?如果是这样,那对我来说就是一个交易破坏者。

控制器

[EnableQuery]
[HttpGet]
public async Task<IHttpActionResult> Get()
    {
        var list = await db.ConfigSets.ToListAsync();
        Mapper.CreateMap<ConfigSet, ConfigSetDTO>();
        var configSetDTOs = Mapper.Map<List<ConfigSet>, List<ConfigSetDTO>>(list);
        return Ok(configSetDTOs); //IT LOOKS GOOD HERE!
    }

WebApiConfig:

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
            config.EnableCors();

            // OData - must be before config.Routes when using a prefix. In this case "api"
            config.MapODataServiceRoute("odata", "api", GetEdmModel(), new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));
            config.EnsureInitialized();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );          
        }

        private static IEdmModel GetEdmModel()
        {
            var builder = new ODataConventionModelBuilder();
            builder.Namespace = "Services";
            builder.ContainerName = "DefaultContainer";
            builder.EntitySet<ConfigSet>("Configuration");
            var edmModel = builder.GetEdmModel();
            return edmModel;
        }
    }

【问题讨论】:

  • 我不确定Mapper.Map 返回什么,但无论它是什么,它都应该实现IQueryable 或其中的泛型。您是否尝试过使用硬编码的 List&lt;ConfigSetDTO&gt; 实例进行测试,并在 return 调用中返回 Ok(myList.AsQueryable()); ?这应该可以帮助您缩小问题所在,整体配置或您尝试返回的数据的数据或格式。
  • @Igor..Mapper 是 Automapper。我在没有它的情况下尝试了这段代码以及您的建议并得到了相同的结果。
  • 我想我明白了。 builder.EntitySet&lt;ConfigSet&gt;("Configuration"); 表示应该从 Get() 方法返回的预期类型是 ConfigSet 类型,但您返回的是 ConfigSetDTO 类型。要查看这是否确实是问题,请尝试返回 ConfigSet 实例的硬编码列表,但在 Ok 方法中调用 AsQueryable()。 IE; return Ok(myConfigSetList.AsQueryable());
  • @Igor...你在做某事。映射一定是问题,因为它有效: var list = await db.ConfigSets.ToListAsync();return Ok(list);
  • 除了从 SQL 视图中提取的 EF 数据之外,我从未使用过来自 oData 的 Get() 函数,因此我不确定什么是可能的或不可能的。据我了解,整个目的是 MS Odata Web API 控制器根据传入的 URL 处理所有查询逻辑,这就是为什么您希望将 oData 与 Get-er 函数一起使用,因为它提供了一种标准方法查询数据,不需要额外的代码来过滤后端,并且过滤发生在数据库而不是内存中。如果你在这之间推动像 Automapper 这样的东西,我怀疑它会起作用。

标签: asp.net-web-api odata asp.net-web-api2 automapper


【解决方案1】:

旧版本的 OData 允许您只公开任何旧的 IQueryable,无论它包含数据库对象还是 DTO。

但是,我可以肯定地说您在这里使用了错误的 AutoMapper - 您在客户端而不是在数据库上进行映射,这仍然允许您返回 IQueryable。不要使用 Mapper.Map,而是在查询中使用 .ProjectTo 扩展方法:

using AutoMapper.QueryableExtensions;  // This using is required

[EnableQuery]
[HttpGet]
public IHttpActionResult Get()
{
    var query = db.ConfigSets.ProjectTo<ConfigSetDTO>();
    return Ok(query);
}

您也不应该在动作方法中定义您的地图 - 它们应该在您的应用程序启动时定义一次。

【讨论】:

  • 我仍然收到这些更改的 406 错误。映射现在通过从 Global.asax 调用的 Automapper 配置文件进行。有什么想法吗?
  • 我认为你的提议很有希望并且有道理,但我得到了 406。
【解决方案2】:

除了我从 SQL 视图中提取的 EF 数据之外,我从未使用过来自 oData 的 Get() 函数,因此我不确定什么是可能的或不可能的。据我了解,整个目的是 MS Odata Web API 控制器根据传入的 URL 处理所有查询逻辑,这就是为什么您希望使用 oData 和 Get 方法,因为它提供了一种标准方法查询数据,过滤后端不需要额外的代码,过滤发生在数据库而不是内存中。如果你在这之间推动像 Automapper 这样的东西,我怀疑它会不会起作用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-10
    • 2014-02-11
    • 2016-03-22
    • 2015-06-01
    • 2016-03-07
    • 2013-08-28
    • 1970-01-01
    相关资源
    最近更新 更多