WebAPI和ODataController返回406 Not Acceptable

Iva*_*ono 33 c# odata asp.net-web-api

在将OData添加到我的项目之前,我的路由设置如下:

       config.Routes.MapHttpRoute(
            name: "ApiById",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: new { id = @"^[0-9]+$" },
            handler: sessionHandler
        );

        config.Routes.MapHttpRoute(
            name: "ApiByAction",
            routeTemplate: "api/{controller}/{action}",
            defaults: new { action = "Get" },
            constraints: null,
            handler: sessionHandler
        );

        config.Routes.MapHttpRoute(
            name: "ApiByIdAction",
            routeTemplate: "api/{controller}/{id}/{action}",
            defaults: new { id = RouteParameter.Optional },
            constraints: new { id = @"^[0-9]+$" },
            handler: sessionHandler
Run Code Online (Sandbox Code Playgroud)

所有控制器都提供Get,Put(操作名称为Create),Patch(操作名称为Update)和Delete.例如,客户端使用这些各种标准URL来表示CustomerType请求:

string getUrl =  "api/CustomerType/{0}";
string findUrl = "api/CustomerType/Find?param={0}";
string createUrl = "api/CustomerType/Create";
string updateUrl = "api/CustomerType/Update";
string deleteUrl = "api/CustomerType/{0}/Delete";
Run Code Online (Sandbox Code Playgroud)

然后我添加了一个OData控制器,其动作名称与我的其他Api控制器相同.我还添加了一条新路线:

        ODataConfig odataConfig = new ODataConfig();

        config.MapODataServiceRoute(
            routeName: "ODataRoute",
            routePrefix: null,
            model: odataConfig.GetEdmModel()
        );
Run Code Online (Sandbox Code Playgroud)

到目前为止,我在客户端没有任何改变.当我发送请求时,我收到406 Not Available错误.

路线混乱了吗?我怎么解决这个问题?

Jee*_*Lee 73

如果您使用的是OData V4,请替换 using System.Web.Http.OData;

using System.Web.OData;

在ODataController为我工作.

  • 使用System.Web.OData.*命名空间用于odata v4端点和System.Web.Http.Odata.*是所有旧的odata v1-3的东西.我也遇到了这个问题,因为ms网站上的例子经常将这两个问题混合起来. (6认同)
  • 现在是 Microsoft.AspNet.OData (4认同)
  • 请参阅 https://github.com/Microsoft/aspnet-api-versioning/issues/315:“使用 Microsoft.AspNet.OData v7.0,命名空间已从 System.Web.OData 更改为 Microsoft.AspNet.OData。” (2认同)

Jer*_*her 18

配置路由的顺序具有影响.就我而言,我也有一些标准的MVC控制器和帮助页面.所以在Global.asax:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    GlobalConfiguration.Configure(config =>
    {
        ODataConfig.Register(config); //this has to be before WebApi
        WebApiConfig.Register(config); 

    });
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
}
Run Code Online (Sandbox Code Playgroud)

当我启动项目并且需要时,过滤器和routeTable部件不存在.

ODataConfig.cs:

public static void Register(HttpConfiguration config)
{
    config.MapHttpAttributeRoutes(); //This has to be called before the following OData mapping, so also before WebApi mapping

    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();

    builder.EntitySet<Site>("Sites");
    //Moar!

    config.MapODataServiceRoute("ODataRoute", "api", builder.GetEdmModel());
}
Run Code Online (Sandbox Code Playgroud)

WebApiConfig.cs:

public static void Register(HttpConfiguration config)
{
    config.Routes.MapHttpRoute( //MapHTTPRoute for controllers inheriting ApiController
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
    );
}
Run Code Online (Sandbox Code Playgroud)

作为奖励,这是我的RouteConfig.cs:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute( //MapRoute for controllers inheriting from standard Controller
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}
Run Code Online (Sandbox Code Playgroud)

这必须是那个确切的顺序.我尝试移动调用并最终导致MVC,Api或Odata因404或406错误而中断.

所以我可以打电话:

localhost:xxx/ - >导致帮助页面(家庭控制器,索引页面)

