我想工作了构建我们的API的最佳方式; 我们评论,我们在一个标准的REST结构已经设置(列表中的一个所有,列表,创建,更新等)。 它并不完全适合的例子是:每次审查可以连接到一个或多个其他类型,例如事件,地点或事物。
我的想法是网址是沿着线:/事件/评论/(或此如/评论/事件/相反)/位置/评论/ /事/评论/
不过,我可以看到的问题是“GET”对于这些应该返回父对象,即事件。
因此,使用ServiceStack,什么是处理这种情况的最好方法是什么? 它是创建每个数据请求的定制服务,而不是滥用外的现成的REST设置或我错过了一些更根本的?
首先“最佳”的解决方案是一个相当主观的术语。 我一般会瞄准干燥,促进最少的努力,摩擦和繁琐重复使用的,高性能的解决方案,而其他人可能在它遵循REST的原则如何密切定义“最佳”。 所以,你会得到不同的反应取决于目标是什么。 我只能提供我会怎么处理它。
从他们的定制路由ServiceStack服务实现的去耦
有一点要记住的是你如何定义和ServiceStack设计你的服务是相当去耦你如何揭露他们,因为你可以在任何定制的路由下暴露你的服务。 ServiceStack鼓励基于消息的设计,所以你应该给每个操作一个截然不同的消息。
使用逻辑/分层网址结构
我会使用一个逻辑地址的结构,我的目标是代表一个名词,这是分层结构化的识别符,即父路径分类的资源,并赋予它有意义的上下文。 因此,在这种情况下,如果你想揭露事件及评论我的倾向是去以下网址结构:
/events //all events
/events/1 //event #1
/events/1/reviews //event #1 reviews
每个资源标识符可以有任何HTTP动词应用到他们
履行
对于实现我一般遵循基于消息的设计和组基于响应类型的所有相关业务和呼叫上下文。 为此,我会做这样的事情:
[Route("/events", "GET")]
[Route("/events/category/{Category}", "GET")] //*Optional top-level views
public class SearchEvents : IReturn<SearchEventsResponse>
{
//Optional resultset filters, e.g. ?Category=Tech&Query=servicestack
public string Category { get; set; }
public string Query { get; set; }
}
[Route("/events", "POST")]
public class CreateEvent : IReturn<Event>
{
public string Name { get; set; }
public DateTime StartDate { get; set; }
}
[Route("/events/{Id}", "GET")]
[Route("/events/code/{EventCode}", "GET")] //*Optional
public class GetEvent : IReturn<Event>
{
public int Id { get; set; }
public string EventCode { get; set; } //Alternative way to fetch an Event
}
[Route("/events/{Id}", "PUT")]
public class UpdateEvent : IReturn<Event>
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
}
并按照事件的评论类似的模式
[Route("/events/{EventId}/reviews", "GET")]
public class GetEventReviews : IReturn<GetEventReviewsResponse>
{
public int EventId { get; set; }
}
[Route("/events/{EventId}/reviews/{Id}", "GET")]
public class GetEventReview : IReturn<EventReview>
{
public int EventId { get; set; }
public int Id { get; set; }
}
[Route("/events/{EventId}/reviews", "POST")]
public class CreateEventReview : IReturn<EventReview>
{
public int EventId { get; set; }
public string Comments { get; set; }
}
实施应该是相当直截了当根据这些消息,(取决于代码库大小),我会在2 EventsService和EventReviewsService类组织。 我要指出,我用多元化的服务请求DTO自己的名字,以避免与同名的数据模型冲突。
虽然我已经分离UpdateEvent
和CreateEvent
在这里,我有时会将它们合并成一个单一的幂等StoreEvent
如果用例允许操作。
体育项目结构
理想情况下,根级APPHOST项目应保持轻量和自由实现的。 虽然只有少数服务小项目它的确定对什么事都可以在一个单一的项目,并在需要时简单地增加你的架构。
对于中型到大型项目,我们建议的物理结构,低于这个例子的目的,我们将假设我们的应用被称为EventMan。
这些项目的顺序也显示它的依赖,如顶级EventMan
项目引用的所有子项目,而最后EventMan.ServiceModel
项目引用无 :
- EventMan
AppHost.cs // ServiceStack ASP.NET Web or Console Host Project
- EventMan.ServiceInterface // Service implementations (akin to MVC Controllers)
EventsService.cs
EventsReviewsService.cs
- EventMan.Logic //For larger projs: pure C# logic, data models, etc
IGoogleCalendarGateway //E.g of a external dependency this project could use
- EventMan.ServiceModel //Service Request/Response DTOs and DTO types
Events.cs //SearchEvents, CreateEvent, GetEvent DTOs
EventReviews.cs //GetEventReviews, CreateEventReview
Types/
Event.cs //Event type
EventReview.cs //EventReview type
随着EventMan.ServiceModel
DTO的保持自己独立的执行和无依赖的DLL,你能够自由地分享这个DLL中的任何.NET客户端项目作为-是-你可以使用任何通用的使用C#服务客户端 ,以提供的端至端输入API而没有任何代码生成。
更新
不知道它会在你的场景/有助于理解,但我觉得这个演讲有帮助:
设计一个美丽的REST + JSON API