向REST响应添加验证信息

本文关键字:验证 信息 添加 响应 REST | 更新日期: 2023-09-27 18:03:55

我正在尝试构建一个(大部分)restful服务,但是我在设计的一部分上遇到了困难。我们公开了各种资源,在服务器端看起来像:

public class Thing1 : Resource {
  public string ABC {get;set;}
  public string DEF {get;set;}
}

其中Resource为基类:

public class Resource {
  public List<Link> Links {get;set;}
}

其中Link s依次只结合rel s和uri s。通过这种方式,每个Resource都有指向其他资源等的链接,并且消费者可以在服务提供的各种资源中导航。

一些(但不是全部)资源是可编辑的,因此消费者将检索资源,对其进行更改,然后将这些更改PUT返回给服务。当然,在这一点上,服务将根据需要执行验证(并处理任何并发性问题)。

但是,像往常一样,如果消费应用程序可以在尝试PUT请求之前执行一些验证,以减少不必要的往返(与我们可能使用javascript验证的方式非常相似,即使服务器必须重复它)。

所以,我想在响应中包含一些验证信息,以便消费应用程序知道(例如),ABC不能超过6个字符。应该注意的是,目前,消费者可以使用相同的资源类(它们与适当的MediaTypeFormatter类一起位于单独的程序集中)-添加属性(例如System.ComponentModel.DataAnnotations.RequiredAttribute)感觉是错误的,因为这样消费应用程序最终得到的验证就像它们使用共享程序集时一样,而不是像现在在服务中那样。

还有一些更基于策略的验证,在运行时才能计算实际的验证属性。

tl;博士;

在REST响应中包含"有用的"验证信息,以及实际资源,以便消费应用程序可以构建良好的用户体验,这是一个好的设计吗?

向REST响应添加验证信息

比如

> GET /api/Thing/1
< 200 OK
< Content-Type:  application/vnd.acme.resource+xml
<resource>
  <ABC>blah</ABC>
  <DEF>blurg</DEF>
  <links>
    <links rel="help" href="/api/help/things"/>
    <links rel="http://acme.com/rels/validationrules" href="/api/validationrules/things"/>
  </links>
</resource>
> GET /api/validationrules/things
< 200 OK
< Content-Type: application/vnd.acme.validationrules+xml
<rules resourceType="thing">
  <property name="ABC" MaxLength="6"/>
  <property name="DEF" MaxLength="8"/>
</rules>

我在自己的API中做了类似的事情。不幸的是,据我所知,目前还没有一种标准的媒体类型能够满足这种特殊的需求。我怀疑试图定义这种类型的媒体类型会导致"厨房水槽"效应,每个人都有不同的需求,他们都被扔在一起,最终的结果对每个人来说都太复杂了。

然而,定义您自己的媒体类型来满足您的特殊需求可能是一个非常容易管理的挑战。

在我看来,这个解决方案的重要之处在于/api/validationrules/things应该很少更改,因此可以由客户端私下缓存。这意味着客户端为将此信息作为独立的资源检索而支付的成本非常低。

如果你有足够的预算(时间,金钱或两者),为资源构建一个元服务,这样你的rest只返回数据(带有元数据标识符),如果客户端需要,它可以请求验证它刚刚收到的数据的元数据。这样你只发送客户需要的,并且蛋黄和蛋白有合理的分离。

作为实现的变体,对于每个请求/some/res/ource,您可以创建一个同伴/some/res/ource/meta,它将返回关于该资源的只读元数据。如果路径几乎相同,你可以将验证定义为类成员的属性,元服务将简单地从route中找到一个类,并从类定义中构建验证信息。

如果我正确理解了你的问题,你可以执行这样的解决方案:

    public class ServiceResponse
    {
        private List<Exception> exceptions = new List<Exception>();
        public List<Exception> Errors { get { return exceptions; } }
        private string password;
        public string Password
        {
            get
            {
                return password;
            }
            set
            {
                if (string.IsNullOrEmpty(value))
                {
                    exceptions.Add(new ArgumentException("Password cannot be empty!"));
                }
                if (value != null && value.Length < 10)
                {
                    exceptions.Add(new ArgumentException("Password is too short!"));
                }
                if (exceptions.Count == 0)
                {
                    password = value;
                }
                //else throw an Exception that errors were occured or do nothing
            }
        }
    }
然后您可以检查Errors属性是否有任何错误,如果有,则显示所有错误或任何您想要的错误。在这种情况下,属性'password'将不会设置,直到一切都是正确的。硬编码错误消息可以用资源字符串替换。因此,您发送的响应可以在客户端正确处理,而无需任何javascript。