Re[4]: [MVC] Post-Redirect-Get и автоматизация форм
От: A_HREF Россия  
Дата: 03.05.11 05:52
Оценка:
Здравствуйте, mogadanez, Вы писали:

M>* Post надо делать _только_ через Ajax.

M>* при этом показывать "крутяшку"
M>* назад прилетает :
M> ** либо структура с ошибками ( например те которые нельзя просто проверить на клиенте )
M> ** либо разрешение сделать редирект( напрмиер просто url )

Полностью согласен, делается это не сложно, а результат красивый. Только добавлю свои пять копеек.
* Оно должно корректно отрабатывать если нет ява скрипта (есть придурки, которые бегают с NoScript в Фаерфоксе)
* Оно должно работать с валидацией (клиентская и серверная)

Давайте рассмотрим простейший пример типа «форма логина на сайте».

Вьюшка:
<fieldset class="smallest">
    <% using (Html.BeginForm("login", "account", FormMethod.Post, new{id="loginForm"})) { %>
    <%= Html.HiddenFor(m => Model.ReturnUrl)%>
    <ol>
        <li>
            <%= Html.LabelFor(model => Model.LoginOrEmail)%>
            <%= Html.TextBoxFor(model => Model.LoginOrEmail, new { @class = "text bigest", id = "login", tabindex = "1" })%>
            <%= Html.ValidationMessageFor(model => Model.LoginOrEmail)%>
        </li>
        <li>
            <%= Html.LabelFor(model => Model.Password)%>
            <%= Html.PasswordFor(model => Model.Password, new { @class = "text big", tabindex = "2" })%>
            <%= Html.ValidationMessageFor(model => Model.Password)%>
        </li>
        <li class="checkbox">
            <%= Html.CheckBoxFor(m => Model.Persist, new {tabindex = "3"})%>
            <%= Html.LabelFor(m => Model.Persist)%>
        </li>
        <li>            
            <%= Html.Button("Войти") %>
        </li>
    </ol>
    <% } %>
    <script type="text/javascript">
        $(document).ready(function () { tryAjaxFormSubmit('loginForm'); });
    </script>
</fieldset>


Обычная такая вьюшка, но если у чувака есть JS, то вызовется функция tryAjaxFormSubmit, которая выглядит так:

function tryAjaxFormSubmit(formId) {
    var f = $("#" + formId);
    f.submit(function () {                
        var validator= f.validate();
        if (validator.valid()) {
            var action = f.attr("action");
            var serializedForm = f.serialize();
            $.ajax({
                type: 'POST',
                url: action,
                dataType: 'json',
                data: serializedForm,
                beforeSend: function () { setFormStateLoading('loginForm', true); },
                success: function (result) {
                    if (result.RedirectUrl != null && result.RedirectUrl != '')
                        window.location = result.RedirectUrl;
                    else {
                        validator.showErrors(result.Errors);
                        setFormStateLoading('loginForm', false);
                    }
                }
            });
        }
        return false;
    });
}

Эта функция перехватывает сабмит формы, валидирует ее с помощью jQuery.validation, если валидация на клиенте прошла, то форма сабмитится на сервер.

Метод контроллера:


[HttpPost, OutputCache(Duration = 60), HandleAjaxRequest]
public ActionResult Login(UserLoginInput input)
{
    if (!ModelState.IsValid)
    {
        return View();
    }

    var user = _userService.GetByLoginOrEmail(input.LoginOrEmail);
    

    FormsAuthentication.SetAuthCookie(user.Login, input.Persist);
    if (!String.IsNullOrEmpty(input.ReturnUrl))
        return Redirect(input.ReturnUrl);
            
    return RedirectToAction("Index", "Pages");
}


Все как обычно, кроме атрибута HandleAjaxRequest, который как раз и обрабатывает результат экшена контроллера.

HandleAjaxRequest:
public class HandleAjaxRequestAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (!filterContext.HttpContext.Request.IsAjaxRequest()) return;

        var state = filterContext.Controller.ViewData.ModelState;
        if (!state.IsValid)
        {
            var response = new JsonResponse
                            {
                                   Status = Status.Error,
                                   Errors = state.ToJson()
                            };
            filterContext.Result = new JsonResult() { Data = response };
            return;
        }

        if ((filterContext.Result is RedirectToRouteResult || filterContext.Result is RedirectResult))
        {
            string url;
            if (filterContext.Result is RedirectResult)
            {
                url = (filterContext.Result as RedirectResult).Url;
            }
            else
            {
                var result = (RedirectToRouteResult)filterContext.Result;
                url = UrlHelper.GenerateUrl(result.RouteName, null, null, result.RouteValues, RouteTable.Routes, filterContext.RequestContext, false);
            }

            var response = new JsonResponse
                                {
                                       Status = Status.Ok,
                                       RedirectUrl = url
                                   };
            filterContext.Result = new JsonResult { Data = response };
        }
    }
}


Суть атрибута в том, чтобы проверить какой был запрос, через Аякс или нет. Если через аякс, то перезаписываем результат экшена на JSON. Если были ошибки, то конвертим их массив строк ключ-значение, который с удовольствием кушает jquery.validation.

Вот в принципе и все. У кого есть JS то все происходит красиво, у кого нет — тоже не плохо
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.