问题
I wrote my own extension for ModelStateDictionary, but I am running in to the problem of displaying the inputted value.
Here is my attempt:
public static string ToLoggingFormat(this ModelStateDictionary modelState)
{
var sb = new StringBuilder();
sb.AppendLine("Input validation error occurred:");
foreach (var ms in modelState.Values)
{
foreach (ModelError error in ms.Errors)
{
sb.AppendLine(error.ErrorMessage);
}
if (!string.IsNullOrEmpty(ms.AttemptedValue)) {
sb.AppendLine($"Attempted Value: {ms.AttemptedValue}");
}
}
return sb.ToString();
}
My problem is that ms.AttemptedValue is always null. Any ideas please? I have tried ms.RawValue also but that is also null. The value I am passing in is invalid because it is over the limit and therefore not null.
I am adding a custom XML Input formatter as suggested!
public class ModelStateXmlInputFormatter : XmlSerializerInputFormatter
{
public ModelStateXmlInputFormatter(MvcOptions options) : base(options)
{
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
var result = await base.ReadRequestBodyAsync(context);
foreach (var property in context.ModelType.GetProperties())
{
var propValue = property.GetValue(result.Model, null);
var propAttemptValue = property.GetValue(result.Model, null)?.ToString();
context.ModelState.SetModelValue(property.Name, propValue, propAttemptValue);
}
return result;
}
I add it like so,
services.AddMvc(options =>
{
// Remove JSON input
options.OutputFormatters.RemoveType(typeof(JsonOutputFormatter));
options.InputFormatters.RemoveType(typeof(JsonInputFormatter));
options.ReturnHttpNotAcceptable = true;
// Add our customer XmlInputFormatter
options.InputFormatters.Add(new ModelStateXmlInputFormatter(options));
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
But the extension still returns null for ms.AttemptedValue. Any ideas? :))
This is how I am using the extension:
[HttpPost]
[Route("myrequest")]
public async Task<IActionResult> MyRequest([FromBody] MyRequestType request)
{
if (!ModelState.IsValid) {
Logger.LogThis(ModelState.ToLoggingFormat());
}
}
回答1:
For this issue, it happens for JsonInputFormatter. In Asp.Net Core, there are two ways to bind requst to model, FromForm with model binding and FromBody with JsonInputFormatter. For model binding, it calls context.ModelState.SetModelValue to set value for RawValue and AttemptedValue. But, JsonInputFormatter does not implement this feature.
You need to implement this feature in by custom JsonInputFormatter like
ModelStateJsonInputFormatterpublic class ModelStateJsonInputFormatter : JsonInputFormatter { public ModelStateJsonInputFormatter(ILogger<ModelStateJsonInputFormatter> logger, JsonSerializerSettings serializerSettings, ArrayPool<char> charPool, ObjectPoolProvider objectPoolProvider, MvcOptions options, MvcJsonOptions jsonOptions) : base(logger, serializerSettings, charPool, objectPoolProvider, options, jsonOptions) { } public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context) { var result = await base.ReadRequestBodyAsync(context); foreach (var property in context.ModelType.GetProperties()) { var propValue = property.GetValue(result.Model, null); var propAttemptValue = property.GetValue(result.Model, null)?.ToString(); context.ModelState.SetModelValue(property.Name, propValue, propAttemptValue); } return result; } }Register
ModelStateJsonInputFormatterservices.AddMvc(opt => { var serviceProvider = services.BuildServiceProvider(); var modelStateJsonInputFormatter = new ModelStateJsonInputFormatter( serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<ModelStateJsonInputFormatter>(), serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value.SerializerSettings, serviceProvider.GetRequiredService<ArrayPool<char>>(), serviceProvider.GetRequiredService<ObjectPoolProvider>(), opt, serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value ); opt.InputFormatters.Insert(0, modelStateJsonInputFormatter); }).SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Latest);
回答2:
Try this:
foreach (var ms in modelState.Keys)
{
var value = modelState[ms];
foreach (ModelError error in value.Errors)
{
sb.AppendLine(error.ErrorMessage);
}
if (!string.IsNullOrEmpty(value.Value.AttemptedValue)) {
sb.AppendLine($"Attempted Value: {value.Value.AttemptedValue}");
}
}
Not tested...
来源:https://stackoverflow.com/questions/57891941/extending-modelstate-to-output-my-own-message-how-do-i-get-model-state-value