OData v4.0 - PUT, PATCH, DELETE возврат 404

У нас есть проект C# .NET Web Api со следующими пакетами Nuget, среди прочих:

  • MVC 5.2.3
  • Microsoft ASP.NET Web API 2.2 для OData v4.0 (версия 6.0.0)
  • Microsoft.AspNet.OData.Versioning (версия 2.1.0)

В IIS глаголы PUT, PATCH и DELETE были включены для ExtensionlessUrl-Integrated-4.0 в файле applicationhost.config.

Ниже приведен WebApiConfig.cs

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        System.Web.Routing.RouteTable.Routes.Ignore("robots.txt");
        System.Web.Routing.RouteTable.Routes.Ignore("{resource}.axd/{*pathInfo}");
        // http://weblogs.asp.net/imranbaloch/handling-http-404-error-in-asp-net-web-api
        System.Web.Routing.RouteTable.Routes.MapHttpRoute(
            name: "Error404",
            routeTemplate: "{*url}",
            defaults: new { controller = "Error", action = "Handle404" }
        );

        config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;

        // NOTE: Method below removed and functionality to replace it not working due to bug https://github.com/OData/WebApi/issues/812
        //config.EnableCaseInsensitive(caseInsensitive: true);

        // http://stackru.com/questions/30987439/elmah-axd-on-webapi-2-2-no-http-resource-was-found
        config.Routes.MapHttpRoute(
            "AXD", "{resource}.axd/{*pathInfo}", null, null,
            new StopRoutingHandler());

        // we will use attribute routing
        config.MapHttpAttributeRoutes();

        // set default page size and total number of rows to return from query
        config.AddODataQueryFilter(new EnableQueryAttribute
        {
            PageSize = ConfigurationWrapper.Singleton.ODataPageSize,
            MaxTop = ConfigurationWrapper.Singleton.ODataMaxTop,
            MaxExpansionDepth = ConfigurationWrapper.Singleton.ODataMaxExpansionDepth
        });

        config.AddApiVersioning(o =>
        {
            o.AssumeDefaultVersionWhenUnspecified = true;
            o.ReportApiVersions = true;
        });

        // http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-routing-conventions
        // Create the default collection of built-in conventions
        IList<IODataRoutingConvention> conventions = ODataRoutingConventions.CreateDefault();

        // Insert the custom convention at the start of the collection; caters for ~/entityset/key/navigation/key
        conventions.Insert(0, new NavigationIndexRoutingConvention());

        config.MapODataServiceRoute(
            routeName: "odata",
            routePrefix: null,
            model: EdmModelBuilder.GetEdmModel(),
            pathHandler: new DefaultODataPathHandler(),
            routingConventions: conventions,
            batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer));

        //config.MapVersionedODataRoutes(
        //routeName: "odata",
        //routePrefix: null,
        //models: EdmModelBuilder.GetEdmModels(config),
        //pathHandler: new DefaultODataPathHandler(),
        //routingConventions: GetConventions());

        // EnableDependencyInjection is required if you want to have OData routes and custom routes together in a controller
        config.EnableDependencyInjection();
        config.Count().Filter().OrderBy().Expand().Select().MaxTop(null); //new line

        config.Formatters.Remove(config.Formatters.XmlFormatter);

        // The XML formatter is not well enough supported by OData v4.0 (apparently works with OData v3.0), reverting to JSON only
        config.Formatters.InsertRange(0, ODataMediaTypeFormatters.Create());

        config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

        /* ReferenceLoopHandling.Ignore: Json.NET will ignore objects in reference loops and not serialize them. The first time an object is encountered
         * it will be serialized as usual but if the object is encountered as a child object of itself the serializer will skip serializing it.
         */
        config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;

        config.Filters.AddRange(new List<IFilter>
        {
            new ForfrontAuthenticationAttribute(), // custom
            new RateLimitAttribute(), // custom
            new RequestAuditAttribute(), // custom
            new SuppressResponseCodeSuccessAttribute(), // custom
            new ExceptionHandlingAttribute() // custom
        });

        // http://weblogs.asp.net/imranbaloch/handling-http-404-error-in-asp-net-web-api
        config.Services.Replace(typeof(IHttpControllerSelector), new HttpNotFoundAwareDefaultHttpControllerSelector(config));
        config.Services.Replace(typeof(IHttpActionSelector), new HttpNotFoundAwareControllerActionSelector());

        config.EnsureInitialized();
    }
}

Действие контроллера, которое я хочу вызвать, определяется как:

[ApiVersion("1.0")]
[ODataRoutePrefix("MicrosoftDynamicsContactFieldMappings")]
[ControllerName("MicrosoftDynamicsContactFieldMappings")]
public class MicrosoftDynamicsContactFieldMappingsController : ForfrontODataController
{
    // DELETE: MicrosoftDynamicsContactFieldMappings(5)
    /// <summary>
    /// We don't really delete records, but update, user doesn't need to know internal workings.
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    [AcceptVerbs("DELETE")]
    public IHttpActionResult Delete([FromODataUri] int key)
    {
        // this is not being called
    }
}

Запрос REST API в fiddler имеет следующий формат:

DELETE http://dev2.e-shot.local/MicrosoftDynamicsContactFieldMappings(11) 
HTTP/1.1
Host: dev2.e-shot.local
User-Agent: Fiddler
Authorization: Token [token value goes here]
Accept-Language: en-GB

Когда сделан запрос на УДАЛЕНИЕ (PATCH или PUT), возвращается 404. Похоже, маршрутизация OData не рассматривается.

Я надеялся, что не придется отлаживать сборки OData, любая помощь очень ценится.

Спасибо Рик

1 ответ

ОБНОВЛЕНИЕ: мне удалось заставить глаголы DELETE, PATCH и PUT работать, используя маршрутизацию атрибутов и избегая соглашения о маршрутизации OData.

[AcceptVerbs("DELETE")]
[Route("MicrosoftDynamicsContactFieldMappings({key})")]
public IHttpActionResult Delete([FromUri] int key)
{
}

[AcceptVerbs("PATCH", "MERGE")]
[Route("MicrosoftDynamicsContactFieldMappings({key})")]
public IHttpActionResult Patch([FromODataUri] int key, 
       Delta<MicrosoftDynamicsContactFieldMapping> item)
{
}

В моем случае

Имя таблицы было: Пользователи

Поле первичного ключа: IDUser

Мне пришлось переименовать имя столбца IDUser в UserId в модели EF и запустить обновление базы данных.

после выполнения этого кода ниже работал:

    [Route("({key})")]
    [HttpPatch]
    public IActionResult Patch([FromODataUri]int key,
        Delta<mUSER> userPatch)
    {}
Другие вопросы по тегам