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

Рекомендации по ошибкам MVC, jQuery и обработке

Есть ли у кого-нибудь элегантный способ борьбы с ошибками в ASP.Net MVC? Я постоянно сталкиваюсь с проблемами при работе с запросами на действия контроллера, где Action может использоваться как для обычных запросов, так и для запросов AJAX. Проблема, которую я имею, - найти элегантный способ решения этих проблем.

Например, как я могу обрабатывать ошибки проверки? В идеале я хотел бы отправить форму на сервер через AJAX, а затем вернуть любые ошибки, которые действие запустило, и отобразить их на странице, но для того, чтобы они работали с помощью обычной обратной передачи, когда клиент отключил JavaScript. Я знаю, что я могу использовать плагин проверки jQuery по мере их ввода, но это не то же самое, и он не идеален, учитывая, что ограничения на данные, подлежащие проверке, будут указаны в двух местах (мои сопоставления проверки nHibernate и в файле JavaScript).

Как насчет того, когда пользователь запрашивает несуществующую запись? Должен ли я перенаправить на страницу 404? Что делать, если запрос был сделан через Ajax (скажем, для загрузки в диалоговое окно).

Итак:

Как вы обрабатываете ошибки, вызванные действиями контроллера, когда они вызывались с помощью Ajax? Особенно ошибки модели штата (например, проверка). Могу ли я отправить его через JSON?

У вас есть советы о том, как делать действия контроллера, которые хорошо работают при вызове обычно и через Ajax? Это раздражающая проблема при написании методов действий. Из-за типа возврата я могу получить другой результат в зависимости от вызывающего. Есть ли способ сделать это без двух методов действий?

Какова ваша общая стратегия для обработки ошибок в действиях на MVC? Вы перенаправляете на страницы с ошибками? Переадресовываете ли вы на другую страницу?

Изменить: Я думаю, что часть проблемы заключается в том, что я хочу, чтобы произошли разные вещи, поэтому, если есть ошибка, я хотел бы остановить какой-либо прогресс, пока не исправит и, следовательно, отправит ошибку. В противном случае я могу обновить различные области страницы. Но если бы у меня было одно возвращение, как бы я знал, что это успех или неудача, не обертывая его объектом, у которого есть свойство, обвиняющее его (тем самым затрудняя использование частичных представлений).

Спасибо

4b9b3361

Ответ 1

Вызов AJAX не обновляет страницу, поэтому я бы определенно не перенаправлял 403, 404 и т.д. Я бы отобразил диалоговое окно на стороне клиента, объясняющее неожиданный результат запроса соответствующим образом. Ваш контроллер может вернуть результаты неудачной проверки на вызов AJAX аналогично тому, как он будет возвращать данные успеха, поэтому ваш диалог также может отображать все, что требуется в этом сценарии.

Ответ 2

У вас есть советы о том, как делать действия контроллера, которые хорошо работают при вызове обычно и через Ajax? Это раздражающая проблема при написании методов действий.

Да, да. Мы также работали над аналогичной проблемой - мы хотели, чтобы в приложении было множество форм, которые обычно вызывались бы через ajax, но могли бы попасть в обычное состояние. Более того, мы не хотели, чтобы в javascript плавала целая куча дубликатов логики. Во всяком случае, техника, с которой мы столкнулись, заключалась в том, чтобы использовать пару ActionFilterAttributes для перехвата форм, а затем немного javascript, чтобы связать форму для обработки ajax.

Во-первых, для запроса ajax нам просто нужно поменять основные страницы:

    private readonly string masterToReplace;

    /// <summary>
    /// Initializes an Ajax Master Page Switcharoo
    /// </summary>
    /// <param name="ajaxMaster">Master page for ajax requests</param>
    public AjaxMasterPageInjectorAttribute(string ajaxMaster)
    {
        this.masterToReplace = ajaxMaster;
    }

    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        if (!filterContext.HttpContext.Request.IsAjaxRequest() || !(filterContext.Result is ViewResult)) return;
        ViewResult vr = (ViewResult) filterContext.Result;
        vr.MasterName = masterToReplace;
    }
}

