我对API控制器有如下操作:
[HttpPost]
public async Task<IActionResult> StartDeployment(
[FromQuery]Guid deploymentId,
[FromRoute]RequestInfo requestInfo,
[FromBody]DeploymenRequest deploymentRequest)
{
}
可通过复杂的URL(requestInfo
)获得,并接收HTTP POST请求有效负载(deploymentRequest
)。
是否可以将[FromRoute]
和[FromBody]
组合在一起,所以我将拥有单个请求模型:
public class UberDeploymentRequestInfo
{
[FromQuery]public Guid DeploymentId { get; set; }
[FromRoute]public RequestInfo RequestInfo { get; set; }
[FromBody]public DeploymenRequest DeploymentRequest { get; set; }
}
因此我可以使用Fluent验证来使用单个验证器:
internal class UberDeploymentRequestInfoValidator : AbstractValidator<UberDeploymentRequestInfo>
{
public UberDeploymentRequestInfoValidator()
{
// validation rules can access both url and payload
}
}
答案 0 :(得分:2)
我对上述ModelBinder进行了进一步的自定义,以使其更通用,并且现在可用于许多不同的合同。想想我也可以在这里共享它,在这里找到了下面的大部分代码。
public class BodyAndQueryAndRouteModelBinder<T> : IModelBinder where T : new()
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
// Read Cat from Body
var memoryStream = new MemoryStream();
var body = bindingContext.HttpContext.Request.Body;
var reader = new StreamReader(body);
var text = await reader.ReadToEndAsync();
var contract = JsonConvert.DeserializeObject<T>(text);
var properties = typeof(T).GetProperties();
foreach (var property in properties)
{
var valueProvider = bindingContext.ValueProvider.GetValue(property.Name);
if (valueProvider.FirstValue.IsNotNullOrEmpty())
{
property.SetValue(contract, valueProvider.FirstValue);
}
}
bindingContext.Result = ModelBindingResult.Success(contract);
}
}
然后我在父合同上使用活页夹:
[ModelBinder(BinderType = typeof(BodyAndQueryAndRouteModelBinder<ConfirmStatusRequest>))]
public class ConfirmStatusRequest
{
public string ShortCode { get; set; }
public IEnumerable<DependantRequest> Dependants { get; set; }
public IEnumerable<CheckinQuestionAnswer> Answers { get; set; }
}
答案 1 :(得分:1)
它是由注释中提到的自定义模型绑定器实现的。这是一些将所有内容连接起来的代码段,在示例中,您可以将带有以下JSON正文的http请求发送到API /api/cats?From=james&Days=20
{
"Name":"",
"EyeColor":"Red"
}
一些课程,您也可以在这里找到它们:https://github.com/atwayne/so-51316269
// We read Cat from request body
public class Cat
{
public string Name { get; set; }
public string EyeColor { get; set; }
}
// AdoptionRequest from Query String or Route
public class AdoptionRequest
{
public string From { get; set; }
public string Days { get; set; }
}
// One class to merge them together
[ModelBinder(BinderType = typeof(CatAdoptionEntityBinder))]
public class CatAdoptionRequest
{
public Cat Cat { get; set; }
public AdoptionRequest AdoptionRequest { get; set; }
}
public class CatAdoptionEntityBinder : IModelBinder
{
public async Task BindModelAsync(ModelBindingContext bindingContext)
{
// Read Cat from Body
var memoryStream = new MemoryStream();
var body = bindingContext.HttpContext.Request.Body;
var reader = new StreamReader(body, Encoding.UTF8);
var text = reader.ReadToEnd();
var cat = JsonConvert.DeserializeObject<Cat>(text);
// Read Adoption Request from query or route
var adoptionRequest = new AdoptionRequest();
var properties = typeof(AdoptionRequest).GetProperties();
foreach (var property in properties)
{
var valueProvider = bindingContext.ValueProvider.GetValue(property.Name);
if (valueProvider != null)
{
property.SetValue(adoptionRequest, valueProvider.FirstValue);
}
}
// Merge
var model = new CatAdoptionRequest()
{
Cat = cat,
AdoptionRequest = adoptionRequest
};
bindingContext.Result = ModelBindingResult.Success(model);
return;
}
}
// Controller
[HttpPost()]
public bool Post([CustomizeValidator]CatAdoptionRequest adoptionRequest)
{
return ModelState.IsValid;
}
public class CatAdoptionRequestValidator : AbstractValidator<CatAdoptionRequest>
{
public CatAdoptionRequestValidator()
{
RuleFor(profile => profile.Cat).NotNull();
RuleFor(profile => profile.AdoptionRequest).NotNull();
RuleFor(profile => profile.Cat.Name).NotEmpty();
}
}
// and in our Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddFluentValidation();
services.AddTransient<IValidator<CatAdoptionRequest>, CatAdoptionRequestValidator>();
}
答案 2 :(得分:1)
我找到了另一种将 IActionContextAccessor
注入验证器的解决方案。有了这个,我可以访问 ROUTE 参数,而无需特殊的模型绑定。
Startup.cs
services.AddHttpContextAccessor();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
CatValidator.cs
public class CatValidator : AbstractValidator<CatDto>
{
public CatValidator(IActionContextAccessor actionContextAccessor)
{
RuleFor(item => item.Age)
.MustAsync(async (context, age, propertyValidatorContext, cancellationToken) =>
{
var catId = (string)actionContextAccessor.ActionContext.RouteData.Values
.Where(o => o.Key == "catId")
.Select(o => o.Value)
.FirstOrDefault();
return true;
});
}
}