简体   繁体   English

ServiceStack请求和响应对象

[英]ServiceStack Request and Response Objects

Is it ok (read good practice) to re-use POCO's for the request and response DTO's. 是否可以(阅读良好实践)重复使用POCO进行请求和响应DTO。 Our POCO's are lightweight (ORM Lite) with only properties and some decorating attributes. 我们的POCO是轻量级的(ORM Lite),只有属性和一些装饰属性。

Or, should I create other objects for the request and/or response? 或者,我应该为请求和/或响应创建其他对象吗?

Thanks, 谢谢,

I would say it depends on your system, and ultimately how big and complex it is now, and has the potential to become. 我会说这取决于你的系统,最终它有多大和复杂,并且有可能成为。

The ServiceStack documentation doesn't specify which design pattern you should use. ServiceStack文档未指定您应使用的设计模式。 Ultimately it provides the flexibility for separating the database model POCO's from the DTOs, but it also provides support for their re-use. 最终,它提供了将数据库模型POCO与DTO分离的灵活性,但它也为它们的重用提供了支持。

When using OrmLite: 使用OrmLite时:

OrmLite was designed so that you could re-use your data model POCOs as your request and response DTOs. OrmLite的设计使您可以将数据模型POCO重新用作请求和响应DTO。 As noted from the ServiceStack documentation , this was an intentional design aim of the framework: 从ServiceStack文档中可以看出 ,这是框架的有意设计目标:

The POCOs used in Micro ORMS are particularly well suited for re-using as DTOs since they don't contain any circular references that the Heavy ORMs have (eg EF). Micro ORMS中使用的POCO特别适合作为DTO重新使用,因为它们不包含重ORM具有的任何循环引用(例如EF)。 OrmLite goes 1-step further and borrows pages from NoSQL's playbook where any complex property eg List is transparently blobbed in a schema-less text field, promoting the design of frictionless Pure POCOS that are uninhibited by RDBMS concerns. OrmLite更进一步,从NoSQL的剧本借阅页面,其中任何复杂的属性,例如List在无模式文本字段中透明地膨胀,促进了RDBMS关注不受限制的无摩擦Pure POCOS的设计。

Consideration: 考虑:

If you do opt to re-use your POCOs, because it is supported, you should be aware that there are situations where it will be smarter to use separate request and response DTOs. 如果您确实选择重新使用您的POCO,因为它受支持,您应该知道在某些情况下使用单独的请求和响应DTO会更聪明。

In many cases these POCO data models already make good DTOs and can be returned directly instead of mapping to domain-specific DTOs. 在许多情况下,这些POCO数据模型已经形成了良好的DTO,可以直接返回,而不是映射到特定于域的DTO。

^ Not all cases . ^并非所有情况 Sometimes the difficulty of choosing your design pattern is foreseeing the cases where it may not be suitable for re-use. 有时候选择设计模式的难度是预见到它可能不适合重复使用的情况。 So hopefully a scenario will help illustrate a potential problem. 所以希望情景有助于说明潜在的问题。

Scenario: 场景:

  • You have a system where users can register for your service. 您有一个系统,用户可以在其中注册您的服务。
  • You, as the administrator, have the ability to list users of your service. 作为管理员,您可以列出服务的用户。

If you take the OrmLite POCO re-use approach, then we may have this User POCO: 如果你采用OrmLite POCO重用方法,那么我们可能会有这个User POCO:

public class User
{
    [PrimaryKey, AutoIncrement, Alias("Id")]
    public int UserId { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public string Salt { get; set; }
    public bool Enabled { get; set; }
}

When you make your Create User request you populate Username and Password of your User POCO as your request to the server. 当您创建“创建用户”请求时,您将User POCO的UsernamePassword填充为对服务器的请求。

We can't just push this POCO into the database because: 我们不能只是将这个POCO推送到数据库中,因为:

  • The password in the Password field will be plain text. Password字段中的Password为纯文本。 We are good programmers, and security is important, so we need to create a salt which we add to the Salt property, and hash Password with the salt and update the Password field. 我们是优秀的程序员,安全性很重要,因此我们需要创建一个salt,我们将其添加到Salt属性中,并使用salt哈希Password并更新Password字段。 OK, that's not a major problem, a few lines of code will sort that before the insert. 好吧,这不是一个主要问题,几行代码将在插入之前对其进行排序。

  • The client may have set a UserId , but for create this wasn't required and will cause our database query to fail the insert. 客户端可能已设置UserId ,但对于create,这不是必需的,并且将导致我们的数据库查询无法插入。 So we have to default this value before inserting into the database. 因此我们必须在插入数据库之前默认此值。

