Лучшие практики для MVC, jQuery и обработки ошибок - PullRequest
18 голосов
/ 04 августа 2009

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

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

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

Итак:

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

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

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

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

Спасибо

Ответы [ 7 ]

3 голосов
/ 04 августа 2009

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

2 голосов
/ 06 августа 2009

Есть ли у вас советы о том, как выполнять действия контроллера, которые хорошо работают при обычном вызове и через 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;
        }
    }
}

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

1 голос
/ 19 ноября 2010

Атрибут селектора действий для 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, поэтому такие ошибки легко обрабатываются с помощью функции ajax:

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

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

1 голос
/ 05 августа 2009

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

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

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

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

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

0 голосов
/ 05 августа 2009

В прошлом я использовал 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);
  }
}
0 голосов
/ 05 августа 2009

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

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

0 голосов
/ 04 августа 2009

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

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

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

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