简体   繁体   English

推荐的ServiceStack API结构

[英]Recommended ServiceStack API Structure

I'm trying work out the best way to structure our API; 我正在尝试找出构建API的最佳方法; we have Reviews which we've setup in a standard REST structure (list one, list all, create, update etc). 我们在标准REST结构中设置了评论(列表一,列出所有,创建,更新等)。 Where it doesn't quite fit the examples is: each review can be linked to one or more other types eg Event, Location or Thing. 在不完全符合示例的情况下:每个评论可以链接到一个或多个其他类型,例如事件,位置或事物。

My thinking is the urls would be along the lines of: /event/reviews/ (or the reverse of this eg /reviews/event/) /location/reviews/ /thing/reviews/ 我的想法是网址将是:/ event / reviews /(或相反的例如/ reviews / event /)/ location / reviews / / thing / reviews /

The issue I can see however is the "GET" for each of these should return the parent object ie an Event. 然而,我可以看到的问题是每个这样的“GET”应该返回父对象,即一个事件。

So using ServiceStack, what's the best way to handle this scenario? 那么使用ServiceStack,处理这种情况的最佳方法是什么? Is it to create a custom service for each data request rather than abusing the out-of-the-box REST setup or have I missed something more fundamental? 是为每个数据请求创建一个自定义服务而不是滥用开箱即用的REST设置还是我错过了一些更基本的东西?

Firstly "Best" solution is a fairly subjective term. 首先,“最佳”解决方案是一个相当主观的术语。 I'll generally aim for DRY, re-usable, performant solutions that promotes the least effort, friction and chattiness, whilst others may define "Best" in how closely it follows the principles of REST. 我通常的目标是干燥,可重复使用,高性能的解决方案,促进最小的努力,摩擦和chattiness,而其他人可能会定义“最佳”,它遵循REST的原则。 So you will get varied responses depending on what the goals are. 因此,根据目标的不同,您将获得不同的响应。 I can only offer how I would approach it. 我只能提供如何处理它。

ServiceStack service implementations are de-coupled from their custom routes ServiceStack服务实现从其自定义路由中解耦

One thing to keep in mind is how you define and design your services in ServiceStack are fairly de-coupled in how you expose them, since you can expose your services under any custom route. 需要记住的一件事是,您在ServiceStack中定义和设计服务的方式与您公开它们的方式完全脱离,因为您可以在任何自定义路径下公开您的服务。 ServiceStack encourages a message-based design so you should give each operation a distinct message. ServiceStack鼓励基于消息的设计,因此您应该为每个操作提供不同的消息。

Use a logical / hierarchical Url structure 使用逻辑/分层Url结构

I'd use a logical Url structure that I aim to represent the identifier of a noun, which is hierarchically structured, ie the parent path categorizes your resource and gives it meaningful context. 我使用逻辑Url结构,我的目标是表示名词的标识符,它是分层结构的,即父路径对您的资源进行分类并为其提供有意义的上下文。 So in this case if you wanted to expose Events and reviews my inclination is to go with following url structure: 因此,在这种情况下,如果您想公开事件和评论,我倾向于使用以下url结构:

/events             //all events
/events/1           //event #1
/events/1/reviews   //event #1 reviews

Each of these resource identifiers can have any HTTP Verb applied to them 这些资源标识符中的每一个都可以应用任何HTTP谓词

Implementation 履行

For the implementation I generally follow a message-based design and group all related operations based on Response type and call context. 对于实现,我通常遵循基于消息的设计,并基于响应类型和调用上下文对所有相关操作进行分组。 For this I would do something like: 为此我会做类似的事情:

[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; }
}

And follow a similar pattern for Event reviews 并按照类似的模式进行活动评论

[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; }
}

The implementation should be fairly straight forward based on these messages, which (depending on code-base size) I would organize in 2 EventsService and EventReviewsService classes. 基于这些消息,实现应该是相当直接的,这些消息(取决于代码库大小)我将在2个EventsServiceEventReviewsService类中组织。 I should note that I use pluralization for Service Request DTO names myself to avoid clashing with data models of the same name. 我应该注意,我自己使用多个服务请求DTO名称,以避免与同名的数据模型发生冲突。

Although I've separated UpdateEvent and CreateEvent here, I will sometimes will merge them into a single idempotent StoreEvent operation if the use-case permits. 虽然我在这里分离了UpdateEventCreateEvent ,但如果用例允许,我有时会将它们合并到一个幂等的StoreEvent操作中。

Physical Project Structure 物理项目结构

Ideally the root-level AppHost project should be kept lightweight and implementation-free. 理想情况下,根级AppHost项目应保持轻量级且无实现。 Although for small projects with only a few services it's ok for everything to be in a single project and to simply grow your architecture when and as needed. 虽然对于只有少量服务的小型项目,可以将所有内容都放在一个项目中,并根据需要简单地扩展您的架构。

For medium-to-large projects we recommend the physical structure below which for the purposes of this example we'll assume our Application is called EventMan . 对于中型到大型项目,我们建议使用下面的物理结构,为了本示例的目的,我们假设我们的应用程序称为EventMan

The order of the projects also show its dependencies, eg the top-level EventMan project references all sub projects whilst the last EventMan.ServiceModel project references none : 项目的顺序也显示其依赖关系,例如顶级EventMan项目引用所有子项目,而最后一个EventMan.ServiceModel项目引用none

- 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

With the EventMan.ServiceModel DTO's kept in their own separate implementation and dependency-free dll, you're freely able to share this dll in any .NET client project as-is - which you can use with any of the generic C# Service Clients to provide an end-to-end typed API without any code-gen. 通过将EventMan.ServiceModel DTO保存在各自独立的实现和无依赖dll中,您可以自由地在任何.NET客户端项目中共享此dll,您可以将其与任何通用C#服务客户端一起使用提供端到端类型的API,没有任何代码。


Update 更新

Not sure if it will help in your scenario/understanding, but I find this presentation helpful: 不确定它是否有助于你的场景/理解,但我发现这个演示文稿很有帮助:

Designing a Beautiful REST+JSON API 设计一个漂亮的REST + JSON API

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM