ASP.NET Core 2.2 - AJAX Get And Post Methods

While creating this article, I took a hard look at whether I wanted to continue to develop with jQuery. The 2 biggest considerations were Bootstrap components and client-side validation. For the demonstration in this article, I use a Bootstrap Modal which relies on jQuery, but I developed the AJAX calls with XML HttpRequest. I use a radio button group for the item selection and demonstrate the vanilla JavaScript methods to manipulate the values.

I will assume you have created a new ASP.NET Core 2.2 Razor Pages project. I reference my previous article ASP.NET Core 2.2 - Error and Exception Handling but I will show an alternate error handling. I use page handlers for the server-side code and mock an item not found error. The Get and Post methods have very different implementations.

From Learn Razor Pages. Handler Methods in Razor Pages

Let's start with a Get method. You can use the default Index page for testing.

Edit Index.cshtml.cs, add OnGetLogTypeById:
public JsonResult OnGetLogTypeById(int logTypeId)
{
    var returnString = "Not Found";
    switch (logTypeId)
    {
        case 1:
            returnString = "Debug";
            break;
        case 2:
            returnString = "Information";
            break;
        case 3:
            returnString = "Warning";
            break;
        case 4:
            returnString = "Error";
            break;
        case 5:
            returnString = "Critical";
            break;
        default:
            break;
    }
    if (returnString == "Not Found")
    {
        return new JsonResult(new { Status = "Failed", Result = "Type Not Found" });
        //throw new InvalidOperationException($"OnGetLogTypeId - Id Not Found.");
    }

    return new JsonResult(new { Status = "Success", Result = returnString });
}

Notice the JsonResult return type and the commented exception which I will explain later.

Edit Index.cshtml, add the radio buttons and Bootstrap modal:
<div class="row mb-2">
    <div class="custom-control custom-radio custom-control-inline">
        <input type="radio" id="DebugRadioButtonId" value="1" class="custom-control-input" name="LogTypeGroup" />
        <label class="custom-control-label" for="DebugRadioButtonId">
            Debug
        </label>
    </div>
    <div class="custom-control custom-radio custom-control-inline">
        <input type="radio" id="InformationRadioButtonId" value="2" class="custom-control-input" name="LogTypeGroup" />
        <label class="custom-control-label" for="InformationRadioButtonId">
            Information
        </label>
    </div>
    <div class="custom-control custom-radio custom-control-inline">
        <input type="radio" id="WarningRadioButtonId" value="3" class="custom-control-input" name="LogTypeGroup" />
        <label class="custom-control-label" for="WarningRadioButtonId">
            Warning
        </label>
    </div>
    <div class="custom-control custom-radio custom-control-inline">
        <input type="radio" id="ErrorRadioButtonId" value="4" class="custom-control-input" name="LogTypeGroup" />
        <label class="custom-control-label" for="ErrorRadioButtonId">
            Error
        </label>
    </div>
    <div class="custom-control custom-radio custom-control-inline">
        <input type="radio" id="CriticalRadioButtonId" value="5" class="custom-control-input" name="LogTypeGroup" />
        <label class="custom-control-label" for="CriticalRadioButtonId">
            Critical
        </label>
    </div>
    <div class="custom-control custom-radio custom-control-inline">
        <input type="radio" id="NotFoundRadioButtonId" value="6" class="custom-control-input" name="LogTypeGroup" />
        <label class="custom-control-label" for="NotFoundRadioButtonId">
            Not Found
        </label>
    </div>
</div>

<div class="row mb-2">
    <button type="button" id="GetLogTypeButtonId" class="btn btn-primary">Get Log Type</button>
</div>

<div class="modal" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title"></h5>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">×</span>
                </button>
            </div>
            <div class="modal-body">
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-primary btn-update">Update</button>
                <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
            </div>
        </div>
    </div>
