问题描述
我们有一个应用程序需要一些字段。如果这些字段不存在,我们将返回 400 响应,解释正确错误消息中缺少的内容。将 APIM 添加到组合中似乎使事情变得复杂了很多。由于 APIM 知道该字段是必需的,因此它看起来会短路并返回 404 并带有通用消息,而不是我们自我解释的错误消息。
解决方法
我遇到了同样的问题,我最终改变了我的方法。我所做的是在应用程序端配置它并使用 FluentValidation 使查询字符串参数成为必需。所以,我的模型现在看起来像这样:
using FluentValidation;
public class UrlQueryParameters
{
public string PropertyA { get; set; }
public string PropertyB { get; set; }
}
public class UrlQueryParametersValidator : AbstractValidator<UrlQueryParameters>
{
public UrlQueryParametersValidator()
{
RuleFor(o => o.PropertyA)
.NotEmpty()
.WithMessage("The 'PropertyA' parameter was missing or a value was not provided.");
RuleFor(o => o.PropertyB)
.NotEmpty()
.WithMessage("The 'PropertyB' parameter was missing or a value was not provided.");
}
}
上述代码为 PropertyA
和 PropertyB
属性定义了几个带有自定义消息的验证规则。
现在,通过在 ConfigureServices
文件的 Startup.cs
方法中添加以下代码,启用 FluentValidation 作为我们应用程序的默认验证机制:
public void ConfigureServices(IServiceCollection services) {
// Rest of the code omitted for brevity
services
.AddControllers()
.AddFluentValidation(fv =>
{
fv.DisableDataAnnotationsValidation = true;
// The following line registers ALL public validators in the assembly containing UrlQueryParametersValidator
// There is no need to register additional validators from this assembly.
fv.RegisterValidatorsFromAssemblyContaining<UrlQueryParametersValidator>(lifetime: ServiceLifetime.Singleton);
});
}
此时,您的 API 端点应验证请求中所需的参数,并且 APIM 不应在您尝试访问 404 Not Found
时通过抛出 /api/foo/{id}
来短路请求。
之所以有效,是因为 Swashbuckle 不会自动从 FluentValidation 导入验证规则。这意味着,在 Swagger UI 中查看属性 PropertyA
和 PropertyB
时不会将它们标记为必需。这是这种方法的缺点,因为来自 Swagger UI 的必需查询字符串参数不会被标记为必需,这可能会使消费者感到困惑。但对我来说,将正确的 StatusCode 和有意义的消息返回给消费者更为重要,这就是为什么我会暂时坚持这种解决方法。您可以尝试使用 MicroElements.Swashbuckle.FluentValidation
至少根据 Swagger UI 架构中的要求设置/标记参数。但仅此而已。
我在这里写了博客:Dirty Hack on Making the Required QueryString Params to Work in Azure APIM
,在 API/Product/Global 级别策略添加 on-error 部分,使用选择策略检查是否找到操作:
<choose>
<when condition="@(context.LastError.Source == "configuration" && context.LastError.Reason == "OperationNotFound")">
<return-response>
<set-status code="400" reason="Bad Request" />
</return-response>
</when>
</choose>