localhost:xxx/api/ - >导致OData $元数据

localhost:xxx/api/Sites - >导致我的SitesController的Get方法继承自ODataController

localhost:xxx/api/Test - >导致我继承自ApiController的TestController的Get方法.


mar*_*oss 11

将routePrefix设置为"api".

ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<CustomerType>("CustomerType");

config.MapODataServiceRoute(routeName: "ODataRoute", routePrefix: "api", model: builder.GetEdmModel());
Run Code Online (Sandbox Code Playgroud)

你使用哪个OData版本?System.Web.OData对于V3 ,检查OData V4使用的正确名称空间System.Web.Http.OData.控制器中使用的命名空间必须与WebApiConfig中使用的命名空间一致.

  • 版本3和版本4的System.Web.OData和System.Web.Http.OData名称空间之间的区别给我带来了很多悲伤.请注意,在我的情况下,此错误导致406 Not Acceptable错误,而不是OP报告的406 Not Available错误. (3认同)
  • 对我来说,正确名称空间的提示是正确的.看来脚手架功能正在使用V3命名空间*System.Web.Http.OData*而不是*System.Web.OData* (2认同)

Cip*_*anu 7

另一件需要考虑的事情是URL区分大小写,因此:

localhost:xxx/api/Sites -> OK
localhost:xxx/api/sites -> HTTP 406

  • 这不会产生404吗? (2认同)

Ste*_*ene 7

我的问题与返回实体模型而不是我暴露的模型(builder.EntitySet<ProductModel>("Products");)有关.解决方案是将实体映射到资源模型.


Tik*_*all 5

我遇到的问题是我已将我的实体集命名为“产品”,并且具有一个ProductController。原来,实体集的名称必须与您的控制器名称匹配。

所以

builder.EntitySet<Product>("Products");
Run Code Online (Sandbox Code Playgroud)

使用名为ProductController的控制器将给出错误。

/ api /产品将给出406

/ api /产品将给出404

因此,使用某些新的C#6功能,我们可以改为:

builder.EntitySet<Product>(nameof(ProductsController).Replace("Controller", string.Empty));
Run Code Online (Sandbox Code Playgroud)


Rob*_*don 5

此页面上的所有优秀解决方案都不适合我。通过调试,我可以看到路由被选中并且 OData 查询正在正确运行。但是,在控制器退出后,它们被破坏了,这表明是格式导致了 OData 全能错误:406 Not Acceptable。

我通过添加一个基于 Json.NET 库的自定义格式化程序来解决这个问题:

public class JsonDotNetFormatter : MediaTypeFormatter
{
    public JsonDotNetFormatter()
    {
        SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/json"));
    }

    public override bool CanReadType(Type type)
    {
        return true;
    }

    public override bool CanWriteType(Type type)
    {
        return true;
    }

    public override async Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
    {
        using (var reader = new StreamReader(readStream))
        {
            return JsonConvert.DeserializeObject(await reader.ReadToEndAsync(), type);
        }
    }

    public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
    {
        if (value == null) return;
        using (var writer = new StreamWriter(writeStream))
        {
            await writer.WriteAsync(JsonConvert.SerializeObject(value, new JsonSerializerSettings {ReferenceLoopHandling = ReferenceLoopHandling.Ignore}));
        }
    }
Run Code Online (Sandbox Code Playgroud)

然后在WebApiConfig.cs,我添加了这一行config.Formatters.Insert(0, new JsonDotNetFormatter())。请注意,我严格遵守 Jerther 的回答中描述的顺序。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        ConfigureODataRoutes(config);
        ConfigureWebApiRoutes(config);
    }

    private static void ConfigureWebApiRoutes(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional });
    }

    private static void ConfigureODataRoutes(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        config.Formatters.Insert(0, new JsonDotNetFormatter());
        var builder = new ODataConventionModelBuilder();
        builder.EntitySet<...>("<myendpoint>");
        ...
        config.MapODataServiceRoute("ODataRoute", "odata", builder.GetEdmModel());
    }
}
Run Code Online (Sandbox Code Playgroud)