</div>
Edit Index.cshtml, add the JavaScript:
@section Scripts {
    <script>

        function getLogTypeById(id) {
            var xhr = new XMLHttpRequest();
            xhr.onreadystatechange = function () {
                var modalBody = document.querySelector('.modal-body');
                if (this.readyState == 4 && this.status == 200) {
                    modalBody.innerHTML = '';
                    var responseJSON = JSON.parse(this.responseText);
                    if (responseJSON.status == 'Success') {
                        var dl = document.createElement('dl');
                        for (prop in responseJSON) {
                            var dt = document.createElement('dt');
                            dt.textContent = prop;
                            dl.appendChild(dt);
                            var dd = document.createElement('dd');
                            dd.textContent = responseJSON[prop];
                            dl.appendChild(dd);
                        }
                        var successDiv = document.createElement('div');
                        successDiv.classList.add('alert', 'alert-success');
                        successDiv.appendChild(dl);
                        modalBody.appendChild(successDiv);
                    }
                    else {
                        var dl = document.createElement('dl');
                        for (prop in responseJSON) {
                            var dt = document.createElement('dt');
                            dt.textContent = prop;
                            dl.appendChild(dt);
                            var dd = document.createElement('dd');
                            dd.textContent = responseJSON[prop];
                            dl.appendChild(dd);
                        }
                        var alertDiv = document.createElement('div');
                        alertDiv.classList.add('alert', 'alert-danger');
                        alertDiv.appendChild(dl);
                        modalBody.appendChild(alertDiv);
                    }
                }
                else if (this.readyState == 4) {
                    modalBody.innerHTML = '';
                    var alertDiv = document.createElement('div');
                    alertDiv.classList.add('alert', 'alert-danger');
                    alertDiv.textContent = 'Error ' + this.status;
                    modalBody.appendChild(alertDiv);
                }
            };
            xhr.open('GET', '/index?handler=LogTypeById&logTypeId=' + id);
            xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
            xhr.onerror = function (error) {
                var modalBody = document.querySelector('.modal-body');
                modalBody.innerHTML = '';
                var alertDiv = document.createElement('div');
                alertDiv.classList.add('alert', 'alert-danger');
                alertDiv.textContent = 'Error ' + error.target.status;
                modalBody.appendChild(alertDiv);
            };
            xhr.send();
        }

        document.addEventListener('DOMContentLoaded', function () {
         
            document.querySelector('#GetLogTypeButtonId').addEventListener('click', function () {
                var modalBody = document.querySelector('.modal-body');
                modalBody.innerHTML = '';
                document.querySelector('.modal-title').textContent = 'Get Log Type';
                document.querySelector('.btn-update').style.display = 'none';
                var radioButton = document.querySelector('input[name="LogTypeGroup"]:checked');
                if (radioButton == null) {
                    var alertDiv = document.createElement('div');
                    alertDiv.classList.add('alert', 'alert-danger');
                    alertDiv.textContent = 'Please select a log type.';
                    modalBody.appendChild(alertDiv);
                    $('.modal').modal('show');
                    return;
                }

                // Begin Spinner - Bootstrap 4.2.1 or above
                //var spinnerDiv = document.createElement('div');
                //spinnerDiv.classList.add('spinner-border', 'text-alert', 'd-flex', 'mt-2', 'ml-auto', 'mr-auto');
                //spinnerDiv.setAttribute('role', 'status');

                //var span = document.createElement('span');
                //span.classList.add('sr-only');
                //span.innerText = 'Loading...';

                //spinnerDiv.appendChild(span);
                //modalBody.appendChild(spinnerDiv);
                // End Spinner

                $('.modal').modal('show');
                var id = parseInt(radioButton.value);
                getLogTypeById(id);
            });

        });

    </script>
}

Typically, a Get method passes an id parameter. I just appended the id variable to the xhr.open's URL. You should use a loading spinner while waiting for a response. Bootstrap 4.2.1 introduced a spinner component.

From Bootstrap Documentation. Spinners

Build, run and test.

None Selected:
Get Select Log Type.
Success:
Get Success.
Failed:
Get Failed.

Now let's add a Post method.

