下面是我提出的实现(在等待更好的想法时:)
这是一种通用的方法,而且我认为它具有很强的可扩展性——在提供我正在寻找的错误自动响应功能(当模型状态包含一个或多个错误时)的同时,希望允许类似于模型验证的参数深度验证。
我希望这不是太多的代码,所以答案(!);我有一大堆的文档注释,我把它们取出来以缩短它。
-
对将从中构造参数值的XML进行架构验证失败
-
缺少(null)参数值
模式验证目前是在模型绑定期间执行的,并且会自动将模型错误添加到ModelState中—所以这很好。所以我需要一种方法来执行自动空值检查。
最后,我创建了两个类来结束验证:
[AttributeUsage(AttributeTargets.Parameter,
AllowMultiple = false, Inherited = false)]
public abstract class ValidateParameterAttribute : Attribute
{
private bool _continueValidation = false;
public bool ContinueValidation
{ get { return _continueValidation; } set { _continueValidation = value; } }
private int _order = -1;
public int Order { get { return _order; } set { _order = value; } }
public abstract bool Validate
(ControllerContext context, ParameterDescriptor parameter, object value);
public abstract ModelError CreateModelError
(ControllerContext context, ParameterDescriptor parameter, object value);
public virtual ModelError GetModelError
(ControllerContext context, ParameterDescriptor parameter, object value)
{
if (!Validate(context, parameter, value))
return CreateModelError(context, parameter, value);
return null;
}
}
[AttributeUsage(AttributeTargets.Parameter,
AllowMultiple = false, Inherited = false)]
public class RequiredParameterAttribute : ValidateParameterAttribute
{
private object _missing = null;
public object MissingValue
{ get { return _missing; } set { _missing = value; } }
public virtual object GetMissingValue
(ControllerContext context, ParameterDescriptor parameter)
{
return MissingValue;
}
public override bool Validate
(ControllerContext context, ParameterDescriptor parameter, object value)
{
return !object.Equals(value, GetMissingValue(context, parameter));
}
public override ModelError CreateModelError
(ControllerContext context, ParameterDescriptor parameter, object value)
{
return new ModelError(
string.Format("Parameter {0} is required", parameter.ParameterName));
}
}
有了这个我就可以做到:
public void ActionMethod([RequiredParameter]MyModel p1){ }
输入
ParameterValidationAttribute
:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
Inherited = false)]
public class ParameterValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var paramDescriptors = filterContext.ActionDescriptor.GetParameters();
if (paramDescriptors == null || paramDescriptors.Length == 0)
return;
var parameters = filterContext.ActionParameters;
object paramvalue = null;
ModelStateDictionary modelState
= filterContext.Controller.ViewData.ModelState;
ModelState paramState = null;
ModelError modelError = null;
foreach (var paramDescriptor in paramDescriptors)
{
paramState = modelState[paramDescriptor.ParameterName];
parameters.TryGetValue(paramDescriptor.ParameterName, out paramvalue);
foreach (var validator in paramDescriptor.GetCustomAttributes
(typeof(ValidateParameterAttribute), false)
.Cast<ValidateParameterAttribute>().OrderBy(a => a.Order)
)
{
modelError =
validator.GetModelError(filterContext, paramDescriptor, paramvalue);
if(modelError!=null)
{
if (paramState == null)
modelState[paramDescriptor.ParameterName] =
paramState = new ModelState();
paramState.Errors.Add(modelError);
if (validator.ContinueValidation == false)
break;
}
}
}
base.OnActionExecuting(filterContext);
}
}
呼!快到了。。。
所以,现在我们可以这样做:
[ParameterValidation]
public ActionResult([RequiredParameter]MyModel p1)
{
}
为了完成这个难题,我们需要一些能够调查模型错误并在有错误时自动响应的东西。这是最不整洁的类(我讨厌使用的名称和参数类型),我可能会在我的项目中更改它,但它可以工作,所以我还是会发布它:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
Inherited = false)]
public abstract class RespondWithModelErrorsAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
ModelStateDictionary modelState =
filterContext.Controller.ViewData.ModelState;
if (modelState.Any(kvp => kvp.Value.Errors.Count > 0))
filterContext.Result = CreateResult(filterContext,
modelState.Where(kvp => kvp.Value.Errors.Count > 0));
base.OnActionExecuting(filterContext);
}
public abstract ActionResult CreateResult(
ActionExecutingContext filterContext,
IEnumerable<KeyValuePair<string, ModelState>> modelStateWithErrors);
}
在我的应用程序中,我有一个XmlResult,它接受一个模型实例,并使用DataContractSerializer或XmlSerializer序列化响应—因此我创建了
RespondWithXmlModelErrorsAttribute
Errors
[ParameterValidation]
[RespondWithXmlModelErrors(Order = int.MaxValue)]
public ActionResult([RequiredParameter]MyModel p1)
{
}
但是对于web服务(XML或JSON)来说,能够将错误报告转移到其他东西上,使得编写实际操作方法变得更加容易,而且更具表现力,我觉得。