【问题标题】:Route attribute Name propertyRoute 属性 Name 属性
【发布时间】:2018-07-17 17:36:42
【问题描述】:

我有 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 不能返回 null


我的RouteConfig

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

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

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

任何其他方法都不起作用

【问题讨论】:

  • 错误出现在哪一行?你能提供一个堆栈跟踪吗?

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


【解决方案1】:

据我了解,CreateAtRoute 方法必须得到另一个问题 路线名称。如您所见,我可以通过添加路由名称来解决问题 参数(现在注释)并将“DefaultApi”替换为“CreateOwner” 但它看起来像一个黑客。我相信还有另一种方法可以做到这一点 避免名称属性。

您的理解几乎是正确的。但是,您不应为当前路由指定名称,而应为指向已创建资源的路由指定名称。 CreatedAtRoute 填充响应 Location 标头,该标头应包含新创建资源的 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 action,否则多个action会匹配你的GET请求。)

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

【讨论】:

  • 感谢您的贡献!阅读您的答案,我找到了解决方案。仍然是另一个答案,由于缺乏信息,您无法将其提供给我:)
【解决方案2】:

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

  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从未使用过,但我真正的实现更困难。

【讨论】:

    猜你喜欢
    • 2014-12-25
    • 2017-03-30
    • 1970-01-01
    • 2010-10-02
    • 1970-01-01
    • 2014-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多