  • The Enabled property may have been passed with the request. Enabled属性可能已与请求一起传递。 What if somebody has set this? 如果有人设置了这个怎么办? We only wanted the deal with Username and Password , but now we have to consider other fields that would effect the database insert. 我们只想要处理UsernamePassword ,但现在我们必须考虑影响数据库插入的其他字段。 Similarly they could have set the Salt (though this wouldn't be a problem because we would be overriding the value anyway.) So now you have added validation to do. 类似地,他们可以设置Salt (虽然这不会是一个问题,因为无论如何我们都会覆盖这个值。)所以现在你已经添加了验证。

But now consider when we come to returning a List<User> . 但是现在考虑我们何时返回List<User>

If you re-use the POCO as your response type, there are a lot of fields that you don't want exposed back to the client. 如果您重新使用POCO作为响应类型,则有许多字段不希望暴露给客户端。 It wouldn't be smart to do: 做起来并不聪明:

return Db.Select<User>();

Because you don't have a tight purpose built response for listing Users, the Password hash and the Salt would need to be removed in the logic to prevent it being serialised out in the response. 由于您没有针对列出用户的紧密目的构建响应,因此需要在逻辑中删除Password哈希和Salt ,以防止在响应中将其序列化。

Consider also that during the registration of a user, that as part of the create request we want to ask if we should send a welcome email. 还要考虑在注册用户期间,作为创建请求的一部分,我们想询问是否应该发送欢迎电子邮件。 So we would update the POCO: 所以我们会更新POCO:

public class User
{
    // Properties as before
    ...
    [Ignore] // This isn't a database field
    public bool SendWelcomeEmail { get; set; }
}

We now have the additional property that is only useful in the user creation process. 我们现在拥有仅在用户创建过程中有用的附加属性。 If you use the User POCO over and over again, you will find over time you are adding more and more properties that don't apply to certain contexts. 如果您反复使用User POCO,您会发现随着时间的推移,您添加的内容越来越多,不适用于某些上下文。

When we return the list of users, for example, there is now an optional property of SendWelcomeEmail that could be populated - it just doesn't make sense. 例如,当我们返回用户列表时,现在可以填充SendWelcomeEmail的可选属性 - 它只是没有意义。 It can then be difficult to maintain the code. 然后很难维护代码。

A key thing to remember is that when sharing a POCO object such that it is used as both a request and response object: Properties that you send as a response will be exposed in a request. 要记住的一个关键事项是,在共享POCO对象时,它既可以用作请求对象,也可以用作响应对象: 作为响应发送的属性将在请求中公开。 You will have to do more validation on requests, ultimately the sharing of the POCO may not save effort. 您将不得不对请求进行更多验证,最终共享POCO可能无法节省成本。

In this scenario wouldn't it be far easier to do: 在这种情况下,做起来不会容易得多:

public class CreateUserRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
    public bool SendWelcomeEmail { get; set; }
}

public class UserResponse
{
    public int UserId { get; set; }
    public string Username { get; set; }
    public bool Enabled { get; set; }
}

public class User
{
    [PrimaryKey, AutoIncrement, Alias("Id")]
    public int UserId { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public string Salt { get; set; }
    public bool Enabled { get; set; }
}

We know now when we create a request ( CreateUserRequest ) that we don't have to consider UserId , Salt or Enabled . 我们现在知道当我们创建一个请求( CreateUserRequest )时,我们不必考虑UserIdSaltEnabled

When returning a list of users it's now List<UserResponse> and there is no chance the client will see any properties we don't want them to see. 当返回用户列表时,它现在是List<UserResponse>并且客户端不可能看到我们不希望他们看到的任何属性。

It's clear to other people looking at the code, the required properties for requests, and what will be exposed in response. 其他人看到代码,请求所需的属性以及响应中将会暴露的内容,这一点很清楚。

Summary: 摘要:

Sorry, it's a really long answer, but I think this addresses an aspect of sharing POCOs that some people miss, or fail to grasp initially, I was one of them. 对不起,这是一个非常长的答案,但我认为这解决了分享POCO的一个方面,有些人错过了,或者最初没有掌握,我就是其中之一。

