2

Not sure what's wrong with my request but apparently the controller action doesn't receive my post. I want to post this form using antiforgery token and i also want to use ajax request. For this reason, I didn't use the default aspnet core form which include antiforgery token but instead i used the html helper @Html.AntiforgeryToken(). Any idea where the problem come from?

@model RunViewModel
@{
        ViewData["Title"] = "Run";
    }
    <h1 class="text-info">@ViewData["Title"]</h1>

    <form id="run-form">
        @Html.AntiForgeryToken()
        <div class="form-group">
            <label asp-for="InstanceName"></label>
            <input asp-for="InstanceName" class="form-control" placeholder="Enter an instance name here..." />
        </div>
        <div class="form-group">
            <label asp-for="DatabaseName"></label>
            <input asp-for="DatabaseName" class="form-control" placeholder="Enter a database name here..." />
        </div>
        <div class="form-group">
            <label asp-for="FolderPath"></label>
            <input asp-for="FolderPath" class="form-control" />
        </div>
        <div class="col-3 offset-4">
            <button type="submit" class="btn btn-primary">Run!</button>
        </div>
    </form>
@section Scripts {
    <script>

        $("#run-form").submit(function () {

            var data = $('#run-form').serializeArray();
            console.log(data);

            alert($('input:hidden[name="__RequestVerificationToken"]').val());

            $.ajax({
                type: 'POST',
                url: '/Run/Run',
                contentType: 'application/json',
                headers: { RequestVerificationToken: $('input:hidden[name="__RequestVerificationToken"]').val() },
                data: JSON.stringify(data),
                success: function (data) {
                    if (data.status = "success") {
                        alert("Database has been updated !"); //TODO : Mettre un div vert avec un message à la place.
                    }
                },
                error: function () {
                    alert("An error occured 2!"); //TODO : Mettre un div rouge avec un message à la place.
                }
            });

            //event.preventDefault();
        });
    </script>
}

and the controller part is :

        [HttpPost]
    [ValidateAntiForgeryToken]
    public JsonResult Run(string jsonObj)
    {
        //TODO : CHECK JSONOBJ
        RunViewModel model = JsonSerializer.Deserialize<RunViewModel>(jsonObj);

        try
        {
            //Call a service here...
        }
        catch (Exception ex)
        {

            //Renvoyer un message
        }

        return Json(model);
    }

and the viewmodel is :

public class RunViewModel
    {
        [Required]
        [Display(Name="Instance name : ")]
        public string InstanceName { get; set; }

        [Required]
        [Display(Name="Database name : ")]
        public string DatabaseName { get; set; }

        [Required]
        [Display(Name="SQL folder path : ")]
        public string FolderPath { get; set; }

        /// <summary>
        /// Liste ordonnée des fichiers, regroupés par version, à lancer sur la base de données suivant la version actuelle de la base de données.
        /// </summary>
        public IOrderedEnumerable<IGrouping<Version, string>> SqlFilesGroupedByVersion { get; set; }
    }
Ashiquzzaman
  • 5,129
  • 3
  • 27
  • 38
Hans
  • 382
  • 1
  • 4
  • 17

1 Answers1

0
  1. The jqXHR.success(), jqXHR.error(), and jqXHR.complete() callbacks will be deprecated in jQuery 1.8. To prepare your code for their eventual removal, use jqXHR.done(), jqXHR.fail(), and jqXHR.always() instead.

  2. Use ModelState Validation for validate your model

  3. Submit your form using OnClick method for avoiding e.reeventDefault().

I give a full example below as per your requirement (please read all inline comment)--

Model:-

public class RunViewModel
{
    [Required]
    [Display(Name = "Instance name : ")]
    public string InstanceName { get; set; }

    [Required]
    [Display(Name = "Database name : ")]
    public string DatabaseName { get; set; }

    [Required]
    [Display(Name = "SQL folder path : ")]
    public string FolderPath { get; set; }

    /// <summary>
    /// Liste ordonnée des fichiers, regroupés par version, à lancer sur la base de données suivant la version actuelle de la base de données.
    /// </summary>
    public IOrderedEnumerable<IGrouping<Version, string>> SqlFilesGroupedByVersion { get; set; }
}

public class ValidationError
{

    public string PropertyName { get; set; }
    public string[] ErrorList { get; set; }
}

Controller:-

public class RunController : Controller
{
    [HttpGet]
    // GET: Run/Save
    public ActionResult Save()
    {
        var model = new RunViewModel();
        return View(model);
    }