На обратной стороне мы используем xVal для проверки на стороне клиента, поэтому не следует сильно мешать неправильным данным, но все же можно получить некоторые. С этой целью мы просто используем стандартную проверку и метод aciton возвращает форму с сообщениями проверки. Успешные должности вознаграждаются переадресацией в целом. В любом случае мы делаем небольшую инъекцию json для случая успеха:

/// <summary>
/// Intercepts the response and stuffs in Json commands if the request is ajax and the request returns a RedirectToRoute result.
/// </summary>
public class JsonUpdateInterceptorAttribute : ActionFilterAttribute 
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            JsonResult jr = new JsonResult();
            if (filterContext.Result is RedirectResult)
            {
                RedirectResult rr = (RedirectResult) filterContext.Result;
                jr.Data = new {command = "redirect", content = rr.Url};
            }
            if (filterContext.Result is RedirectToRouteResult)
            {
                RedirectToRouteResult rrr = (RedirectToRouteResult) filterContext.Result;
                VirtualPathData vpd = RouteTable.Routes.GetVirtualPath(filterContext.RequestContext, rrr.RouteValues);
                jr.Data = new {command = "redirect", content = vpd.VirtualPath};
            }
            if (jr.Data != null)
            {
                filterContext.Result = jr;
            }
        }
    }
}

Последний трюк использует небольшой javascript-объект, чтобы связать все вместе:

function AjaxFormSwitcher(form, outputTarget, doValidation) {
    this.doValidation = doValidation;
    this.outputTarget = outputTarget;
    this.targetForm = form;
}

AjaxFormSwitcher.prototype.switchFormToAjax = function() {
    var afs = this;
    var opts = {
        beforeSubmit: this.doValidation ? afs.checkValidation : null,
        complete: function(xmlHttp, status){ afs.processResult(afs, xmlHttp, status); },
        clearForm: false
    };
    this.targetForm.ajaxForm(opts);
}

AjaxFormSwitcher.prototype.checkValidation = function(formData, jqForm, options) {
    jqForm.validate();
    return jqForm.valid();
}

AjaxFormSwitcher.prototype.processResult = function(afs, xmlHttp, status) {
    if (xmlHttp == null) return;
    var r = xmlHttp;
    var c = r.getResponseHeader("content-type");
    if (c.match("json") != null) {
        var json = eval("(" + r.responseText + ")");
        afs.processJsonRedirect(json);
    }
    if (c.match("html") != null) {
        afs.outputTarget.html(r.responseText);
    }
}

AjaxFormSwitcher.prototype.processJsonRedirect = function(data) {
    if (data!=null) {
        switch (data.command) {
            case 'redirect':
                window.location.href = data.content;
                break;
        }
    }
}

Этот маленький script обрабатывает такие вещи, как проводка формы, чтобы сделать ajax и обработать результат (либо json command, либо html, который отображается в целевом объекте). Я, по общему признанию, сосать при написании javascript, поэтому, возможно, есть гораздо более грациозный способ написать это.

Ответ 3

Отказ от ответственности: я не делал много программирования ASP.NET MVC.

Тем не менее, я использовал ряд инфраструктур MVC на других языках и выполнил немало проектов django. Со временем я разработал шаблон, чтобы иметь дело с подобными вещами в django, используя включение шаблона.

По существу, я создаю частичный шаблон, содержащий форму (и любые ошибки проверки), которая входит в основной шаблон. Представление (или Контроллер в вашем случае) затем выбирает между этими двумя шаблонами: запросы AJAX получают частичный шаблон, а обычные запросы получают полный шаблон. В основном шаблоне я использую jQuery form plugin для отправки формы через AJAX и просто заменяю текущую форму данными с сервера: если проверка не удалась, я получаю форму с выделенными полями и список ошибок сверху; если POST завершается успешно, форма заменяется сообщением об успешном завершении или тем, что более подходит для вашей ситуации.

Я предполагаю, что в мире ASP.NET MVC UserControls может быть эквивалентом частичных? В этой статье показано, как добиться чего-то похожего на то, что я описал (хотя статья кажется довольно устаревшей, и все могло измениться).

