【发布时间】: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