Đây là việc thực hiện mà tôi đã đưa ra (trong khi chờ đợi bất kỳ ý tưởng tốt hơn :))
Đó là một cách tiếp cận chung và tôi nghĩ là khá khả năng mở rộng - cho phép hy vọng một tương tự loại chiều sâu để xác thực thông số khi bạn nhận được xác thực mẫu cùng một lúc như cung cấp chức năng tự động trả lời lỗi (khi trạng thái mô hình chứa một hoặc nhiều lỗi) mà tôi đang tìm kiếm.
Tôi hy vọng đây không phải là quá nhiều mã cho câu trả lời SO (!); Tôi đã có một tải ý kiến tài liệu trong đó tôi đã đưa ra để giữ cho nó ngắn hơn.
Vì vậy, trong trường hợp của tôi, tôi có hai loại lỗi mô hình đó, nếu chúng xảy ra, nên chặn thực hiện các phương pháp hành động:
- Không xác schema của XML từ đó một giá trị tham số sẽ được xây dựng
- Thiếu (null) tham số giá trị
Schema xác nhận hiện đang thực hiện trong mô hình ràng buộc, và tự động thêm lỗi mô hình để các ModelState - vì vậy đó là tuyệt vời. Vì vậy, tôi cần một cách để thực hiện kiểm tra tự động null.
Cuối cùng tôi đã tạo ra hai lớp học để quấn lên xác nhận:
[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));
}
}
Với điều này thì tôi có thể làm điều này:
public void ActionMethod([RequiredParameter]MyModel p1){ /* code here */ }
Nhưng điều này tự nó không làm bất cứ điều gì của Tất nhiên, vì vậy bây giờ chúng tôi cần một cái gì đó để thực sự kích hoạt xác nhận, để có được các lỗi mô hình và thêm chúng vào trạng thái mô hình.
Nhập 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);
}
}
Whew! Gần đó bây giờ ...
Vì vậy, bây giờ chúng ta có thể làm điều này:
[ParameterValidation]
public ActionResult([RequiredParameter]MyModel p1)
{
//ViewData.ModelState["p1"] will now contain an error if null when called
}
Để hoàn thành các câu đố chúng ta cần cái gì đó có thể điều tra các lỗi mô hình và tự động trả lời nếu có bất kỳ. Đây là gọn gàng nhất của các lớp (Tôi ghét tên và kiểu tham số được sử dụng) và tôi có lẽ sẽ thay đổi nó trong dự án của tôi, nhưng nó hoạt động vì vậy tôi sẽ đăng nó anyway:
[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);
}
Trong tôi ứng dụng Tôi có một XmlResult mà có một mẫu Model và serializes để đáp ứng bằng cách sử dụng hoặc DataContractSerializer hoặc XmlSerializer - vì vậy tôi đã tạo ra RespondWithXmlModelErrorsAttribute
kế thừa từ loại cuối cùng này để xây dựng một trong những người có mô hình như một lớp Errors
. của các lỗi mô hình dưới dạng chuỗi. Mã phản hồi cũng tự động được đặt thành 400 Yêu cầu không hợp lệ.
Vì vậy, bây giờ tôi có thể làm điều này:
[ParameterValidation]
[RespondWithXmlModelErrors(Order = int.MaxValue)]
public ActionResult([RequiredParameter]MyModel p1)
{
//now if p1 is null, the method won't even be called.
}
Trong trường hợp của các trang web giai đoạn cuối cùng này sẽ không nhất thiết được yêu cầu, vì lỗi mô hình thường được bao gồm trong một tái dựng hình của trang đó gửi các dữ liệu ở nơi đầu tiên, và cách tiếp cận MVC hiện có phù hợp với tiền phạt này.
Nhưng đối với các dịch vụ web (XML hoặc JSON) có thể giảm tải báo cáo lỗi sang một thứ khác, hãy viết phương pháp hành động thực tế dễ dàng hơn nhiều - và tôi cảm thấy nhiều hơn nữa.
chấp nhận câu trả lời này vì nó hoạt động thực sự tốt cho tôi, và tôi nghĩ nó có thể mở rộng phù hợp. thư viện tiện ích mở rộng Mvc trong nhà. –
Đây là một giải pháp tuyệt vời và những gì tôi đã được sau khi .. chúc mừng Andras. Nỗ lực đầu tiên của tôi gần như ở đó nhưng tôi thấy những gì tôi đã mất tích. – horHAY