如何在.NET C#中获取数据标注模型验证结果中的Json属性名

92dk7w1h  于 2023-05-30  发布在  .NET
关注(0)|答案(1)|浏览(93)

我正在使用.NET Core C#中的数据注解验证数据模型。

型号:

public class UserDetails
{
    /// <summary>
    /// First name of user
    /// </summary>
    [Required(AllowEmptyStrings = false, ErrorMessage = "First name is required.")]
    [JsonProperty("firstName")]
    [DisplayName("firstName")]
    public string? FirstName { get; set; }
}

验证扩展类:

public static class RequestValidationExtension
  {
    public static bool IsValid(this object o, out ICollection<ValidationResult> validationResults)
    {
        validationResults = new List<ValidationResult>();
        return Validator.TryValidateObject(o, new ValidationContext(o, null, null), validationResults, true);
    }
  }

在azure函数类中检查数据模型验证

UserDetails userRequest= JsonConvert.DeserializeObject<UserDetails>(requestBody);

            //Validate Data Annotations.
            if (!userRequest.IsValid(validationResults: out var validationResults))
            {
                string propertyName = validationResults.Select(s => s.MemberNames.FirstOrDefault()).FirstOrDefault().ToString();  -- o/p: FirstName not firstName
               string errorMessage = validationResults.Select(s => s.ErrorMessage);
            }

电流输出:

字符串propertyName返回FirstName而不是firstName。无论我在JsonProperty()中提供什么,我都需要camelCase值

预期输出:

firstName

我的目标:

我有如下所示的自定义异常类,我有targetField要设置模型JSON属性名称。

{
    "errordetails": {
        "statusCode": 400,
        "functionName": "CreateUser",
        "errorMessage": "First name is required.",
        "targetField": "firstName"
    }
}
whlutmcx

whlutmcx1#

在.net 7.0+中,我现在没有测试,你只需要设置以下内容:

services.AddControllers().AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
    });

对于我测试的<.net7.0,您创建了ProblemDetailsFactory的继承:

public sealed class CustomProblemDetailsFactory : ProblemDetailsFactory
{
    private readonly ApiBehaviorOptions options;
    private readonly JsonOptions jsonOptions;
    public CustomProblemDetailsFactory(IOptions<ApiBehaviorOptions> options, IOptions<JsonOptions> jsonOptions)
{
    this.options = options?.Value ?? throw new ArgumentNullException(nameof(options));
    this.jsonOptions = jsonOptions?.Value ?? throw new ArgumentNullException(nameof(jsonOptions));
}

public override ProblemDetails CreateProblemDetails(
    HttpContext httpContext,
    int? statusCode = null,
    string title = null,
    string type = null,
    string detail = null,
    string instance = null)
{
    statusCode ??= 500;

    var problemDetails = new ProblemDetails
    {
        Status = statusCode,
        Title = title,
        Type = type,
        Detail = detail,
        Instance = instance,
    };

    ApplyProblemDetailsDefaults(httpContext, problemDetails, statusCode.Value);

    return problemDetails;
}

public override ValidationProblemDetails CreateValidationProblemDetails(
    HttpContext httpContext,
    ModelStateDictionary modelStateDictionary,
    int? statusCode = null,
    string title = null,
    string type = null,
    string detail = null,
    string instance = null)
{
    if(modelStateDictionary == null)
    {
        throw new ArgumentNullException(nameof(modelStateDictionary));
    }

    statusCode ??= 400;

    var errors = modelStateDictionary.ToDictionary(
        kvp => jsonOptions?.JsonSerializerOptions?.PropertyNamingPolicy?.ConvertName(kvp.Key) ?? kvp.Key,
        kvp => kvp.Value.Errors.Select(x => x.ErrorMessage).ToArray());

    var problemDetails = new ValidationProblemDetails(errors)
    {
        Status = statusCode,
        Type = type,
        Detail = detail,
        Instance = instance,
    };

    if(title != null)
    {
        // For validation problem details, don't overwrite the default title with null.
        problemDetails.Title = title;
    }

    ApplyProblemDetailsDefaults(httpContext, problemDetails, statusCode.Value);

    return problemDetails;
}

private void ApplyProblemDetailsDefaults(HttpContext httpContext, ProblemDetails problemDetails, int statusCode)
{
    problemDetails.Status ??= statusCode;

    if(options.ClientErrorMapping.TryGetValue(statusCode, out var clientErrorData))
    {
        problemDetails.Title ??= clientErrorData.Title;
        problemDetails.Type ??= clientErrorData.Link;
    }

    var traceId = Activity.Current?.Id ?? httpContext?.TraceIdentifier;

    if(traceId != null)
    {
        problemDetails.Extensions["traceId"] = traceId;
    }
}
}

之后,将其注册为singleton:

services.AddSingleton<ProblemDetailsFactory, CustomProblemDetailsFactory>();

并为整个项目注册camel大小写,如上面提到的.net 7+。当然,如果您需要不同的序列化输出,您可以更改ProblemDetailsFactory类的扩展。希望这能帮上忙。从这个线程得到的:https://github.com/dotnet/aspnetcore/issues/7439#issuecomment-541625844

相关问题