5

我想使用 ASP.NET Web Api 实现以下休息模式:

http://mydomain/api/students
http://mydomain/api/students/s123
http://mydomain/api/students/s123/classes
http://mydomain/api/students/s123/classes/c456

我使用 ApiController 和以下两种方法使前两个链接正常工作:

public class StudentsController : ApiController {
  // GET api/students
  public IEnumerable<Student> GetStudents() {  
  }

  // GET api/students/5
  public IEnumerable<Student> GetStudent(string id) {  
  }
}

在同一个控制器中,(或者我是否需要一个名为 ClassesController 的不同控制器?),我将如何实现最后两个链接?此外,“类”部分的路由会是什么样子(如果需要)?

这是我的 WebApiConfig (我想保持动态,而不是尽可能硬编码到 /classes 的路由:

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

// EDIT - I'm getting 404's when trying to use this
context.Routes.MapHttpRoute(
  name: "JobsApi",
  routeTemplate: this.AreaName + "/Students/{id}/Classes/{classId}",
  defaults: new { classId = RouteParameter.Optional }
);   

编辑这是我新创建的 ClassesController:

public class ClassesController : ApiController {
  // GET api/classes
  public IEnumerable<TheClass> Get(string id) {    
      return null;
  }
}

尝试访问此 URL 时出现 404 错误:

http://mydomain/api/students/s123/classes
4

2 回答 2

7

ASP.NET 中的路由可以表达这些更复杂的规则,但需要明确设置。例如,在这种情况下,您必须定义 2 条路线:

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

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

你会有一个控制器:

public class ClassesController
{
   public TheClass Get(int studentId, int classId)
   {
       ....
   }
}

这可能并不理想,但却是主要选择。

我正在研究分层路由,由于 Web API 中的实现问题,这是不可能的,但这个问题现在已经修复,所以我可能会再次开始研究它。

于 2013-02-03T17:47:31.073 回答
7

使用这种漂亮的分层方法,您会更加关注内部路由。有一个很好的示例应用程序采用分层资源结构:PingYourPackage。检查出。

注意:我有一篇关于这个问题的博客文章,解释了以下问题,并为那些有一些代码示例的人提供了解决方案。您可以查看它以获取更多详细信息:

让我通过设置一个示例场景来简要解释这里的问题。对于此类情况,这可能不是理想的方法,但可以很好地解决问题。假设您的数据库中有以下两家货运公司的附属公司:

  • 附属公司 1(ID:100)
  • 附属公司 2(编号:101)

然后假设这些附属公司附有一些货物:

  • 附属公司 1(键:100)
    • 装运1(关键:100)
    • 装运 2(密钥:102)
    • 装运4(密钥:104)
  • 附属公司 2(键:101)
    • 装运 3(密钥:103)
    • 装运 5(密钥:105)

最后,我们希望有以下资源结构:

GET api/affiliates/{key}/shipments
GET api/affiliates/{key}/shipments/{shipmentKey}
POST api/affiliates/{key}/shipments
PUT api/affiliates/{key}/shipments/{shipmentKey}
DELETE api/affiliates/{key}/shipments/{shipmentKey}

路由问题

@Ali 已经解释过了,但我在这里有不同的方法。假设我们正在发送一个 GET 请求/api/affiliates/105/shipments/102。请注意,此处的附属密钥是 105,它不存在。因此,我们希望尽快在此处终止请求。我们可以通过每个路由消息处理程序来实现这一点。

授权问题

如果你有某种类型的身份验证,你会想要确保(在我们这里的场景中)经过身份验证的用户和请求的附属资源是相关的。例如,假设Affiliate1在Affiliate 角色下进行了身份验证,并且您已AuthorizeAttribute注册以检查“Affiliate”角色授权。在这种情况下,您将惨遭失败,因为这意味着Affiliate1可以访问以下资源:/api/affiliates/101/shipments属于 Affiliate2。我们可以通过自定义来消除这个问题AuthorizeAttribute

所有权问题

现在,以下 URI 应该为我提供正确的数据:

获取 /api/affiliates/100/shipments/102

但是,以下 URI 会发生什么:

获取 /api/affiliates/100/shipments/103

这应该会得到“404 Not Found”HTTP 响应,因为ID 为100的会员不拥有ID 为103的货件

于 2013-02-04T10:29:11.923 回答