简体   繁体   English

如何在传递到控制器C#Web API的模型上创建自定义验证消息

[英]How do I create a custom validation message on a model passed into a controller c# web api

How do I create a custom validation message on a model passed into a controller action method of ac# web API? 如何在传递给ac#Web API的控制器操作方法的模型上创建自定义验证消息?

Here is the model: 这是模型:

[DataContract]
public class TestDto //: IValidatableObject
{
    [DataMember]
    [LongValidation("Its not a long!")]
    public long? ID { get; set; }
    [DataMember]
    public string Description { get; set; }

    public string DescriptionHidden { get; set; }
}

Here is my controller class: 这是我的控制器类:

public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET api/values/5
    public string Get(int id)
    {
        return "value";
    }

    // POST api/values
    public string Post([FromBody]TestDto testDto)
    {
        //todo: should a post return any data?
        if (ModelState.IsValid)
            return "success!";
        else
        {
            var ret = string.Empty;
            foreach (var modelState in ModelState)
            {
                ModelErrorCollection errorCollection = modelState.Value.Errors;
                var errors = string.Empty;
                foreach (var error in errorCollection)
                {
                    errors = errors + "exception message: " + error.Exception.Message + ", errorMessage: " + error.ErrorMessage;
                }
                ret = ret + "Error: " + modelState.Key + ", " + modelState.Value.Value + ", errors: " + errors;
            }
            return ret;
        }
    }

    // PUT api/values/5
    public void Put(int id, [FromBody]string value)
    {
    }

    // DELETE api/values/5
    public void Delete(int id)
    {
    }
}

If I post this object into the Post action: 如果我将此对象发布到Post操作中:

{
"ID" : "1aaa","Description": "sample string 2",
}

In my LongValidation's valid method, I get the default value for a long not : "1aaa", so I cannot perform the correct validation in the validator. 在我的LongValidation的有效方法中,我获得了long的默认值not:“ 1aaa”,因此无法在验证器中执行正确的验证。

Here is the code for the Long Validator: 这是Long Validator的代码:

public class LongValidationAttribute : ValidationAttribute
{
    public LongValidationAttribute(string errorMessage) : base(errorMessage)
    {
    }

    public override string FormatErrorMessage(string name)
    {
        return base.FormatErrorMessage(name);
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        long ret;
        bool success = long.TryParse(value.ToString(), out ret);
        //return base.IsValid(value, validationContext);
        if (success == false)
            return new ValidationResult(this.ErrorMessage);
        else
        {
            return ValidationResult.Success;
        }
    }
}

1aaa is not a long value, but a string value. 1aaa不是long值,而是string值。 So when you submit the data to the endpoint, the default model binder will not be able to map this string value to your long? 因此,当您将数据提交到端点时,默认的模型绑定器将无法将此字符串值映射到您的long? property. 属性。

If you absolutely want to get this string value mapped to your clas property and get validated, you need to change your property from long? 如果您绝对希望将此字符串值映射到clas属性并得到验证,则需要将属性更改为long? to string type. string类型。 But ofcourse now, you need to convert it to long in other parts of your code ! 但是,当然,现在,您需要在代码的其他部分将其转换为long!

I suggest simply use the appropriate numeric type if you are expecting numeric value in normal use case. 如果您在正常使用情况下期望数值,我建议您仅使用适当的数值类型。 In your code you can check whether it is null or not and use it as needed. 在您的代码中,您可以检查它是否为null并根据需要使用它。

See this post: https://blog.markvincze.com/how-to-validate-action-parameters-with-dataannotation-attributes/ 看到这个帖子: https : //blog.markvincze.com/how-to-validate-action-parameters-with-dataannotation-attributes/

Basically you need to hook into the MVC pipeline with a custom filter attribute: 基本上,您需要使用自定义过滤器属性挂接到MVC管道中:

public class ValidateActionParametersAttribute : ActionFilterAttribute  
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var descriptor = context.ActionDescriptor as ControllerActionDescriptor;

        if (descriptor != null)
        {
            var parameters = descriptor.MethodInfo.GetParameters();

            foreach (var parameter in parameters)
            {
                var argument = context.ActionArguments[parameter.Name];

                EvaluateValidationAttributes(parameter, argument, context.ModelState);
            }
        }

        base.OnActionExecuting(context);
    }

    private void EvaluateValidationAttributes(ParameterInfo parameter, object argument, ModelStateDictionary modelState)
    {
        var validationAttributes = parameter.CustomAttributes;

        foreach (var attributeData in validationAttributes)
        {
            var attributeInstance = CustomAttributeExtensions.GetCustomAttribute(parameter, attributeData.AttributeType);

            var validationAttribute = attributeInstance as ValidationAttribute;

            if (validationAttribute != null)
            {
                var isValid = validationAttribute.IsValid(argument);
                if (!isValid)
                {
                    modelState.AddModelError(parameter.Name, validationAttribute.FormatErrorMessage(parameter.Name));
                }
            }
        }
    }
}

Based on Kevin's excellent answer I have created the following class to achieve the desired effect under MVC4 / MVC5: 基于Kevin的出色回答,我创建了以下类以在MVC4 / MVC5下达到预期的效果:

public sealed class ValidateActionParametersAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        var descriptor = context.ActionDescriptor;
        if (descriptor != null)
        {
            var modelState = context.Controller.ViewData.ModelState;
            foreach (var parameterDescriptor in descriptor.GetParameters())
            {
                EvaluateValidationAttributes(
                    suppliedValue: context.ActionParameters[parameterDescriptor.ParameterName],
                    modelState: modelState,
                    parameterDescriptor: parameterDescriptor
                );
            }
        }

        base.OnActionExecuting(context);
    }

    static private void EvaluateValidationAttributes(ParameterDescriptor parameterDescriptor, object suppliedValue, ModelStateDictionary modelState)
    {
        var parameterName = parameterDescriptor.ParameterName;
        parameterDescriptor
            .GetCustomAttributes(inherit: true)
            .OfType<ValidationAttribute>()
            .Where(x => !x.IsValid(suppliedValue))
            .ForEach(x => modelState.AddModelError(parameterName, x.FormatErrorMessage(parameterName)));
    }
}

Note: The .ForEach method used above is a simple extension method which you can write on your own. 注意:上面使用的.ForEach方法是一个简单的扩展方法,您可以自己编写。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM