下面是我想出來的(在等待更好的想法:))
這是一個通用的辦法,我認爲執行是相當可擴展性 - 允許希望類似在提供模型驗證的同時提供我期待的錯誤自動響應功能(當模型狀態包含一個或多個錯誤時)時,可以進行參數驗證的深度。
我希望這不是太多代碼的SO回答(!);我在那裏有大量的文檔評論,我已經把它縮短了。
所以,在我的情況我有兩種類型的模型錯誤的,如果他們出現的,應該阻止該操作方法的執行:
從參數值將被構建的XML的
- 失敗架構驗證模型中結合當前正在執行
- 失蹤(空)參數值
模式驗證,並自動添加模型錯誤的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)
{
//using a virtual method so that a missing value could be selected based
//on the current controller's state.
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){ /* code here */ }
但是,這對自己沒有做任何事情當然,所以現在我們需要一些實際觸發驗證,以獲得模型錯誤並將它們添加到模型狀態。
輸入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];
//fetch the parameter value, if this fails we simply end up with null
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)
{
//create model state for this parameter if not already present
if (paramState == null)
modelState[paramDescriptor.ParameterName] =
paramState = new ModelState();
paramState.Errors.Add(modelError);
//break if no more validation should be performed
if (validator.ContinueValidation == false)
break;
}
}
}
base.OnActionExecuting(filterContext);
}
}
呼!近有現在......
所以,現在我們可以這樣做:
[ParameterValidation]
public ActionResult([RequiredParameter]MyModel p1)
{
//ViewData.ModelState["p1"] will now contain an error if null when called
}
爲了完成我們需要的東西,可以調查模型的錯誤並自動,如果有任何迴應了這個難題。這是班中最整潔(我討厭的名稱和使用的參數類型),我可能會改變它在我的項目,但它的作品,所以我會反正張貼:
[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,它接受一個Model實例並使用DataContractSerializer或XmlSerializer序列化到響應 - 所以我創建了RespondWithXmlModelErrorsAttribute
,它繼承了這個最後一個類型,將其中的一個與該模型一起構建爲Errors
類,該類僅包含每個類作爲字符串的模型錯誤。響應代碼也會自動設置爲400錯誤請求。
因此,現在我可以這樣做:
[ParameterValidation]
[RespondWithXmlModelErrors(Order = int.MaxValue)]
public ActionResult([RequiredParameter]MyModel p1)
{
//now if p1 is null, the method won't even be called.
}
在這最後階段不一定會被要求在網頁的情況下,由於模型誤差通常包括在頁面的重新渲染髮送首先是數據,現有的MVC方法適合這種情況。
但對於Web服務(XML或JSON)能夠卸載錯誤報告的東西別的使得編寫實際的操作方法容易得多 - 並且更富於表現力,我覺得。
接受這個答案,因爲它對我來說真的很好,我認爲它是適當的可擴展性是一個非常有效的補充我們的內部Mvc擴展庫。 – 2010-09-15 21:39:24
這是一個很好的解決方案,我之後做了什麼..乾杯安德拉斯。我的第一次嘗試幾乎在那裏,但我看到我失蹤了。 – horHAY 2016-11-25 15:58:55