    // POST: Run/Save
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Save(RunViewModel model)
    {
        var status = "Failed";
        var massage = "";
        try
        {
            //If modelstate validation need

            if (!ModelState.IsValid)
            {
                return Json(new
                {
                    status,
                    massage,
                    model,
                    errorList = GetModelStateErrors(ModelState).ToList()
                });
            }
            else
            {
                //Write your code

                status = "success";
                massage = "Database has been updated !";

            }

        }
        catch (Exception ex)
        {

            massage = ex.Message;
        }
        return Json(new
        {
            status,
            massage,
        });

    }

    public IEnumerable<ValidationError> GetModelStateErrors(ModelStateDictionary modelState)
    {
        var errors = (from m in modelState
                      where m.Value.Errors.Count() > 0
                      select
                         new ValidationError
                         {
                             PropertyName = m.Key,
                             ErrorList = (from msg in m.Value.Errors
                                          select msg.ErrorMessage).ToArray()
                         })
                            .AsEnumerable();
        return errors;
    }
}

Save.cshtml:-

@model RunViewModel
@{
    ViewData["Title"] = "Run";
}
<h1 class="text-info">@ViewData["Title"]</h1>
<style>
    .field-validation-valid{
        color:#FF0000;
    }
</style>
<form id="run-form">
    @Html.AntiForgeryToken()
    <div class="form-group">
        <label asp-for="InstanceName"></label>
        <input asp-for="InstanceName" class="form-control" placeholder="Enter an instance name here..." />
        <span asp-validation-for="InstanceName"></span>
    </div>
    <div class="form-group">
        <label asp-for="DatabaseName"></label>
        <input asp-for="DatabaseName" class="form-control" placeholder="Enter a database name here..." />
        <span asp-validation-for="DatabaseName"></span>
    </div>
    <div class="form-group">
        <label asp-for="FolderPath"></label>
        <input asp-for="FolderPath" class="form-control" />
        <span asp-validation-for="FolderPath"></span>

    </div>
    <div class="col-3 offset-4">
        <button type="button" id="btnSubmin" class="btn btn-primary">Run!</button>
    </div>
</form>
@section Scripts {
    <script>

        $("#btnSubmin").on("click", function () {
            var valToken = $('input:hidden[name="__RequestVerificationToken"]').val();
            var model = getFormData('run-form');
            $.ajax({
                type: 'POST',
                url: '/Run/Save',
                contentType: 'application/x-www-form-urlencoded',//set a Content-Type of application/json; charset=UTF-8 When Call API,application/x-www-form-urlencoded is the default contentType
                headers: { RequestVerificationToken: valToken },
                dataType: 'json',
                data: model,//you need to pass json string
            }).done(function (data) {
                if (data.status === "success") {
                    alert("Database has been updated !"); //TODO : Mettre un div vert avec un message à la place.
                } else {
                    displayValidationErrors(data.errorList);
                }
            }).fail(function (data) {

                    alert("An error occured 2!"); //TODO : Mettre un div rouge avec un message à la place.

            });
            /*

             The jqXHR.success(), jqXHR.error(), and jqXHR.complete() callbacks will be deprecated in jQuery 1.8. To prepare your code for their eventual removal, use jqXHR.done(), jqXHR.fail(), and jqXHR.always() instead.

             */

        });

        //Show Validation Message
        function displayValidationErrors(errors) {
            $.each(errors, function (idx, validationError) {
                $("span[data-valmsg-for='" + validationError.propertyName + "']").text(validationError.errorList[0]);// if not working then captilized ex: errorList to ErrorList,propertyName to PropertyName
            });
        }

        //Convert to Json From InputForm
        function getFormData(formId) {
            var $form = $("#" + formId);
            var unindexed_array = $form.serializeArray();
            var indexed_array = {};

            $.map(unindexed_array, function (n, i) {
                indexed_array[n['name']] = n['value'];
            });
            return indexed_array;
        }

    </script>
}

(Tested)

Ashiquzzaman
  • 5,129
  • 3
  • 27
  • 38
  • Hi Ashiquzzaman ! Thank you for your answer. It looks good however I tried and there is a get request sent to the controller rather than post. I thought it was the same issue than https://stackoverflow.com/questions/12195883/jquery-ajax-is-sending-get-instead-of-post. Also, I have the message box "An errror occured 2!" which appeared. – Hans Apr 05 '20 at 14:22
  • @Hans Yes you are right. That was an issue. But this code are ok as per i tested (.net core 3.1). – Ashiquzzaman Apr 05 '20 at 14:36
  • 1
    Ashiquzzaman I found my issue. I added method="post" on the "form" tag. – Hans Apr 05 '20 at 14:36