更多ServiceStack请求DTO建议

时间:2016-10-09 18:54:59

标签: servicestack

这是关于:

的后续行动

ServiceStack Request DTO design

在上述问题中,设计严格地涉及读取操作。写操作怎么样?假设我们想要添加用于创建新预订限制的操作,那么重用名词是否合适?

[Route("/bookinglimits/","POST")]
public class CreateBookingLimit : IReturn<BookingLimit>
{      
     BookingLimit newBookingLimit
}

- 或 - 这会是更好的设计吗?

[Route("/bookinglimits/","POST")]
public class CreateBookingLimit : IReturn<BookingLimit>
{      
  public int ShiftId { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
  public int Limit { get; set; }    }
}

另外,如果我们想添加编辑 - 我们是否应该插入和编辑共享相同的模型并添加ID?

[Route("/bookinglimits/","POST")]
[Route("/bookinglimits/{Id}/","PUT")]
public class CreateBookingLimit : IReturn<BookingLimit>
{      
  public int Id { get; set; }
  public int ShiftId { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
  public int Limit { get; set; }    }
}

当我最有意义地重复使用POCO时,我正试图绕过我的脑袋,当它更有意义地分离意图时。

1 个答案:

答案 0 :(得分:4)

基于消息的API设计

在设计理想的基于消息的API时,需要注意以下几点:服务有效地最终为2个主服务器提供服务:Native Client API和REST API。 Native Clients只是以原始形式发送和接收消息,因此他们可以使用C#请求和响应DTO免费建模自然API,以捕获服务执行其操作所需的信息以及应返回的内容。

将消息投影到理想的HTTP API

在设计基于消息的API之后,您需要通过使用[Route]属性来定义请求DTO来关注如何最好地将消息投影到REST API中服务的自定义端点。

Designing a REST-ful service with ServiceStack上的前一个答案提供了不同请求DTO映射到哪些路线的示例,一般情况下,您希望围绕资源设计您的API,其中每个操作都是&#34 ;作用于资源&#34;这将使您更容易定义自定义路由。用于创建和更新预订限制的理想HTTP API如下所示:

POST /bookinglimits       (Create Booking Limit)
PUT  /bookinglimits/{id}  (Update Booking Limit)

关于良好API设计的一般建议

虽然不是专门针对Web服务,但Ten Rules for Good API Design上的这篇文章提供了有关一般(代码或服务)API设计的良好建议。由于API使用者是您的API的目标受众,他们主要从他们那里获取最大价值,因此他们的设计应该进行优化,以便他们自我描述,使用一致的命名,使用直观,并且可以在不破坏现有客户的情况下进化。消息为naturally suited to versioning,但在对现有已发布的API进行更改时仍需要注意,如果需要,任何其他属性都是可选的,并且具有默认的回退行为。

出于这个原因,虽然您可以通过返回裸BookingLimit来保存一些代码,但我希望为每个服务返回一个特定的Response DTO,它允许服务返回其他元数据而不会破坏现有客户端,同时保持所有服务的一致请求/响应模式。虽然这只是我的偏好 - 返回裸体类型也很好。

ServiceStack实施

要在ServiceStack中实现此功能,我不会使用相同的Request DTO来支持多个动词。由于请求DTO被称为Create*,它传达用户应该只发送此请求DTO以创建预订限制,这通常使用POST请求来完成,例如:

[Route("/bookinglimits", "POST")]
public class CreateBookingLimit : IReturn<CreateBookingLimitResponse>, IPost
{      
  public int ShiftId { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
  public int Limit { get; set; } 
}

public class CreateBookingLimitResponse
{
    public BookingLimit Result { get; set; }
    public ResponseStatus ResponseStatus { get; set; }
}

IPutIPostVerb interface markers,它允许用户和服务客户​​端知道哪个Verb应该发送此消息,这样就可以将所有消息发送到一个Service Gateway method

如果您的服务还支持更新预订限额,那么我会为其创建一个单独的服务,如下所示:

[Route("/bookinglimits/{Id}", "PUT")]
public class UpdateBookingLimit : IReturn<UpdateBookingLimitResponse>, IPut
{      
  public int Id { get; set; }
  public int ShiftId { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
  public int Limit { get; set; } 
}

public class UpdateBookingLimitResponse
{
    public BookingLimit Result { get; set; }
    public ResponseStatus ResponseStatus { get; set; }
}

通过使用单独的操作,您可以确保请求DTO仅包含与该操作相关的属性,从而减少API使用者的混淆。

如果对您的服务有意义,例如两个操作的模式保持不变我将创建/更新操作合并到一个操作中。当你这样做时,你应该使用一致的动词,表明一个操作何时同时执行,例如Store*CreateOrUpdate*

[Route("/bookinglimits", "POST")]
public class StoreBookingLimit : IReturn<StoreBookingLimitResponse>, IPost
{      
  public int Id { get; set; }
  public int ShiftId { get; set; }
  public DateTime StartDate { get; set; }
  public DateTime EndDate { get; set; }
  public int Limit { get; set; } 
}

public class StoreBookingLimitResponse
{
    public BookingLimit Result { get; set; }
    public ResponseStatus ResponseStatus { get; set; }
}

在大多数情况下,服务器为资源生成Id,您应该使用POST,在客户端指定Id的极少数情况下,例如SlugGuid您可以使用PUT粗略转换为&#34;在此位置投放此资源&#34;当客户端知道资源的URL时,这是可能的。

基于消息的API示例

大多数情况下,消息应包含的内容将根据服务要求显而易见,并且随着时间的推移变得直观且自然。有关基于消息的综合API的示例,您可以查看AWS Web服务,该服务在基于消息的设计背后有效地为其Web服务提供服务,该设计使用服务客户端发送消息以访问其所有API,例如, AWS DynamoDB API Reference列出了可用的每个操作以及服务返回的其他DTO类型,例如,这些是他们在创建/修改和查询项目时使用的DynamoDB API:

操作

  • BatchGetItem
  • BatchWriteItem
  • DeleteItem
  • 的GetItem
  • PutItem
  • 查询
  • 扫描
  • 的updateItem

数据类型

  • AttributeDefinition
  • 的AttributeValue
  • AttributeValueUpdate
  • 条件
  • ...

在ServiceStack中,操作称为操作以及您将使用请求DTO定义的内容,而AWS数据类型只是被称为DTO,我将其保存在Types命名空间中以区分来自运营部门。

DynamoDb.ServiceModel (project)

/GetItem
/PutItem
/UpdateItem
/DeleteItem
/Query
/Scan

/Types 
  /AttributeDefinition
  /AttributeValue
  /AttributeValueUpdate

您通常不需要额外的批量请求显式服务,因为您可以使用ServiceStack Auto Batched Requests免费获得。 ServiceStack还包括许多其他好处,它能够在源DTO中生成包含自定义属性和接口的更丰富的DTO,以使richer and succinct end-to-end typed API需要更少的样板和生成的代码,使您可以使用相同的通用服务客户端可以调用任何提供同步和惯用异步API的ServiceStack服务。附加元数据还支持无缝更高级别的功能,如Encrypted MessagingCache Aware ClientsMultiple FormatsService GatewayHTTP Verb Interface Markers等。

否则AWS遵循与ServiceStack非常相似的方法,使用generic Service Clients设计基于消息的API来发送DTOs native in each language