json - ServiceStack - 在映射到 DTO 之前验证 json 数据

标签 json rest validation servicestack c#-5.0

问题:

使用 ServiceStack,是否可以在将 JSON 数据(通过 ServiceStack)映射到 DTO 之前对其进行验证?

示例:

我的 DTO 形状:

public class ExampleDto
{
  public int? MyValue {get;set;}
}

示例(概率)有效负载:

{
  "MyValue": "BOB"
}

问题:

对我来说,问题是 API 的使用者没有正确查看文档,并且正在尝试传递一个字符串,其中 ServiceStack 映射将期望映射一个可为空的整数。这只是作为 NULL 出现的。

我用的真的很酷validation feature在我的 API 中,但这仅在数据(由我的 API 的使用者传入)映射到 DTO 之后才会生效。据我所知,它没有看到用户尝试传递无法映射到 DTO 的值。

ServiceStack 中是否有任何方法可以验证任何潜在的序列化错误?

理想情况下,我希望能够在 FluentValidation 功能返回的同一错误列表中返回不匹配的序列化以保持一致性,但我会同意不允许最终用户能够进行此类操作根本没有要求。

最佳答案

更新:为了更轻松地支持此场景,我在 this commit 中添加了对覆盖 JSON 反序列化的支持您可以通过重写 AppHost 中的 OnDeserializeJson() 来拦截 JSON 反序列化,例如:

class AppHost : AppHostBase
{
    //..
    public override object OnDeserializeJson(Type intoType, Stream fromStream)
    {
        if (MyShouldValidate(intoType))
        {
            var ms = MemoryStreamFactory.GetStream();
            fromStream.CopyTo(ms); // copy forward-only stream
            ms.Position = 0;
            var json = ms.ReadToEnd();
            // validate json...
            fromStream = ms; // use buffer
        }

        return base.OnDeserializeJson(intoType, fromStream);
    }    
}

此更改可从 v5.12.1+ 开始使用,即现在的 available on MyGet .

这也可以在早期版本中通过注册 custom JSON Format 来完成这实际上是通过将现有的 JSON 反序列化方法路由到可重写的 OnDeserializeJson() 来实现的。


您应该引用Order of Operation文档以找出反序列化之前可用的自定义 Hook ,这些 Hook 是:

  1. IAppHost.PreRequestFilters 在请求 DTO 反序列化之前执行
  2. 默认请求 DTO 绑定(bind)或 Custom Request Binding (如果已注册)

但是,如果您想读取 PreRequestFilters 中的 JSON 请求正文,则需要 Buffer the Request然后您可以自己读取 JSON 进行反序列化和验证。

appHost.PreRequestFilters.Add((httpReq, httpRes) => {
    if (!httpReq.PathInfo.StartsWith("/my-example")) continue;
    httpReq.UseBufferedStream = true;  // Buffer Request Input

    var json = httpReq.GetRawBody();
    // validate JSON ...
    if (badJson) {
        //Write directly to Response
        httpRes.StatusCode = (int)HttpStatusCode.BadRequest;
        httpRes.StatusDescription = "Bad Request, see docs: ...";
        httpRes.EndRequest();

        //Alternative: Write Exception Response
        //httpRes.WriteError(new ArgumentException("Bad Field","field"));            
    }
});

如果请求是有效的 JSON,但只是对于类型无效,您可以将 JSON 解析为非类型化字典,并使用 JS Utils 检查其值。 ,例如:

try {
    var obj = (Dictionary<string, object>) JSON.parse(json);
} catch (Exception ex) {
    // invalid JSON
}

或者,您可以使用以下命令接管请求的反序列化 Custom Request Binding ,即:

base.RegisterRequestBinder<ExampleDto>(httpReq => ... /*ExampleDto*/);

这会抛出自己的异常,尽管您应该注意 ServiceStack API 会反序列化其 Request DTO from multiple sources因此,如果您只检查 JSON 请求正文,它将忽略调用 API 的所有其他方式。

接管请求 DTO 反序列化的另一种方法是让您的服务 read directly from the Request Stream通过让您的请求 DTO 实现 IRequiresRequestStream 来告诉 ServiceStack 跳过反序列化,以便您的服务可以执行此操作并手动验证它,例如:

public class ExampleDto : IRequiresRequestStream
{
    public Stream RequestStream { get; set; }
    public int? MyValue {get;set;}
}

public class MyServices : Service
{
    public IValidator<ExampleDto> Validator { get; set; }

    public async Task<object> PostAsync(ExampleDto request)
    {
        var json = await request.RequestStream.ReadToEndAsync();
        // validate JSON...
        // deserialize into request DTO and validate manuallly
        var dto = json.FromJson<ExampleDto>();
        await Validator.ValidateAndThrowAsync(dto, ApplyTo.Post);
    }
}

关于json - ServiceStack - 在映射到 DTO 之前验证 json 数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69159450/

相关文章:

php - Drupal,如何通过 RESTful Web 服务公开特定节点数据而无需身份验证

rest - spring-security-rest插件注销失败

javascript - 最简单的 JQuery 验证规则示例

javascript - 在 Node.js 中向 Angular.js 发送响应

java - 改造响应主体接收到具有空字段的一半对象

javascript - 未捕获的 token : when want to get json by ajax

FireFox 中的 Javascript 错误不在 IE 和 Chrome 中

validation - Drupal:自定义内容类型验证

json - 在 Go 中循环嵌套的 JSON 元素

c# - 使用 JSON.Net CustomCreationConverter