我正在寻找针对特定其他Web服务用例的端点设计的建议。
我们有一个基本API可以将特定用户注册到特定课程中:
PUT /rest/courses/{courseId}/enroll/{studentId}
这很好用,而且很简单。
但是,我们通常有N个用户,我们想要注册到同一个课程,其中N可能是1到30.我看到两个可能的选项来完成这个批量注册:
实施单独的端点,如下所示:
PUT /rest/courses/{courseId}/enroll
...并将N个学生ID放在请求正文中。
选项1似乎效率不高。选项2闻起来有点不合适(对于PUT来说似乎不对,但对于POST来说也是如此)。这些选项中的任何一个对你来说都显得更好(更“安静”)吗?或者是否有明确的第三种,更好的选择?
谢谢!
答案 0 :(得分:3)
使用PUT /rest/courses/{courseId}/enroll/{studentId}
- 正如你所说的那样,它是直截了当的并且可以正常工作,但鉴于REST是REpresentational State Transfer,总是问自己REST的一个问题是 - 表示的资源状态是什么在那个URI?
e.g。如果客户端要调用GET /rest/courses/{courseId}/enroll/{studentId}
,将返回什么资源表示?它只是学生,还是他们在该课程中注册的一些细节? (例如包括报名日期)?使用动词' enroll'在资源上调用GET是否有意义?在里面?同一个学生可以多次报名参加同一课程(即如果他们在以后的学期重读课程吗?)
另一种可能性是:
POST /rest/courses/{courseId}/enrollments
Body:
{
"studentId" : xyz
}
这将返回状态代码201 Created,并导致在某个位置创建资源,并将Location标头设置为该URI,例如
/rest/enrollments/{enrollmentId}
如果您使用相同的StudentId执行了另一个POST,您的业务逻辑将检查是否允许这样做,例如给定注册的学期,如果请求无效,则返回400,如果有效,则返回201 Created with 2nd Enrollment。
此惯例还允许您定义:
GET /rest/courses/{courseId}/enrollments
可以返回该课程中所有注册的列表,
GET /rest/enrollments
可以返回所有课程中所有注册的列表(如果有帮助的话)。
要回到原来的问题,如果您的URI应该是一个'集合'资源,从技术上讲,PUT应该取代整个集合 - 因为它应该在语义上意味着客户端正在说"这是我希望存储在这个资源URI中的表示" - 正如你猜测的那样,它没有正确的气味。
POST /rest/courses/{courseId}/enrollments
BODY
{
students : [
{
studentId : 100
},
{
studentId : 101
}
]
}
然而,允许这样做会引入一些复杂性:
您的问题提到您认为即使使用POST,将多个学生提交到.../enroll
端点似乎也无法恢复。我认为这部分是因为enroll
(和enrollment
)声音就像他们只与一个学生有关。另一种方法是定义新资源 - bulkEnrollment
。例如而
POST /rest/courses/{courseId}/enrollments
意味着'使用POST数据创建注册,并且只接受一个学生,一个新资源:
POST /rest/courses/{courseId}/bulkEnrollments
意味着使用POST数据创建批量注册'并接受多个学生ID。批量注册本身成为您跟踪的实体,然后可以在以下位置检索其资源表示:
GET /rest/bulkEnrollments/{bulkEnrollmentId}
该表示将列出该特定批量操作创建的所有注册,包括可能有关批量注册本身的一些元数据,包括执行该注册的经过身份验证的用户以及何时执行。
无论您是否公开bulkEnrollment
资源,也可能值得考虑将超媒体引入您的表示 - 例如注册集合中的每个条目都可以包含指向个人注册的资源的超媒体链接(例如/rest/enrollments/{enrollmentId}
,有时称为self
链接),每个注册可以包含指向该注册的超媒体链接。表示学生的资源(例如/rest/students/{studentId}
)。
事实上,超媒体(例如Hal)是您可以采用的最RESTful技术 - 远比您在端点中使用的特定动词/名词或结构重要。通过仅让客户端通过跟踪链接之前的请求与其发现的端点进行交互,您的API可以自我描述资源之间的关系。它还为实际URI端点结构随时间变化提供了很大的灵活性。但最重要的是,它允许您在给定资源的当前状态的情况下向客户端发出关于实际允许哪些操作的信号。这个概念被称为HATEOAS(该示例使用Xml,但Hal可以帮助Json中使用相同的概念),并且对于“3”级别来说是必需的。 REST API根据REST API的Richardson Maturity Model。
答案 1 :(得分:0)
PATCH
(建议的RFC 6902标准)作为一种替代选择,可以看起来像这样:
{
"op" : "add",
"path" : "/rest/courses/{courseId}/students",
"value" : ["studentId1", "studentId2"]
}