问题:
使用 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 是:
IAppHost.PreRequestFilters
在请求 DTO 反序列化之前执行- 默认请求 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/