Edit Index.cshtml.cs, add OnPostUpdateLogTypeById:
public JsonResult OnPostUpdateLogTypeById([FromBody] UpdateLogTypeModel model)
{
    if (model == null)
    {
        return new JsonResult(new { Status = "Failed", Error = "model = null" });
        //throw new InvalidOperationException($"OnPostUpdateLogTypeById - model = null.");
    }

    // Find log type
    var validIds = new List(new[] { 1, 2, 3, 4, 5 });
    if (!validIds.Contains(model.LogTypeId))
    {
        return new JsonResult(new { Status = "Failed", model.LogTypeId, Error = "Id Not Found" });
        //throw new InvalidOperationException($"OnPostUpdateLogTypeById - Id Not Found.");
    }

    // Update log type = model.LogTypeValue
    var updateSuccess = true;
    if (!updateSuccess)
    {
        return new JsonResult(new { Status = "Failed", model.LogTypeId, Error = "Update Error" });
        //throw new InvalidOperationException($"OnPostUpdateLogTypeById - Update Error.");
    }

    return new JsonResult(new { Status = "Success", model.LogTypeId, model.LogTypeValue });

}

Notice the [FromBody] decoration and UpdateLogTypeModel class.

Edit Index.cshtml.cs, add the UpdateLogTypeModel class:
public class UpdateLogTypeModel
{
    public string Status { get; set; }

    public int LogTypeId { get; set; }

    public string LogTypeValue { get; set; }
}
Edit Index.cshtml, add the UpdateLogTypeButton and an empty post form above the modal:
<div class="row">
    <form method="post"></form>
    <button type="button" id="UpdateLogTypeButtonId" class="btn btn-primary">Update Log Type</button>
</div>

We will use the empty form to get a RequestVerificationToken.

Edit Index.cshtml, add the updateLogTypeById function to JavaScript:
function updateLogTypeById(logTypeId, logTypeValue) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
        var modalBody = document.querySelector('.modal-body');
        if (this.readyState == 4 && this.status == 200) {
            var responseJSON = JSON.parse(this.responseText);
            if (responseJSON.status == 'Success') {
                var radioButton = document.querySelector('input[name="LogTypeGroup"][value="' + responseJSON.logTypeId + '"]');
                var radioButtonId = radioButton.getAttribute('id');
                document.querySelector('label[for="' + radioButtonId + '"]').textContent = responseJSON.logTypeValue;
            }
            else
            {
                modalBody.innerHTML = '';
                document.querySelector('.btn-update').style.display = 'none';
                var dl = document.createElement('dl');
                for (prop in responseJSON) {
                    var dt = document.createElement('dt');
                    dt.textContent = prop;
                    dl.appendChild(dt);
                    var dd = document.createElement('dd');
                    dd.textContent = responseJSON[prop];
                    dl.appendChild(dd);
                }
                var alertDiv = document.createElement('div');
                alertDiv.classList.add('alert', 'alert-danger');
                alertDiv.appendChild(dl);
                modalBody.appendChild(alertDiv);
                $('.modal').modal('show');
            }
        }
        else if (this.readyState == 4) {
            var modalBody = document.querySelector('.modal-body');
            modalBody.innerHTML = '';
            document.querySelector('.btn-update').style.display = 'none';
            var alertDiv = document.createElement('div');
            alertDiv.classList.add('alert', 'alert-danger');
            alertDiv.textContent = 'Error ' + this.status;
            modalBody.appendChild(alertDiv);
            $('.modal').modal('show');
        }
    };
    xhr.open('POST', '/index?handler=UpdateLogTypeById');
    xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
    xhr.setRequestHeader('RequestVerificationToken',
        document.querySelector('input[type="hidden"][name="__RequestVerificationToken"]').value);
    xhr.onerror = function (error) {
        var modalBody = document.querySelector('.modal-body');
        modalBody.innerHTML = '';
        var alertDiv = document.createElement('div');
        alertDiv.classList.add('alert', 'alert-danger');
        alertDiv.textContent = 'Error ' + error.target.status;
        modalBody.appendChild(alertDiv);
        document.querySelector('.btn-update').style.display = 'none';
        $('.modal').modal('show');
    };
    xhr.send(JSON.stringify({
        logTypeId: logTypeId,
        logTypeValue: logTypeValue
    }));

}
Edit Index.cshtml, add the 2 button event listeners after DOMContentLoaded in JavaScript:
document.querySelector('#UpdateLogTypeButtonId').addEventListener('click', function () {
    var modalBody = document.querySelector('.modal-body');
    modalBody.innerHTML = '';
    document.querySelector('.modal-title').textContent = 'Update Log Type';
    document.querySelector('.btn-update').style.display = 'inline-block';
    var radioButton = document.querySelector('input[name="LogTypeGroup"]:checked');
    if (radioButton == null) {
        var alertDiv = document.createElement('div');
        alertDiv.classList.add('alert', 'alert-danger');
        alertDiv.textContent = 'Please select a log type.';
        modalBody.appendChild(alertDiv);
        document.querySelector('.btn-update').style.display = 'none';
        $('.modal').modal('show');
        return;
    }

    var radioButtonId = radioButton.getAttribute('id');
    var radioButtonText = document.querySelector('label[for="' + radioButtonId + '"]').textContent.trim();

    var hiddenInput = document.createElement('input');
    hiddenInput.setAttribute('type', 'hidden');
    hiddenInput.setAttribute('id', 'LogTypeIdHidden');
    hiddenInput.value = radioButton.value;
    modalBody.appendChild(hiddenInput);

    var header = document.createElement('h6');
    header.textContent = radioButtonText;
    modalBody.appendChild(header);

    var textInput = document.createElement('input');
    textInput.setAttribute('type', 'text');
    textInput.setAttribute('id', 'LogTypeInputId');
    textInput.classList.add('form-control');
    modalBody.appendChild(textInput);

    $('.modal').modal('show');
});

