Czy używać walidacji ASP.NET MVC z jquery ajax?

119

Mam prostą akcję ASP.NET MVC, taką jak ta:

public ActionResult Edit(EditPostViewModel data)
{

}

EditPostViewModelMieć atrybuty walidacyjne jak to:

[Display(Name = "...", Description = "...")]
[StringLength(100, MinimumLength = 3, ErrorMessage = "...")]
[Required()]
public string Title { get; set; }

W widoku używam następujących pomocników:

 @Html.LabelFor(Model => Model.EditPostViewModel.Title, true)

 @Html.TextBoxFor(Model => Model.EditPostViewModel.Title, 
                        new { @class = "tb1", @Style = "width:400px;" })

Jeśli prześlę na formularzu, że to pole tekstowe zostanie umieszczone w formularzu, walidacja zostanie wykonana najpierw na kliencie, a następnie na usłudze ( ModelState.IsValid).

Teraz mam kilka pytań:

  1. Czy można tego użyć zamiast przesyłania jQuery ajax? Po prostu usuwam formularz, a po kliknięciu przycisku przesyłania javascript zbierze dane, a następnie uruchomi plik $.ajax.

  2. Czy strona serwera będzie ModelState.IsValiddziałać?

  3. Jak mogę przekazać problem z walidacją z powrotem do klienta i przedstawić go tak, jakbym korzystał z funkcji build int validation ( @Html.ValidationSummary(true))?

Przykład połączenia Ajax:

function SendPost(actionPath) {
    $.ajax({
        url: actionPath,
        type: 'POST',
        dataType: 'json',
        data:
        {
            Text: $('#EditPostViewModel_Text').val(),
            Title: $('#EditPostViewModel_Title').val() 
        },
        success: function (data) {
            alert('success');
        },
        error: function () {
            alert('error');
        }
    });
}

Edycja 1:

Zawarte na stronie:

<script src="/Scripts/jquery-1.7.1.min.js"></script>
<script src="/Scripts/jquery.validate.min.js"></script>
<script src="/Scripts/jquery.validate.unobtrusive.min.js"></script>
Bluszcz
źródło
Dobra odpowiedź poniżej. Oto powiązane pytanie. Odpowiedź umożliwia walidację po stronie klienta lub serwera. Jestem zakochany w dostarczanym przez nich kodzie JQuery. (Nie, to nie była moja odpowiedź.) Stackoverflow.com/questions/28987752/…
Przygoda

Odpowiedzi:

155

Strona klienta

Korzystanie z jQuery.validatebiblioteki powinno być dość proste w konfiguracji.

Określ następujące ustawienia w Web.configpliku:

<appSettings>
    <add key="ClientValidationEnabled" value="true"/> 
    <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
</appSettings>

Kiedy tworzysz swój pogląd, możesz zdefiniować takie rzeczy:

@Html.LabelFor(Model => Model.EditPostViewModel.Title, true)
@Html.TextBoxFor(Model => Model.EditPostViewModel.Title, 
                                new { @class = "tb1", @Style = "width:400px;" })
@Html.ValidationMessageFor(Model => Model.EditPostViewModel.Title)

UWAGA: Należy je zdefiniować w elemencie formularza

Następnie należałoby uwzględnić następujące biblioteki:

<script src='@Url.Content("~/Scripts/jquery.validate.js")' type='text/javascript'></script>
<script src='@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")' type='text/javascript'></script>

Powinno to umożliwić skonfigurowanie sprawdzania poprawności po stronie klienta

Zasoby

Po stronie serwera

UWAGA: Dotyczy to tylko dodatkowego sprawdzania poprawności po stronie serwera nad jQuery.validationbiblioteką

Może coś takiego mogłoby pomóc:

[ValidateAjax]
public JsonResult Edit(EditPostViewModel data)
{
    //Save data
    return Json(new { Success = true } );
}

Gdzie ValidateAjaxjest atrybut zdefiniowany jako:

public class ValidateAjaxAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.HttpContext.Request.IsAjaxRequest())
            return;

        var modelState = filterContext.Controller.ViewData.ModelState;
        if (!modelState.IsValid)
        {
            var errorModel = 
                    from x in modelState.Keys
                    where modelState[x].Errors.Count > 0
                    select new
                           {
                               key = x,
                               errors = modelState[x].Errors.
                                                      Select(y => y.ErrorMessage).
                                                      ToArray()
                           };
            filterContext.Result = new JsonResult()
                                       {
                                           Data = errorModel
                                       };
            filterContext.HttpContext.Response.StatusCode = 
                                                  (int) HttpStatusCode.BadRequest;
        }
    }
}

To powoduje zwrócenie obiektu JSON określającego wszystkie błędy modelu.

Przykładowa odpowiedź to

[{
    "key":"Name",
    "errors":["The Name field is required."]
},
{
    "key":"Description",
    "errors":["The Description field is required."]
}]

To byłby zwrócony do obsługi błędów zwrotnego na $.ajaxwezwanie

