Как обрабатывать ошибки состояния модели в вызванном ajax действии контроллера, которое возвращает PartialView - PullRequest
22 голосов
/ 14 февраля 2010

У меня есть действие контроллера POST, которое возвращает частичное представление. Все кажется действительно простым. но. Я загружаю его, используя $.ajax(), установив тип как html. Но когда моя модель не прошла проверку, я подумал, что должен просто выбросить ошибку с ошибками состояния модели. Но мой ответ всегда возвращает 500 Ошибка сервера.

Как я могу сообщить об ошибках состояния модели, не возвращая Json с каким-либо результатом. Я все еще хотел бы вернуть частичное представление, которое я могу напрямую добавить к некоторому элементу HTML.

Редактировать

Я бы также хотел избежать частичного просмотра ошибки. Это будет выглядеть как успех на клиенте. Когда клиент анализирует результат, чтобы определить, является ли он успешным, он подвержен ошибкам. Дизайнеры могут изменить вывод частичного представления, и это само по себе нарушит функциональность. Поэтому я хочу выдать исключение, но верное сообщение об ошибке возвращается клиенту ajax.

Ответы [ 2 ]

17 голосов
/ 15 февраля 2010

Решение

Мне пришлось написать две отдельные части, которые автоматически работают точно так, как и предполагалось .

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

Для этого используются две основные части:

  • Пользовательский класс исключений , который генерируется, когда что-то идет не так, чтобы мы могли различать общие исключения, которые могут произойти в любое время по любой причине, и ошибки, связанные с нашей обработкой (в частности, недопустимое состояние модели)
  • Фильтр действия исключения , который перехватывает наше пользовательское исключение и подготавливает результат на основе этого исключения; Как вы увидите из кода, наше пользовательское исключение будет содержать информацию об ошибках состояния модели, поэтому этот фильтр сможет возвращать пользовательский код состояния HTTP, а также некоторую текстовую информацию

Подробнее, тогда ...

Внешняя ссылка : Вся эта информация (подробное объяснение и весь код) также доступна в моем блоге. Последние обновления кода всегда будут опубликованы .

Класс пользовательских исключений

Этот класс содержит две вещи

  1. позволяет легко отличать ошибки состояния модели от обычных исключений
  2. предоставляет некоторые основные функции, которые я могу использовать позже

Этот класс позже используется в моем специальном фильтре ошибок.

public class ModelStateException : Exception
{
    public Dictionary<string, string> Errors { get; private set; }

    public ModelStateDictionary ModelState { get; private set; }

    public override string Message
    {
        get
        {
            if (this.Errors.Count > 0)
            {
                return this.Errors.First().Value;
            }
            return null;
        }
    }

    private ModelStateException()
    {
        this.Errors = new Dictionary<string, string>();
    }

    public ModelStateException(ModelStateDictionary modelState) : this()
    {
        this.ModelState = modelState;
        if (!modelState.IsValid)
        {
            foreach (KeyValuePair<string, ModelState> state in modelState)
            {
                if (state.Value.Errors.Count > 0)
                {
                    this.Errors.Add(state.Key, state.Value.Errors[0].ErrorMessage);
                }
            }
        }
    }
}

Атрибут фильтра ошибок

Этот атрибут помогает возвращать ошибки клиенту в виде кодов ошибок HTTP при наличии ошибок состояния модели.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class HandleModelStateExceptionAttribute : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }

        if (filterContext.Exception != null && typeof(ModelStateException).IsInstanceOfType(filterContext.Exception) && !filterContext.ExceptionHandled)
        {
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();
            filterContext.HttpContext.Response.ContentEncoding = Encoding.UTF8;
            filterContext.HttpContext.Response.HeaderEncoding = Encoding.UTF8;
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
            filterContext.HttpContext.Response.StatusCode = 400;
            filterContext.HttpContext.Response.StatusDescription = (filterContext.Exception as ModelStateException).Message;
        }
    }
}

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

[HandleModelStateException]
public ActionResult AddComment(MyModel data)
{
    // check if state is valid
    if (!this.ModelState.IsValid)
    {
        throw new ModelStateException(this.ModelState);
    }
    // get data from store
    return PartialView("Comment", /* store data */ );
}

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

Одна проблема (теперь решена)

Но есть еще одна проблема, связанная с этим кодом. Когда мой фильтр действий при ошибке устанавливает StatusDescription и эта строка содержит некоторые специальные символы, такие как Č, я получаю мусор на клиенте. Если я не использую IE (я использую версию 8). FF и CH отображают мусор. Вот почему я устанавливаю кодировки, но это не работает. Если у кого-то есть обходной путь для этой особенности, я был бы более чем рад выслушать.
Если я возвращаю сообщение об ошибке в самом контенте, все в порядке. Кодировка правильная, и я могу отображать все, что захочу.

3 голосов
/ 14 февраля 2010

Попробуйте это.

public ActionResult DoAjaxAction(Entity entity)
{
   if(ModelState.IsValid)
   {
     return PartialView("Valid_View", entity);
   }
   else
   {
     return PartialView("Invalid_View", entity);
   } 

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...