Маршрутизация и наследование атрибутов .NET WebAPI


Я играю с идеей иметь базовый контроллер, который использует общий репозиторий для предоставления основных методов CRUD для моих контроллеров API, чтобы мне не приходилось дублировать один и тот же базовый код в каждом новом контроллере. Но у меня возникают проблемы с распознаванием атрибута маршрутизации, когда он находится в базовом контроллере. Чтобы показать, какая именно проблема у меня возникла, я создал очень простой контроллер WebAPI.

когда у меня есть метод Get в основном Контроллер и он наследует от ApiController напрямую у меня нет никаких проблем и это работает, как ожидалось.

[RoutePrefix("admin/test")]
public class TestController : ApiController
{
    [Route("{id:int:min(1)}")]
    public string Get(int id)
    {
        return "Success";
    }
}

когда я перемещаю метод Get в базовый контроллер, он возвращает содержимое страницы 404.

[RoutePrefix("admin/test")]
public class TestController : TestBaseController
{

}

public class TestBaseController : ApiController
{
    [Route("{id:int:min(1)}")]
    public string Get(int id)
    {
        return "Success";
    }
}

еще несколько интересных заметок:

  • Я могу получить доступ к действию в GET/Test / 1. Таким образом, он находит его на основе маршрута по умолчанию все еще.

  • когда я пытаюсь получить доступ к POST / admin / test it возвращает следующий JSON

    { "Сообщение": "не найден HTTP-ресурс, соответствующий URI запроса'http://test.com/admin/test'.", "MessageDetail": "не найден тип, который соответствует контроллеру с именем 'admin'." }

кто-нибудь знает способ заставить маршрутизацию работать с атрибутами от базового контроллера?

3 56

3 ответа:

маршруты атрибутов не могут быть унаследованы. Это было обдуманное дизайнерское решение. Мы не чувствовали себя правильно и не видели действительных сценариев, где было бы целесообразно их унаследовать.

не могли бы вы дать более реалистичный сценарий, где вы хотите использовать это?

[обновление(24.03.2014)]
В предстоящем выпуске 5.2 веб-API MVC будет точка расширения под названием System.Web.Http.Routing.IDirectRouteProvider С помощью которого можно включить наследование сценарий, который вы ищете здесь. Вы можете попробовать это самостоятельно, используя последние ночные сборки(документация о том, как использовать ночные сборки здесь)

[обновление(31.07.2014)]
Пример того, как это можно сделать в Web API 2.2 версия:

config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());

//---------

public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override IReadOnlyList<IDirectRouteFactory> 
    GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
    {
        // inherit route attributes decorated on base class controller's actions
        return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
        (inherit: true);
    }
}

используя Web API 2.2, вы можете:

public class BaseController : ApiController
{
    [Route("{id:int}")]
    public string Get(int id)
    {
        return "Success:" + id;
    }
}
[RoutePrefix("api/values")]
public class ValuesController : BaseController
{
}

config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
    protected override IReadOnlyList<IDirectRouteFactory> 
    GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
    {
        return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>
        (inherit: true);
    }
}

как указано здесь:http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22

получил его.

[Route("api/baseuploader/{action}")]
public abstract class BaseUploaderController : ApiController
{
    [HttpGet] 
    public string UploadFile() 
    {
        return "UploadFile";
    }
}


[Route("api/values/{action}")]
public class ValuesController : BaseUploaderController
{
    [HttpGet]
    public string Get(int id)
    {
        return "value";
    }
}

одно предостережение здесь заключается в том, что параметр действия маршрута должен совпадать с именем действия. Я не мог найти способ обойти это. (Вы не можете переименовать маршрут с помощью атрибута RouteAttribute)