Możesz zapętlić zwrócone dane, aby ustawić komunikaty o błędach zgodnie z potrzebami w oparciu o zwrócone klucze (myślę, że coś takiego $('input[name="' + err.key + '"]')mogłoby znaleźć Twój element wejściowy

Andrew Burgess
źródło
1
Świetna odpowiedź, zwłaszcza dzięki niesamowitemu ValidateAjaxAttribute! Dziękuję Ci!
René
3
Nie rozumiem, dlaczego ta odpowiedź otrzymała tyle głosów. Nie odpowiada na pytanie 1: jak sprawdzić poprawność klienta podczas wysyłania z $ .ajax? Myślę, że odpowiedź @Shyju pomaga w tym.
Valentin,
2
@Valentin - moja odpowiedź jednak pomaga, ponieważ dane są również sprawdzane po stronie serwera. Wtyczki walidacyjne powinny umożliwiać dynamiczną walidację podczas wypełniania formularza, a gdy formularz jest przesyłany (jednak OP chce to zrobić), serwer zapewni ostateczną walidację, która i tak jest preferowana, ponieważ walidację po stronie klienta można ominąć.
Andrew Burgess
8
Używam rozpiętości komunikatów walidacyjnych jQuery, przechodząc przez zwrócone błędy i wstawiając komunikat o błędzie do odpowiedniego zakresu:for (var i = 0; i < modelStateErrors.length; i++) { $('span[data-valmsg-for="' + modelStateErrors[i].key + '"]').text(modelStateErrors[i].errors[0]); }
Ian
7
Ta odpowiedź jest świetna! Ale nadal uważam, że platforma ASP.NET MVC powinna zapewniać wbudowany sposób robienia tego.
Zignd
40

Powinieneś zrobić serializację danych formularza i wysłać je do akcji kontrolera. ASP.NET MVC powiąże dane formularza z EditPostViewModelobiektem (parametr metody akcji) przy użyciu funkcji powiązania modelu MVC.

Możesz zweryfikować swój formularz po stronie klienta i jeśli wszystko jest w porządku, wyślij dane na serwer. Ta valid()metoda się przyda.

$(function () {

    $("#yourSubmitButtonID").click(function (e) {

        e.preventDefault();
        var _this = $(this);
        var _form = _this.closest("form");

        var isvalid = _form .valid();  // Tells whether the form is valid

        if (isvalid)
        {           
           $.post(_form.attr("action"), _form.serialize(), function (data) {
              //check the result and do whatever you want
           })
        }

    });

});
Shyju
źródło
1
Dzięki! Próbowałem tego $ ("form #" + formId) .validate (), ale mówi, że formularz (który został znaleziony) nie ma validate ()?
Ivy
Zobacz Edit1, gdzie pokazuję, że skrypt sprawdzania poprawności jest zawarty na stronie internetowej. Widzę również, że walidacja jest uruchomiona podczas korzystania z przycisku przesyłania zwykłego typu danych wejściowych.
Ivy
Znalazłem problem (usunąłem Scripts.Render z masterpage). Ale nadal masz problemy z odesłaniem błędów walidacji ModelState do klienta? Jak mam sobie z tym poradzić? Na przykład, jeśli użytkownik nie jest już zalogowany (ModelState.AddModelError ("CustomError", "validation text").
Ivy
1
musisz dołączyć do swojej strony pliki jquery.validate i jquery.validate.unobtrusive js. Czy Twoje dane wejściowe HTML mają atrybut, którego szukają wtyczki walidacyjne?
Shyju
1
Ivy: Twój zwracany typ (ActionResult) jest klasą bazową JsonResult. więc może zwrócić dane JSON. Powinieneś zrobić, jeśli jest to wywołanie ajax (sprawdź Request.IsAjax), pobierz błędy walidacji i zbuduj JSON i odeślij go z powrotem do klienta. Sprawdź plik json w metodzie wywołania zwrotnego $ .post i wyświetl komunikaty o błędach.
Shyju
9

Oto dość proste rozwiązanie:

W kontrolerze zwracamy nasze błędy w ten sposób:

if (!ModelState.IsValid)
        {
            return Json(new { success = false, errors = ModelState.Values.SelectMany(x => x.Errors).Select(x => x.ErrorMessage).ToList() }, JsonRequestBehavior.AllowGet);
        }

Oto część skryptu klienta:

function displayValidationErrors(errors)
{
    var $ul = $('div.validation-summary-valid.text-danger > ul');

    $ul.empty();
    $.each(errors, function (idx, errorMessage) {
        $ul.append('<li>' + errorMessage + '</li>');
    });
}

Oto jak sobie z tym radzimy przez Ajax:

$.ajax({
    cache: false,
    async: true,
    type: "POST",
    url: form.attr('action'),
    data: form.serialize(),
    success: function (data) {
        var isSuccessful = (data['success']);

        if (isSuccessful) {
            $('#partial-container-steps').html(data['view']);
            initializePage();
        }
        else {
            var errors = data['errors'];

            displayValidationErrors(errors);
        }
    }
});

Ponadto renderuję częściowe widoki przez Ajax w następujący sposób:

var view = this.RenderRazorViewToString(partialUrl, viewModel);
        return Json(new { success = true, view }, JsonRequestBehavior.AllowGet);

RenderRazorViewToString, metoda:

public string RenderRazorViewToString(string viewName, object model)
    {
        ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext,
                                                                     viewName);
            var viewContext = new ViewContext(ControllerContext, viewResult.View,
                                         ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
            return sw.GetStringBuilder().ToString();
        }
    }
Alex Herman
źródło
3
Twoje „GetModelStateErrors” można całkowicie uprościć, konwertując je na znacznie prostszą instrukcję jednowierszową: return ModelState.Values.SelectMany (x => x.Errors) .Select (x => x.ErrorMessage) .ToList ();
Camilo Terevinto
1
Dlaczego po prostu nie zwrócić a PartialViewdo renderowania do Ajax?
Sinjai
4

Dodano więcej logiki do rozwiązania dostarczonego przez @Andrew Burgess. Oto pełne rozwiązanie:

Utworzono filtr akcji, aby uzyskać błędy dla żądania Ajax:

public class ValidateAjaxAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!filterContext.HttpContext.Request.IsAjaxRequest())
                return;

            var modelState = filterContext.Controller.ViewData.ModelState;
            if (!modelState.IsValid)
            {
                var errorModel =
                        from x in modelState.Keys
                        where modelState[x].Errors.Count > 0
                        select new
                        {
                            key = x,
                            errors = modelState[x].Errors.
                                                          Select(y => y.ErrorMessage).
                                                          ToArray()
                        };
                filterContext.Result = new JsonResult()
                {
                    Data = errorModel
                };
                filterContext.HttpContext.Response.StatusCode =
                                                      (int)HttpStatusCode.BadRequest;
            }
        }
    }

