根据Microsoft enter link description here,我不需要在操作方法中使用以下代码:
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
但这不适用于我的情况。我有以下控制器操作方法:
[Route("/api/advertisers/create")]
[HttpPost]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<AdvertiserViewModel>> Create([FromBody] AdvertiserViewModel viewModel)
{
var advertiser = new Advertiser
{
Name = viewModel.Name,
AdvertiserRed2Id = viewModel.InnerId.Value
};
await _dbContext.Advertisers.AddAsync(advertiser);
await _dbContext.SaveChangesAsync();
return Ok();
}
以及以下视图模型:
public class AdvertiserViewModel
{
[Required(ErrorMessage = "Empty inner id!!!!!!")]
[JsonProperty("inner_id")]
[DisplayName("inner_id")]
public int ? InnerId { get; set; }
[Required(ErrorMessage = "Empty name!!!!!")]
[JsonProperty("name")]
[DisplayName("name")]
public string Name { get; set; }
}
但是在这里我有两个问题。
1)如果InnerId
为null,则没有任何验证消息,.NET Core只需将此值写入我的数据库即可。我的意思是,此JSON将不会发出任何验证错误:
{
"name": "hello!!!",
"inner_ididididididi": 123234
}
如您所见,我这里没有inner_id
。因此,文档中的链接讲的是谎言吗?)还是我不知道如何使用它?我们需要
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
2)第二个问题。我看到了一种趋势,Microsoft正在尝试使所有功能变得更加便捷和简单。没关系。但是我找不到从我的[JsonProperty('some_value')]
中查看验证答案的正确方法。我的意思是这里?例如,我们有以下控制器:
[Route("/api/advertisers/create")]
[HttpPost]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<AdvertiserViewModel>> Create([FromBody] AdvertiserViewModel viewModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var advertiser = new Advertiser
{
Name = viewModel.Name,
AdvertiserRed2Id = viewModel.InnerId.Value
};
await _dbContext.Advertisers.AddAsync(advertiser);
await _dbContext.SaveChangesAsync();
return Ok();
}
以及以下请求:
{
"name": "hello!!!",
"inner_id": null
}
那么,我将得到验证答案吗?以下:
{
"InnerId": [
"Empty inner id!!!!!!"
]
}
剃须刀和剃须刀都可以。但是我的客户是JS应用程序。
客户不了解InnerId
是什么,我的客户只了解inner_id
。
要让我们有可能从api控制器if (!ModelState.IsValid) ...
进行清理,这是一个好的开始,bur为什么没有那么简单的功能(根据请求的JSON字段获取验证答案)?在我看来,[ApiController]
可以做到。
根据我的研究,我得到了以下过滤器:
public class DisplayNameValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.ModelState.ErrorCount > 0)
{
var modelType = context.ActionDescriptor.Parameters
.FirstOrDefault(p =>
p.BindingInfo.BindingSource.Id.Equals("Body", StringComparison.InvariantCultureIgnoreCase) ||
p.BindingInfo.BindingSource.Id.Equals("Custom", StringComparison.InvariantCultureIgnoreCase)
)
?.ParameterType; //Get model type
var expandoObj = new ExpandoObject();
var expandoObjCollection =
(ICollection<KeyValuePair<String, Object>>)
expandoObj; //Cannot convert IEnumrable to ExpandoObject
var dictionary = context.ModelState.ToDictionary(k => k.Key, v => v.Value)
.Where(v => v.Value.ValidationState == ModelValidationState.Invalid)
.ToDictionary(
k =>
{
if (modelType != null)
{
var property = modelType.GetProperties().FirstOrDefault(p =>
p.Name.Equals(k.Key, StringComparison.InvariantCultureIgnoreCase));
if (property != null)
{
//Try to get the attribute
var displayName = property.GetCustomAttributes(typeof(DisplayNameAttribute), true)
.Cast<DisplayNameAttribute>().SingleOrDefault()?.DisplayName;
return displayName ?? property.Name;
}
}
return k.Key; //Nothing found, return original vaidation key
},
v => v.Value.Errors.Select(e => e.ErrorMessage).ToList() as Object); //Box String collection
foreach (var keyValuePair in dictionary)
{
expandoObjCollection.Add(keyValuePair);
}
dynamic eoDynamic = expandoObj;
context.Result = new BadRequestObjectResult(eoDynamic);
}
base.OnActionExecuting(context);
}
}
我可以像这样使用它
[DisplayNameValidationFilter]
MyControllerClass : ControllerBase {...}
但是从我的角度来看,这是一个hack。我被迫将其从一个项目复制到另一个项目。也许包装盒中有一些好的解决方案?
我将向您展示我的Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().ConfigureApiBehaviorOptions(options =>
{
options.SuppressInferBindingSourcesForParameters = true;
options.SuppressModelStateInvalidFilter = true;
// options.SuppressMapClientErrors = true;
}).AddNewtonsoftJson();
services.AddAutoMapper(typeof(Startup));
// db context
services.AddEntityFrameworkNpgsql()
.AddDbContext<ApplicationDbContext>();
// services are here
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
如您所见,我正在将.AddNewtonsoftJson()
用于控制器和[JsonProperty(...)]
(软件包Microsoft.AspNetCore.Mvc.NewtonsoftJson
)。