ASP. NET WebApi2 OData обработка запросов с sla sh / - PullRequest
1 голос
/ 28 мая 2020

Я сделал "стандартный" проект OData Web Api 2 с маршрутизацией по соглашению. Следующие запросы OData работают:

/odata/Users

/odata/Users(123)

/odata/$metadata

/odata/Users?$select=Username

Итак, все казалось, все в порядке, пока я не попробовал это, что, я думаю, также является законным запросом OData:

/odata/Users(123)/Username

Sla sh / в запросе все ломается, и он не попадает в контроллер class и поток аутентификации OData вообще. Должно ли это вообще поддерживаться в реализации OData Microsoft ASP. NET? Или это поддерживается только в том случае, если я определяю явные методы с правильными маршрутами для каждого отдельного свойства, такого как имя пользователя? Есть предложения, как это исправить? Я пробовал явные маршруты {* rest} et c.

1 Ответ

1 голос
/ 01 июня 2020

AFAIK, встроенные соглашения о маршрутизации не включают соглашение о доступе к свойствам. Вам потребуется добавить много действий для каждого доступа к свойству.

Однако, основываясь на этом ресурсе здесь , не так уж сложно добавить пользовательское соглашение о маршрутизации для обработки доступа к свойству шаблон пути: ~/entityset/key/property

Вот соглашение о пользовательской маршрутизации, адаптированное из ссылки, которую я поделился выше

Используемая сборка: Microsoft.As pNet .OData 7.4.1 - подход будет таким же для любой другой библиотеки OData Web API, которую вы можете использовать

Класс, используемый для иллюстрации

public class Product
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
}

Добавить соглашение о маршрутизации для доступа к свойствам

// Usings
using Microsoft.AspNet.OData.Routing;
using Microsoft.AspNet.OData.Routing.Conventions;
using System;
using System.Linq;
using System.Web.Http.Controllers;
// ...

public class CustomPropertyRoutingConvention : NavigationSourceRoutingConvention
{
    private const string ActionName = "GetProperty";

    public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
    {
        if (odataPath == null || controllerContext == null || actionMap == null)
        {
            return null;
        }

        if (odataPath.PathTemplate == "~/entityset/key/property" ||
            odataPath.PathTemplate == "~/entityset/key/cast/property" ||
            odataPath.PathTemplate == "~/singleton/property" ||
            odataPath.PathTemplate == "~/singleton/cast/property")
        {
            var segment = odataPath.Segments.OfType<Microsoft.OData.UriParser.PropertySegment>().LastOrDefault();

            if (segment != null)
            {
                string actionName = FindMatchingAction(actionMap, ActionName);

                if (actionName != null)
                {
                    if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
                    {
                        var keySegment = odataPath.Segments.OfType<Microsoft.OData.UriParser.KeySegment>().FirstOrDefault();
                        if (keySegment == null || !keySegment.Keys.Any())
                            throw new InvalidOperationException("This link does not contain a key.");

                        controllerContext.RouteData.Values[ODataRouteConstants.Key] = keySegment.Keys.First().Value;
                    }

                    controllerContext.RouteData.Values["propertyName"] = segment.Property.Name;

                    return actionName;
                }
            }
        }

        return null;
    }

    public static string FindMatchingAction(ILookup<string, HttpActionDescriptor> actionMap, params string[] targetActionNames)
    {
        foreach (string targetActionName in targetActionNames)
        {
            if (actionMap.Contains(targetActionName))
            {
                return targetActionName;
            }
        }

        return null;
    }
}

Добавьте единственный метод в свой контроллер для обработки запроса любого свойства

public class ProductsController : ODataController
{
    // ...
    [HttpGet]
    public IHttpActionResult GetProperty(int key, string propertyName)
    {
        var product = _db.Products.FirstOrDefault(d => d.Id.Equals(key));
        if (product == null)
        {
            return NotFound();
        }

        PropertyInfo info = typeof(Product).GetProperty(propertyName);

        object value = info.GetValue(product);

        return Ok(value, value.GetType());
    }

    private IHttpActionResult Ok(object content, Type type)
    {
        var resultType = typeof(OkNegotiatedContentResult<>).MakeGenericType(type);
        return Activator.CreateInstance(resultType, content, this) as IHttpActionResult;
    }
    // ...
}

В вашем WebApiConfig.cs (или аналогичном месте, где вы настраиваете службу)

var modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Product>("Products");

var routingConventions = ODataRoutingConventions.CreateDefaultWithAttributeRouting("odata", configuration);
routingConventions.Insert(0, new CustomPropertyRoutingConvention());

configuration.MapODataServiceRoute("odata", "odata", modelBuilder.GetEdmModel(), new DefaultODataPathHandler(), routingConventions);
configuration.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
configuration.EnsureInitialized();

Запрос на свойство Name: /Products(1)/Name

Запрос на свойство Id: /Products(1)/Id

...