属性路由和查询字符串

时间:2014-04-14 20:17:45

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

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

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

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

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

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

以下是有问题的代码,包括控制器,基础api控制器和使HATEOAS实现成为可能的模型工厂。

我遇到的3个问题是:

1)如何让控制器接受" id"在查询字符串和网址格式(... / deliverrables / 1和... / deliverrables?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 }
        );

1 个答案:

答案 0 :(得分:1)

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

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