Построить шаблон маршрута на основе типа контроллера и имени Acton - PullRequest
0 голосов
/ 15 февраля 2019

Отказ от ответственности : Прежде всего, я хочу отметить, что я искал ответ по всему Интернету, прочитал всю документацию, прочитал все вопросы, которые я мог найти здесь, но пока не повезло.

Итак, вот мой случай.Я создаю API с использованием ASP.NET Core 2.2 и использую HATEOAS (спецификация HAL и библиотека Halcyon ).Я должен предоставить ссылки вместе с самим ресурсом.Это то, что привело меня к HATEOAS.Некоторые из ссылок являются шаблонными, поскольку это может быть метод PUT, а id должен быть задан внешним интерфейсом.

Проблема в том, что мои контроллеры могут иметь очень разные маршруты (используя атрибуты на основе атрибутов).маршрутизация) и жесткое кодирование ссылок - это плохо, потому что, если маршрут меняется, мне нужно помнить, чтобы изменить ссылку, где он также используется.По этой причине я решил создать ссылку на основе типа контроллера и имени действия.LinkGenerator - это то, что я нашел, но, похоже, он возвращает ноль, если я не укажу все параметры для маршрута.Вот пример кода:

[Route("api/metadata")]
[ApiController]
public class MetadataController : ControllerBase
{
    private readonly IMetadataProvider _metadataProvider;
    private readonly LinkGenerator _linkGenerator;

    public MetadataController(
        IMetadataProvider metadataProvider,
        LinkGenerator linkProvider)
    {
        _metadataProvider = metadataProvider;
        _linkGenerator = linkProvider;
    }

    [HttpGet]
    public IActionResult GetMetadata()
    {
        var metadata = _metadataProvider.GetMetadata();
        // here url will be 'null', because last parameter is null
        // and route requires parameter 'name' to be specified instead of 'null'
        // EXPECTED: "api/metadata/{name}"
        // ACTUAL: null
        string url = _linkGenerator.GetPathByAction(
            nameof(MetadataController.GetByName),
            nameof(MetadataController).Replace(nameof(Controller), string.Empty),
            null);

        var response = new HALResponse(metadata)
            .AddSelfLink(HttpContext.Request)
            .AddLinks(new Link(name, url));

        return Ok(response);
    }

    [HttpGet("{name}")]
    public IActionResult GetByName(string name)
    {
        var metadata = _metadataProvider.GetMetadataForEntity(name);
        return Ok(metadata);
    }
}

Как мне сгенерировать ссылку, чтобы она не была жестко запрограммирована и имела шаблон?

1 Ответ

0 голосов
/ 18 февраля 2019

После нескольких часов отладки исходного кода ASP.NET я думаю, что нашел способ сделать это.

Кажется, что LinkGenerator предназначен для создания полного и действительного URL,поэтому все параметры обязательны.Что я искал, так это на самом деле шаблон маршрута .

Во время отладки я обнаружил сервис IEndpointAddressScheme<RouteValuesAddress>, внедренный в LinkGnerator.Используется для фактического поиска маршрута .После этого LinkGenerator пытается заполнить все параметры.

Вот этот код исправлен и работает:

[ApiController]
public class MetadataController : ControllerBase
{
    private readonly IMetadataProvider _metadataProvider;
    private readonly IEndpointAddressScheme<RouteValuesAddress> _endpointAddress;

    public MetadataController(
        IMetadataProvider metadataProvider,
        IEndpointAddressScheme<RouteValuesAddress> endpointAddress)
    {
        _metadataProvider = metadataProvider;
        _endpointAddress = endpointAddress;
    }

    [HttpGet]
    public IActionResult GetMetadata()
    {
        var metadata = _metadataProvider.GetMetadata();
        // EXPECTED: "api/metadata/{name}"
        // ACTUAL: "api/metadata/{name}"
        string actionName = nameof(MetadataController.GetById);
        string controllerName = nameof(MetadataController).Replace(nameof(Controller), string.Empty);
        var url = _endpointAddress.FindEndpoints(CreateAddress(actionName, controllerName))
            .OfType<RouteEndpoint>()
            .Select(x => x.RoutePattern)
            .FirstOrDefault();;

        var response = new HALResponse(metadata)
            .AddSelfLink(HttpContext.Request)
            .AddLinks(new Link(name, url));

        return Ok(response);
    }

    [HttpGet("{name}")]
    public IActionResult GetByName(string name)
    {
        var metadata = _metadataProvider.GetMetadataForEntity(name);
        return Ok(metadata);
    }

    private static RouteValuesAddress CreateAddress(string action, string controller)
    {
        var explicitValues = new RouteValueDictionary(null);
        var ambientValues = GetAmbientValues(httpContext);

        explicitValues ["action"] = action;
        explicitValues ["controller"] = controller;

        return new RouteValuesAddress()
        {
            AmbientValues = ambientValues,
            ExplicitValues = explicitValues
        };
    }
}
...