Dodano filtr do mojej metody kontrolera jako:

[HttpPost]
// this line is important
[ValidateAjax]
public ActionResult AddUpdateData(MyModel model)
{
    return Json(new { status = (result == 1 ? true : false), message = message }, JsonRequestBehavior.AllowGet);
}

Dodano wspólny skrypt do walidacji jQuery:

function onAjaxFormError(data) {
    var form = this;
    var errorResponse = data.responseJSON;
    $.each(errorResponse, function (index, value) {
        // Element highlight
        var element = $(form).find('#' + value.key);
        element = element[0];
        highLightError(element, 'input-validation-error');

        // Error message
        var validationMessageElement = $('span[data-valmsg-for="' + value.key + '"]');
        validationMessageElement.removeClass('field-validation-valid');
        validationMessageElement.addClass('field-validation-error');
        validationMessageElement.text(value.errors[0]);
    });
}

$.validator.setDefaults({
            ignore: [],
            highlight: highLightError,
            unhighlight: unhighlightError
        });

var highLightError = function(element, errorClass) {
    element = $(element);
    element.addClass(errorClass);
}

var unhighLightError = function(element, errorClass) {
    element = $(element);
    element.removeClass(errorClass);
}

Na koniec dodałem metodę javascript błędu do mojego formularza Ajax Begin:

@model My.Model.MyModel
@using (Ajax.BeginForm("AddUpdateData", "Home", new AjaxOptions { HttpMethod = "POST", OnFailure="onAjaxFormError" }))
{
}
Manprit Singh Sahota
źródło
1

Możesz to zrobić w ten sposób:

( Edycja: biorąc pod uwagę, że czekasz na odpowiedź jsonzdataType: 'json' )

.NETTO

public JsonResult Edit(EditPostViewModel data)
{
    if(ModelState.IsValid) 
    {
       // Save  
       return Json(new { Ok = true } );
    }

    return Json(new { Ok = false } );
}

JS:

success: function (data) {
    if (data.Ok) {
      alert('success');
    }
    else {
      alert('problem');
    }
},

Jeśli potrzebujesz, mogę również wyjaśnić, jak to zrobić, zwracając błąd 500 i otrzymując błąd w zdarzeniu error (ajax). Ale w twoim przypadku może to być opcja

andres descalzo
źródło
1
Wiem, jak wykonać zwykłe żądanie Jasona, ale nie pomoże mi to w sprawdzaniu poprawności różnych właściwości ani nawet w korzystaniu z wbudowanej walidacji ASP.NET MVC, o którą prosiłem. Prawdopodobnie mógłbym zbudować złożony obiekt Jason, aby wyjaśnić błędy walidacji dla każdej właściwości, ale będzie to dużo pracy i mam nadzieję, że możesz ponownie wykorzystać wbudowaną funkcjonalność walidacji ASP.NET MVC do tego?
Ivy
1 - Jak uruchomić funkcję „SendPost”? 2 - Twoje prawidłowe dane na kliencie?
andres descalzo
Proszę wyjaśnić? Nie dostajesz swojego ostatniego posta?
Ivy