document.querySelector('.btn-update').addEventListener('click', function () {
    var logTypeId = document.querySelector('#LogTypeIdHidden').value;
    var logTypeValue = document.querySelector('#LogTypeInputId').value;
    if (logTypeValue == '') {
        if (!document.querySelector('#ErrorLabelId')) {
            var errorLabel = document.createElement('label');
            errorLabel.setAttribute('id', 'ErrorLabelId');
            errorLabel.classList.add('text-danger');
            errorLabel.textContent = 'New value is required.';
            document.querySelector('.modal-body').appendChild(errorLabel);
        }
        return;
    }
    var id = parseInt(logTypeId);
    updateLogTypeById(id, logTypeValue);
    $('.modal').modal('hide');
});

Build, run and test.

None Selected:
Update Select Log Type.
Success:
Update Success.
Failed:
Update Failed.

I was surprised when I threw an exception for the not found error, the xhr.onerror function was not called. Instead the ReadyState when to 4 but the Status was 500 not 200. You wouldn't want to throw exceptions unless you have implemented proper error and exception handling (see ASP.NET Core 2.2 - Error and Exception Handling). If you edit Index.cshtml.cs > OnGetLogTypeId to throw an exception, you see why I used else if (this.readyState == 4) in the getLogTypeById JavaScript function.

Edit Index.cshtml.cs > OnGetLogTypeById, throw Exception on Not Found:
if (returnString == "Not Found")
{
    //return new JsonResult(new { Status = "Failed", Result = "Type Not Found" });
    throw new InvalidOperationException($"OnGetLogTypeId - Id Not Found.");
}
Throw Exception:
Get Exception.
Ken Haggerty
Created 03/06/19
Updated 03/26/19 19:02 GMT

Log In or Reset Quota to read more.

Successfully completed. Thank you for contributing.
Processing...
Something went wrong. Please try again.
Contribute to enjoy content without advertisments.
You can contribute without registering.

Comments(0)

Loading...
Loading...

Not accepting new comments.

Submit your comment. Comments are moderated.

User Image.
DisplayedName - Member Since ?