Как вернуть результаты проверки? - PullRequest
3 голосов
/ 27 октября 2011

У меня есть сайт ASP.NET MVC, который использует CommandService. Эта служба команд отвечает за выполнение команд, но перед их выполнением необходимо проверить каждую команду, поэтому существует операция Validate, которая возвращает ValidationResult, который выглядит (упрощенно):

public class ValidationResult
{
  public List<string> ErrorCodes { get; set; }
}

Я хотел бы улучшить это, потому что в настоящее время возвращается список строк, таких как 'UserDoesNotExist' или 'TitleIsMandatory', и это, конечно, не лучший подход.

Было бы лучше вернуть что-то строго напечатанное. Но как я могу это сделать?

Вариант 1: использовать одно большое перечисление, например:

public enum ErrorCode { UserDoesNotExist, TitleIsMandatory}

public class ValidationResult
{
  public List<ErrorCode> ErrorCodes { get; set; }
}

Не знаю, стоит ли создавать такое большое перечисление и помещать в него все доменные коды ошибок?

Вариант 2: использовать классы

public class ErrorCode {}
public class UserDoesNotExist : ErrorCode {}
public class TitleIsMandatory : ErrorCode {}

public class ValidationResult
{
  public List<ErrorCode> ErrorCodes { get; set; }
}

Чистее, но сложнее в использовании?

Что бы вы сделали, или я пропустил другие варианты?

Ответы [ 4 ]

2 голосов
/ 02 ноября 2011

Так вот как я это решил. Во-первых, основной класс ValidationResult выглядит так:

 public class ValidationResult
{        
    public List<ValidationResultItem> ValidationResultItems { get; set; }

    public bool IsAcceptable
    {
        get { return (ValidationResultItems == null || !ValidationResultItems.Any(vri => !vri.IsAcceptable)); }
    }       

    public void Add(ValidationResultItem propertyValidationResultItem)
    {
        ValidationResultItems.Add(propertyValidationResultItem);
    }

    public void Add(IEnumerable<ValidationResultItem> validationResultItems)
    {
        ValidationResultItems.AddRange(validationResultItems);
    }       
}

ValidationResultItem - абстрактный класс:

public abstract class ValidationResultItem
{
    private ResultType _resultType;

    protected ValidationResultItem(ResultType resultType, string message)
    {
        ResultType = resultType;
        Message = message;
    }

    public bool IsAcceptable { get; private set; }
    public string Message { get; private set; }
    public ResultType ResultType
    {
        get { return _resultType; }
        set { _resultType = value; IsAcceptable = (_resultType != ResultType.Error); }
    } 
}

и есть две реализации:

public class PropertyValidationResultItem : ValidationResultItem
{        
    public PropertyValidationResultItem(ResultType resultType, string message, string propertyName, object attemptedValue) : base(resultType, message)
    {            
        PropertyName = propertyName;           
        AttemptedValue = attemptedValue;
    }

    public string PropertyName { get; private set; }        
    public object AttemptedValue { get; private set; }             
}

и

public abstract class BusinessValidationResultItem : ValidationResultItem
{
    protected BusinessValidationResultItem(ResultType resultType, string message) : base(resultType, message)
    {
    }
}

Каждый обработчик команд имеет собственную реализацию BusinessValidationResultItem, например:

public class AddArticleBusinessValidationResultItem : BusinessValidationResultItem
{
    public enum AddArticleValidationResultCode { UserDoesNotExist, UrlTitleAlreadyExists, LanguageDoesNotExist }

    public AddArticleBusinessValidationResultItem(ResultType resultType, string message, AddArticleValidationResultCode code)
        : base(resultType, message)
    {
        Code = code;
    }

    public AddArticleValidationResultCode Code { get; set; }
}

Это означает, что если клиент получает ValidationResult, он может привести BusinessValidationResultItem к конкретному AddArticleBusinessValidationResultItem и, таким образом, использовать конкретное перечисление в операторе switch - избегая волшебных строк.

0 голосов
/ 05 ноября 2011

Может быть, глупое наблюдение, но почему бы не сделать проверку на стороне клиента?Клиенты с хорошим поведением должны проверять команды перед их отправкой.Плохо ведущие себя клиенты не будут проверять и отправлять, но также не будут возвращать причину, по которой это не удалось (какой смысл - даже не рекомендуется из POV безопасности).Конечно, на стороне сервера вы должны повторно проверить и каким-то образом зарегистрировать недопустимые команды, чтобы Ops знал, что есть сбои, которые необходимо устранить / изучить.Если у вас есть клиенты с разными технологиями, может быть полезно определить валидацию в DSL, не зависящем от языка, и сгенерировать код на любом языке, который вам нравится, из этого DSL (хотя есть и другие варианты).

0 голосов
/ 27 октября 2011

и это, конечно, не лучший подход.

Почему бы и нет?Что не так со строками?Если вы беспокоитесь о согласованности, возможно, используйте файлы ресурсов или константы.Струны будут более переносимыми между слоями, но это зависит от вашего дизайна.В любом случае, вам, вероятно, понадобится ErrorCode (строка или перечисление) и ErrorMessage (строка).Я предполагаю, что вам нужно преобразовать эту ошибку во что-то более читаемое для пользователя?

Вы должны посмотреть на встроенную проверку, как упомянуто в одном из комментариев.Проверка происходит из System.ComponentModel.DataAnnotations, ничего общего с MVC.Вы можете проверить вне области MVC, вот пример:

var validationContext = new ValidationContext(yourObject, null, null);
var validationResults = new List<ValidationResult>();

Validator.TryValidateObject(yourObject, validationContext, validationResults);

HTH ...

0 голосов
/ 27 октября 2011

Мой REST api очень похож в том смысле, что при наличии ошибки проверки пользователю возвращается строковое сообщение вместе с объектом.

Например:

[DataContract]
public class ApiErrorResult : ApiResult<IList<ErrorCodes>>
{
    public ApiErrorResult(String message)
    {
        Code = ApiCode.ValidationError;
        Error = message;
    }
}

тогда ApiResult:

public class ApiResult<T>
{

    [DataMember]
    public ApiCode Code
    {
        get;
        set;
    }

    [DataMember]
    public String Error
    {
        get;
        set;
    }

    [DataMember]
    public T Context
    {
        get;
        set;
    }
}

Так что, если это ошибка проверки, тогда контекст будет содержать эквивалентные коды ошибок проверки. Если это не так, я сохраняю фактический результат в контексте и возвращаю ApiResult вместо ApiErrorResult

надеюсь, это как-то поможет.

...