Подтвердить что ты не робот

Как удалить префиксы из ключей ModelState?

Например, существует метод действия Web Api:

public HttpMessageResponse Post(UserDto userDto)
{
    if (!this.ModelState.IsValid)
    {
        return this.Request.CreateErrorResponse(
            HttpStatusCode.BadRequest, this.ModelState);
    }

    // ...
}

Клиент отправляет следующий запрос:

HTTP POST: /api/user
{ "username": "me", "password": "Pa$sw0rd" }

И получает ответ:

HTTP 201/Created:
{ "message": "Your request is invalid.",
  "modelState": { "userDto.Password": "Your password is too strong." } }

По умолчанию метод действия предоставляет детали реализации путем префикса ошибок модели с именем аргумента, используемым внутри метода действия. Что делать, если клиентские приложения будут жестко задавать это имя префикса при очистке ошибок модели, а затем изменения кода на стороне сервера (например, вы заменили подпись Post(UserDto userDto) на Post(UserDto dto)), и все клиентские приложения перестают работать.

Вот почему вам нужно убедиться, что этот префикс удален на стороне сервера. Вопрос в том, как сделать это правильно, без осложнений. Например, вы можете создать собственный сериализатор и удалить эти префиксы во время сериализации. Но для этого вам нужно знать имя аргумента модели, а код вызова может выглядеть примерно так:

public HttpMessageResponse Post(UserDto userDto)
{
    if (!this.ModelState.IsValid)
    {
        return this.Request.CreateCustomErrorResponse(
            HttpStatusCode.BadRequest, this.ModelState, modelName: "userDto");
    }

    // ...
}
4b9b3361

Ответ 1

В первой части:

Также сообщения об ошибках, возвращаемые клиенту, не должны содержать эти префиксы

Я согласен с тем, что имя параметра в качестве префикса для всех ошибок состояния модели не является отличным поведением. К счастью, служба, которая имеет такое поведение, является сменной. Вам просто нужно иметь собственный IBodyModelValidator. Вот как это будет выглядеть (используя шаблон Decorator, чтобы служба по умолчанию выполняла большую часть работы):

public class PrefixlessBodyModelValidator : IBodyModelValidator
{
    private readonly IBodyModelValidator _innerValidator;

    public PrefixlessBodyModelValidator(IBodyModelValidator innerValidator)
    {
        if (innerValidator == null)
        {
            throw new ArgumentNullException("innerValidator");
        }

        _innerValidator = innerValidator;
    }

    public bool Validate(object model, Type type, ModelMetadataProvider metadataProvider, HttpActionContext actionContext, string keyPrefix)
    {
        // Remove the keyPrefix but otherwise let innerValidator do what it normally does.
        return _innerValidator.Validate(model, type, metadataProvider, actionContext, String.Empty);
    }
}

Затем заверните службу по умолчанию с помощью:

config.Services.Replace(typeof(IBodyModelValidator), new PrefixlessBodyModelValidator(config.Services.GetBodyModelValidator()));

Для второй части:

elso заменить "modelState" на "ошибки"

Причина, по которой в настоящее время говорит, что "modelState" - это ваш текущий код:

return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);

Эффективно делает следующее:

HttpError error = new HttpError(ModelState, false);
return Request.CreateResponse(HttpStatusCode.BadRequest, error);

Поскольку HttpError сериализуется и имеет свойство с именем "ModelState", это то, что вы видите в ответе.

Если вы хотите другое имя свойства, вы можете использовать собственный класс ошибок:

public class PrettyHttpError
{
    public PrettyHttpError(ModelStateDictionary modelState)
    {
        Message = "Your request is invalid.";
        Errors = new Dictionary<string, IEnumerable<string>>();

        foreach (var item in modelState)
        {
            var itemErrors = new List<string>();
            foreach (var childItem in item.Value.Errors)
            {
                itemErrors.Add(childItem.ErrorMessage);
            }
            Errors.Add(item.Key, itemErrors);
        }
    }

    public string Message { get; set; }

    public IDictionary<string, IEnumerable<string>> Errors { get; set; }
}

И затем создайте свой ответ с этим типом ошибки вместо HttpError:

PrettyHttpError error = new PrettyHttpError(ModelState);
return Request.CreateResponse(HttpStatusCode.BadRequest, error);

Комбинация PrettyHttpError и PrefixlessBodyModelValidator дает запрошенный результат.