Можно ли задать имя параметра запроса в контроллере на основе атрибутов класса?

#c# #asp.net-core #.net-core #asp.net-core-webapi

#c# #asp.net-core #.net-core #asp.net-core-webapi

Вопрос:

Я создаю контроллер, который использует дженерики. Этот родительский контроллер наследуется другими дочерними контроллерами общим способом путем передачи типа класса, с которым они имеют дело. Для действий в родительском контроллере требуется параметр, связанный с параметром запроса, называемым PartitionKey (Cosmos). Теперь каждый параметр типа, используемый с универсальным контроллером, имеет другое свойство для PartitionKey (хотя все они являются идентификаторами Guid, имена меняются). Проблема в том, что когда пользователь API видит «PartitionKey» в качестве описания параметра запроса, он не имеет представления, какое свойство из всех свойств Guid класса является ключом PartitionKey.

Я подумал, что, возможно, может быть, есть способ динамически задавать имя параметра запроса на основе атрибута, установленного в классе свойств, который соответствует PartitionKey . Можно ли установить имя параметра запроса в атрибутах свойств класса на основе контроллера? Или есть лучший способ сделать это?

Родительский контроллер

 using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

namespace x.API.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class GenericController<T> : ControllerBase
    {
        [HttpGet("{id}")]
        public async Task<ActionResult<T>> Get(Guid id, Guid partitionKey)
        {
                var item = await GetItemAsync<T>(id, partitionKey);

                return Ok(item);
        }
 

Дочерний контроллер

 using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;


namespace x.API.Controllers
{
    public class GameController : GenericController<Game>
    {
    }
}
 

Ответ №1:

В какой-то момент вы должны указать имя параметра запроса, в этом нет сомнений. Но действительно ли стоит делать его динамическим? Для меня это звучит как перебор, когда есть еще другие доступные решения, достижимые с небольшими усилиями.

Я сказал решения, потому что есть не только один способ сделать это. Но способ, которым я бы это сделал, — сделать Get() метод GenericController<T> защищенным и удалить [HttpGet] атрибут.

 [ApiController]
[Route("[controller]")]
public class GenericController<T> : ControllerBase
{
    private readonly IEntityService _entityService;

    public GenericController(IEntityService entityService)
    {
        _entityService = entityService;
    }

    protected async Task<ActionResult<T>> GetEntity(Guid id, Guid partitionKey)
    {
        try
        {
            var item = await _entityService.GetItemAsync<T>(id, partitionKey);

            return Ok(item);
        }
        catch (EntityNotFoundException ex)
        {
            return NotFound(ex.Message);
        }
    }
}
 

Контроллеры, производные от GenericController (здесь используются Foo в качестве примера), будут вызывать базовый Get() метод. Каждый производный контроллер имеет полный контроль над путем метода и его именами параметров запроса.

 public class FooController : GenericController<Foo>
{
    public FooController(IEntityService entityService) : base(entityService)
    {

    }

    [HttpGet("{id}")]
    public async Task<ActionResult<Foo>> Get(Guid id, Guid fooKey)
    {
        return await GetEntity(id, fooKey);
    }
}
 

Пример Foo объекта:

 public class Foo
{
    public Guid FooKey { get; set; }
}
 

Комментарии:

1. Да, но, поступая так, как вы говорите, вы теряете удобство простого наследования, вы создаете действия в каждом контроллере. При текущем подходе мне просто нужно объявить наследование и все. Однако конечная точка имеет загадочный параметр PartitionKey, и это то, что я хочу решить. Я думал о добавлении атрибута к свойству в классе, который является PartitionKey, а затем, путем отражения, получить имя свойства и использовать его для пути или параметра, но я не уверен, возможно ли это.