Решение
Мне пришлось написать две отдельные части, которые автоматически работают точно так, как и предполагалось .
Таким образом, он должен возвращать частичное представление, когда процесс действия контроллера завершается успешно, и он должен выдавать ошибку с некоторыми деталями сбоя, когда что-то не так, поэтому на стороне клиента все будет отличаться как успехом, так и неудачей, вместо того, чтобы всегда обрабатывать успех.
Для этого используются две основные части:
- Пользовательский класс исключений , который генерируется, когда что-то идет не так, чтобы мы могли различать общие исключения, которые могут произойти в любое время по любой причине, и ошибки, связанные с нашей обработкой (в частности, недопустимое состояние модели)
- Фильтр действия исключения , который перехватывает наше пользовательское исключение и подготавливает результат на основе этого исключения; Как вы увидите из кода, наше пользовательское исключение будет содержать информацию об ошибках состояния модели, поэтому этот фильтр сможет возвращать пользовательский код состояния HTTP, а также некоторую текстовую информацию
Подробнее, тогда ...
Внешняя ссылка : Вся эта информация (подробное объяснение и весь код) также доступна в моем блоге. Последние обновления кода всегда будут опубликованы .
Класс пользовательских исключений
Этот класс содержит две вещи
- позволяет легко отличать ошибки состояния модели от обычных исключений
- предоставляет некоторые основные функции, которые я могу использовать позже
Этот класс позже используется в моем специальном фильтре ошибок.
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 отображают мусор. Вот почему я устанавливаю кодировки, но это не работает. Если у кого-то есть обходной путь для этой особенности, я был бы более чем рад выслушать.
Если я возвращаю сообщение об ошибке в самом контенте, все в порядке. Кодировка правильная, и я могу отображать все, что захочу.