【问题标题】:Attribute Routing and Query Strings属性路由和查询字符串
【发布时间】:2014-05-28 23:18:07
【问题描述】:

我是来自 WCF 背景的 Web api 新手,在准备之前,我观看了 Shawn Wildermuth 关于该主题的 Pluralsight 课程。他的课程材料是围绕更传统的路由设计的。本课程深入探讨的主题之一是 HATEOAS,以及使用基本 api 控制器和模型工厂实现这一目标是多么容易。

在实现属性路由时,我遇到的第一件事就是需要 UrlHelper 将路由名称作为 Link() 方法的第一个参数,这是在 WebApiConfig 中配置的常规路由中继承的。码。

我通过使用 Name 属性装饰我的控制器路由属性之一来解决此问题,并且无论我使用哪种方法,该控制器中的所有方法似乎都可以访问 name 属性(请参见下面的代码)。虽然我觉得这有点奇怪,但它确实有效。现在我已经实现了 HATEOAS,我注意到它生成的 URL 是查询字符串格式,而不是“url”格式(我知道这个术语是错误的,但请耐心等待)。而不是 .../api/deliverables/1 我得到 .../api/deliverables?id=1。

这是“好的”,但不是所需的输出。虽然我还没有弄清楚如何调整 URL 返回值的格式,但我想我会针对我的控制器测试查询字符串,发现在查询字符串格式中我的控制器不起作用,但在“url " 格式化它。

然后我花了一个小时试图找出原因。我尝试了不同的装饰(即 [FromUri],从我的阅读来看,它应该只对默认为消息正文的复杂对象是必需的)来设置默认值、约束并使其成为可选的(即 {id?})。

下面是有问题的代码,用于控制器、基本 api 控制器和使 HATEOAS 实现成为可能的模型工厂。

我的三个问题是:

1) 如何让控制器接受查询字符串上的“id”和 url 格式(.../deliverables/1 和 .../deliverables?id=1。

2)如何让URL助手的Link方法返回url格式的值(目前是作为查询字符串返回。

3)在 WebAPI 2 中命名路由的正确方法。我在做什么(为单个方法分配一个名称,而其他方法似乎继承它只是闻起来很臭,我不得​​不相信这会崩溃,因为我实际上开始实现更多复杂的代码。Shawn 的实现在某些方面有缺陷吗?我喜欢不必为了测试/开发目的而对 URL 进行硬编码,但也许 UrlHelper 不是实现这一点的最佳方法。它似乎带来了很多可能没有的包袱有必要。

控制器:

[RoutePrefix("api/deliverables")]
public class DeliverablesController : BaseApiController
{
    private readonly IDeliverableService _deliverableService;
    private readonly IUnitOfWork _unitOfWork;

    public DeliverablesController(IDeliverableService deliverableService, IUnitOfWorkAsync unitOfWork)
    {
        _deliverableService = deliverableService;
        _unitOfWork = unitOfWork;
    }

    [Route("", Name = "Deliverables")]
    public IHttpActionResult Get()
    {
        return Ok(_deliverableService.Get().Select(TheModelFactory.Create));
    }

    [Route("{id}")]
    public IHttpActionResult Get(int id)
    {
        return Ok(TheModelFactory.Create(_deliverableService.Find(id)));
    }

    [Route("")]
    public IHttpActionResult Post([FromBody]DeliverableModel model)
    {
        try
        {
            var entity = TheModelFactory.Parse(model);

            if (entity == null)
            {
                return BadRequest("Could not parse Deliverable entry in body.");
            }

            _deliverableService.Insert(entity);
            _unitOfWork.SaveChanges();

            return Created(Request.RequestUri + "/" + entity.Id.ToString(CultureInfo.InvariantCulture),TheModelFactory.Create(entity));


        }
        catch (Exception exception)
        {
            return BadRequest(exception.Message);
        }
    }
}

基础 API 控制器:

public abstract class BaseApiController : ApiController
{
    private ModelFactory _modelFactory;

    protected ModelFactory TheModelFactory
    {
        get
        {
            return _modelFactory ?? (_modelFactory = new ModelFactory(Request));
        }
    }
}

模型工厂:

public class ModelFactory
{
    private readonly UrlHelper _urlHelper;

    public ModelFactory(HttpRequestMessage request)
    {
        _urlHelper = new UrlHelper(request);
    }

    public DeliverableModel Create(Deliverable deliverable)
    {
        return new DeliverableModel
               {
                   Url = _urlHelper.Link("deliverables", new { id = deliverable.Id }),
                   Description = deliverable.Description,
                   Name = deliverable.Name,
                   Id = deliverable.Id
               };
    }

    public Deliverable Parse(DeliverableModel model)
    {
        try
        {
            if (string.IsNullOrEmpty(model.Name))
               return null;

            var entity = new Deliverable
                         {
                             Name = model.Name,
                             Description = !string.IsNullOrEmpty(model.Description)
                                 ? model.Description
                                 : string.Empty
                         };

            return entity;
        }
        catch (Exception)
        {
            return null;
        }
    }
}

澄清一点,非属性(传统)路由对于 URI 和查询字符串格式都没有问题:

config.Routes.MapHttpRoute(
            name: "deliverables",
            routeTemplate: "api/deliverables/{id}",
            defaults: new { controller = "deliverables", id = RouteParameter.Optional }
        );

【问题讨论】:

    标签: c# asp.net-web-api asp.net-web-api-routing


    【解决方案1】:

    在我看来,这是属性路由的问题之一。这就是为什么我将它用于特殊情况。我对大多数路由使用路由表,然后在特殊情况下使用属性路由。

    要以您的方式解决此问题,您是否考虑过 Get(id) 上的多个路由? (我实际上并不认为这会起作用,但值得一试)。

    【讨论】:

    • 感谢您抽出宝贵时间回复,非常感谢。我尝试了多条路线,但仍然无法正常工作。根据您的建议,如果/当有必要时,我将返回路由表并再次处理属性路由。
    • 我认为应该反过来——只要 AttributeRouting 不能满足您的需求,就使用 RouteTables。
    猜你喜欢
    • 2018-10-24
    • 1970-01-01
    • 2015-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-12
    • 1970-01-01
    相关资源
    最近更新 更多