  • Yes you can re-use POCOs for requests and response. 是的,您可以重复使用POCO进行请求和响应。
  • The documentation says it's OK to do so. 文档说这样做是可以的。 In fact it is by design. 实际上它是设计的。
  • In many cases it will be fine to re-use. 在许多情况下,重复使用会很好。
  • There are cases where it's not suitable. 有些情况下它不合适。 (My scenario tries to show this, but you'll find as you develop your own real situations.) (我的场景试图展示这一点,但是当你发展自己的真实情况时,你会发现。)
  • Consider how many additional properties may be exposed because your shared POCO tries to support multiple actions, and how much extra validation work may be required. 考虑可能会暴露多少其他属性,因为您的共享POCO尝试支持多个操作,以及可能需要多少额外的验证工作。
  • Ultimately it's about what you are comfortable maintaining. 最终,这是关于你很舒服的维护。

Hope this helps. 希望这可以帮助。

We have other approach, and my answer is opinionated. 我们有其他方法,我的回答是自以为是。

Because we work not only with C# clients, but mainly with JavaScript clients. 因为我们不仅使用C#客户端,而且主要使用JavaScript客户端。

The request and response DTO's, the routes and the data entities, are negotiated between 请求和响应DTO,路由和数据实体之间进行协商

the customer and the front-end analyst. 客户和前端分析师。 They are part of the specs in a detailed form. 它们是详细形式的规格的一部分。

Even if "customer", in some cases, is our product UI. 即使“客户”,在某些情况下,也是我们的产品UI。

These DTO's don't change without important reason and can be reusable in both sides. 这些DTO在没有重要原因的情况下不会改变,并且可以在双方重复使用。

But the objects in the data layer, can be the same or partial class or different, 但是数据层中的对象可以是相同的也可以是部分类或不同的,

They can be changed internally, including sensitive or workflow information, 它们可以在内部更改,包括敏感或工作流信息,

but they have to be compatible with the specification of the API. 但它们必须与API的规范兼容。

We start with the API first , not the database or ORM. 我们首先使用API ,而不是数据库或ORM。

           Person  { ... }

           Address  { ... }

           ResponceDTO
           { 
             public bool success {get; set;}
             public string message {get; set;}
             public Person  person {get; set;}  
             public List<Address> addresses {get; set;}  
    //customer can use the Person & Address, which can be the same or different
   //with the ones in the data-layer. But we have defined these POCO's  in the specs.                           
            }
           RequestDTO
           { 
             public int Id {get; set;}
             public FilteByAge {get; set;}
            public FilteByZipCode {get; set;}
            }               
           UpdatePersonRequest
           { 
             public int Id {get; set;}
             public bool IsNew {get; set;}
             public Person  person {get; set;} 
             public List<Address> addresses {get; set;}   
            }                  

We don't expose only Request or Response DTOs. 我们不公开请求或响应DTO。

The Person and Address are negotiated with the customer and are referenced in the API specs. 人员和地址与客户协商,并在API规范中引用。

They can be the same or partial or different from the data-layer internal implementation. 它们可以与数据层内部实现相同或部分或不同。

Customer will use them to their application or web site, or mobile. 客户将其用于其应用程序或网站或移动设备。

but the important is that we design and negotiate first the API interface. 但重要的是我们首先设计和协商API接口。

We use also often the requestDTO as parameter to the business layer function, 我们经常使用requestDTO作为业务层函数的参数,

which returns the response object or collection. 它返回响应对象或集合。

By this way the service code is a thin wrapper in front of the business layer. 通过这种方式,服务代码是业务层前面的瘦包装器。

        ResponseDTO  Get(RequestDTO  request)
         {
                      return GetPersonData(request);
          }

Also from the ServiceStack wiki , the API-First development approach 同样来自ServiceStack wiki, API-First开发方法

This will not be a problem given you are OK with exposing the structure of your data objects (if this is a publicly consumed API). 如果您可以公开数据对象的结构(如果这是一个公开使用的API),那么这不会成为问题。 Otherwise, Restsharp is made to be used with simple POCOs :) 否则,Restsharp将与简单的POCO一起使用:)

I think it all depends on how you're using your DTO's, and how you want to balance re-usability of code over readability. 我认为这一切都取决于你如何使用你的DTO,以及你希望如何平衡代码的可重用性和可读性。 If both your requests and responses both utilize a majority of properties on your DTO's, then you'll be getting a lot of re-usability without really lowering readability. 如果您的请求和响应都使用了DTO上的大部分属性,那么您将获得大量的可重用性,而不会降低可读性。 If, for instance, your request object has 10 properties (or vice-versa), but your response only needs 1 of them, someone could make an argument that it's easier to understand/read if your response object only had that 1 property on it. 例如,如果您的请求对象有10个属性(反之亦然),但您的响应只需要其中的1个,那么如果您的响应对象只有1个属性,则有人可以提出一个更容易理解/读取的参数。

In summary, good practice is just clean code. 总之,良好的做法只是干净的代码。 You have to evaluate your particular use case on whether or not your code is easy to use and read. 您必须评估您的特定用例,以确定您的代码是否易于使用和阅读。 Another way to think of it, is to write code for the next person who will read it, even if that person is you. 另一种思考方式是为下一个阅读它的人编写代码,即使那个人就是你。

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

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