Чтобы ответить на второй вопрос, я не думаю, что вы должны делать перенаправление на "странице не найденной" - один возвращает 302, другой должен вернуть 404; один не является условием ошибки, другой -. Если вы потеряете код состояния, ваш javascript усложняется (так как вам нужно как-то проверить фактические возвращенные данные, чтобы выяснить, что произошло). Эти два сообщения должны дать вам несколько идей о том, как для реализации HTTP-страниц ошибок. Django делает что-то подобное (поднимите исключение Http404, верните код статуса 404 и произвольно отрисуйте шаблон 404.html).

Ответ 4

Атрибут выбора действий для Ajax и обычных запросов

Лучший способ разграничения между запросами Ajax и нормальными - написать атрибут пользовательского действия (AjaxOnlyAtribute) и предоставить два метода действий, каждый из которых обрабатывает его собственную ситуацию.

[HttpPost]
[AjaxOnly]
[ActionName("Add")]
public ActionResult AddAjax(Entity data) { ... }

[HttpPost]
[ActionName("Add")]
public ActionResult AddNormal(Entity data) { ... }

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

Обработка ошибок проверки в запросах Ajax

Обработка ошибок проверки в вызовах Ajax (или любых других ошибок в основном) может быть выполнена с использованием фильтра действий исключения. Я написал конкретный номер под названием ModelStateExceptionAttribute. Это делается так:

[HandleModelStateException]
public ActionResult SomeAjaxAction(Data data)
{
    if (!this.ModelState.IsValid)
    {
        throw new ModelStateException(this.ModelState);
    }
    // usual code from here on
}

И вы, вероятно, сделали свой запрос Ajax с помощью jQuery, чтобы такие ошибки gent были легко обработаны функцией ошибки ajax:

$.ajax({
    type: "POST",
    url: "someURL",
    success: function(data, status, xhr) {
        // handle success
    },
    error: function(xhr, status, err) {
        // handle error
    }
});

Вы можете прочитать подробное сообщение в блоге об этом подходе здесь.

Ответ 5

Мы никогда не перенаправляем (зачем заставлять пользователя повторно нажимать кнопку "назад", если они не понимают, что нужно вводить в конкретное поле?), мы отображаем ошибки на месте, будь то AJAX или нет ( расположение "пятна" на странице полностью зависит от вас, для всех запросов AJAX мы просто показываем цветную верхнюю часть страницы, так же, как stackoverflow делает для первоклассников, только наша не подталкивает остальную часть контента вниз).

Что касается проверки формы, вы можете сделать как серверную, так и клиентскую сторону. Мы всегда пытаемся показать ошибки на стороне сервера поверх формы в отдельном контейнере, на стороне клиента - рядом с полем, о котором идет речь, на submit. После того, как страница вернется с сервера с ошибками проверки на стороне сервера, пользователь сначала увидит только серверные, поэтому нет дублирования.

Для данных, указанных в двух местах, не уверен, что я понимаю, поскольку я никогда не занимался сопоставлениями nHibernate Validation.

Ответ 6

Сохраняйте два представления ошибок: одно для обычного (полного) отображения страницы и другое, которое просто отображает ошибки в XML или JSON.

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

Ответ 7

Я использовал xVal в прошлом, наряду с некоторыми встроенными генераторами правил JS на основе отражения. Идея состоит в том, что вы определяете правило один раз (в вашем случае через nHibernate), а хелпер HTML отражает ваши свойства и генерирует код проверки на стороне клиента на лету (используя плагин jQuery.validation). Точно правильный способ иметь отзывчивый пользовательский интерфейс на стороне клиента, сохраняя при этом проверку на стороне сервера.

К сожалению, этот метод не работает для форм, размещенных в AJAX.

Для правил AJAX было бы так же просто, как добавить массив ошибок в возвращаемые объекты JSON. В любом месте, где вы используете AJAX, просто проверьте длину Ошибок (все модели ModelState.IsValid) и отобразите ошибку. Вы можете использовать метод IsAjaxRequest для обнаружения вызова AJAX:

public ActionResult PostForm(MyModel thing)
{
  UpdateModel(thing);

  if (this.Request.IsAjaxRequest() == false)
  {
    return View();
  }
  else
  {
    foreach(var error in ModelState.Errors)
    {
      MyJsonObject.Errors.Add(error.Message); 
    }
    return JsonResult(MyJsonObject);
  }
}