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