路由属性名称 属性

Route attribute Name property

我有 ProductsController 和 OwnersController:

public class ProductsController : ApiController
{

    //constructor is here

    // GET /api/products
    public IHttpActionResult GetProducts()
    {
        return Ok(new ApiResponse());
    }

    // GET /api/products/{productCode}
    [HttpGet, Route("api/products/{productCode}")]
    public IHttpActionResult GetProductByCode(string productCode)
    {
         return Ok(new ApiResponse());
    }

    // POST /api/products
    public IHttpActionResult PostProduct(Product product /*my class*/)
    {
        return CreatedAtRoute("DefaultApi", new { id = product.Id }, product);
    }
}

效果很好。 但是现在我创建了第二个控制器并做同样的事情但是 我在尝试 POST 方法时得到了错误。另一种方法效果很好!

先来看看代码:

public class OwnersController : ApiController
{
    // constructor

    // GET /api/owners/{label}
    // GET /api/owners/{label}?skip=1&take=4
    [Route("api/owners/{label}")]
    public IHttpActionResult GetOwnersExamples(string label, int skip=0, int take=10)
    {
        return Ok(new ApiResponse());
    }
    // POST /api/owners/{productCode}
    //[HttpPost]
    [Route("api/owners/{productCode}"/*, Name = "CreateOwner"*/)]
    public IHttpActionResult PostOwner(string productCode, Owner owner)
    {
        return CreatedAtRoute("DefaultApi", new { id = Owner.Id }, owner);
    }  
}

错误信息:

UrlHelper.Link must not return null


我的路由配置:

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

据我了解 CreateAtRoute 方法必须得到另一个 RouteName 的问题。如您所见,我可以通过添加 Route Name 参数(现在已评论)并将 "DefaultApi" 替换为 "CreateOwner" 来解决问题,但它看起来像是一个 hack。我相信还有另一种方法可以避免 Name Property.

P.S。看起来我的 Web API 只能看到第一个控制器 (ProductsController) - 如果我删除显式 Route [Route("...")]...

,任何其他方法都不起作用

As I understand problem that CreateAtRoute method must get another RouteName. As you see I can solve the problem by adding Route Name parameter (commented now) and replace "DefaultApi" with "CreateOwner" but it looks like a hack. I believe there is another method to do it avoiding Name Property.

你的理解基本正确。但是,您不应为当前路由指定名称,而应为指向已创建资源的路由指定名称。 CreatedAtRoute 填充响应 Location header 应包含新创建资源的 GET-able URI。

这是一个工作示例:

[HttpGet]
[Route("api/owners/{id}", Name = "GetOwnerById")]
public IHttpActionResult GetOwner(int id)
{
    //  Obtain the owner by id here
    return Ok(new ApiResponse());
}

[HttpPost]
[Route("api/owners/{productCode}"/*, Name = "CreateOwner"*/)]
public IHttpActionResult PostOwner(string productCode, Owner owner)
{
    return CreatedAtRoute("GetOwnerById", new { id = owner.Id }, owner);
}

(注意:要使此示例正常运行,您应该注释 GetOwnersExamples 操作,否则多个操作将匹配您的 GET 请求。)

你说它看起来像黑客,但事实并非如此。 CreatedAtRoute 将路由名称作为参数,您应该提供它。否则将如何选择正确的操作并构建 Location header?

我使用后续步骤解决问题:

  1. 删除控制器的所有 [RoutePrefix] - 让它们默认工作 - 它非常适合简单的请求。
  2. 重要提示:检查所有方法是否重复!问题是我有 2 个方法,路由 api/controller/{label}api/controller/{parameter} - 它无法理解默认使用它们中的哪一个。 (或使用显式 uri:api/controller?label=1
  3. 重要提示: 避免将许多复杂类型放入 api 方法中 - 为它们创建 Wrapper 并只放置一个参数!

所有这些操作让我删除了多余的属性并使方法更具可读性。

结果如下:

public IHttpActionResult PostOwner(OwnerWrapper modelWrapper)
{
    string productCode = modelWrapper.Product.Code;
    Owner owner = modelWrapper.Owners[0];

    return CreatedAtRoute("DefaultApi", new { id = Owner.Id }, owner);
}

这只是测试用例,所以我们可以看到productCode从来没有用过,但我的真正实现比较困难。