ASP.NET MVC, Ajax Fetch API, HTML Forms

Все исходники / Язык программирования C# / OS Windows / ASP.NET для веб-приложений / ASP.NET и JavaScript / ASP.NET MVC, Ajax Fetch API, HTML Forms
Оглавление:
  1. ASP.NET MVC AJAX отправка данных HTML Form
  2. HTTP-методы отправки сообщений
  3. Методы отправки POST, GET, PUT
  4. HTML Form элементы на веб-странице
  5. AJAX и Fetch API отправка данных методами POST, PUT
  6. Извлечение данных в приложении ASP.NET MVC
  7. Отправка данных типа application/x-www-form-urlencoded
  8. Исходник примера отправки формы при помощи AJAX, Fetch API

ASP.NET MVC AJAX отправка данных HTML Form

ASP.NET MVC JavaScript HTML Form

HTML формы (веб-формы) представляют собой раздел страницы с интерактивными элементами, предназначенных для ввода данных и отправки этой информации на сервер. Область формы ограничивается тегами <form> </form> и может заключать все элементы тела веб-страницы. Отправка данных производится нажатием на кнопку внутри формы с атрибутом type="submit".

HTML Form наиболее используемый способ обмена данными между клиентом и сервером. Этот способ дает возможность использовать для отправки встроенную функциональность браузера и JavaScript, уменьшая тем самым количество требуемого программного кода.

В ASP.NET взаимодействие с HTML-формами происходит через серверный код, написанный на языках C#, F# или VB.NET. Технология AJAX добавляет этому процессу возможность фонового обмена данными: пользователь нажимает на кнопки – интерфейс быстро обновляется без перезагрузки страницы.

При AJAX взаимодействии сервера и клиента основная валидация данных и обработка ошибок должна происходить на стороне сервера. Кроме того, важно обеспечить защиту от запросов с чужих сайтов (атаки XSRF/CSRF).

Ниже описываются примеры AJAX и Fetch API обмена данными c ASP.NET MVC приложением посредством интерактивных элементов HTML-формы. Клиентский код выполнен на TypeScript и JavaScript, серверный на C#.

HTTP-методы отправки сообщений

Для отправки данных HTML-формой стандартно используются два метода POST и GET. Обмен сообщениями при помощи JavaScript не ограничивается двумя вышеуказанными методами отправки и может включать метод PUT.

Используя JavaScript для отправки данных форм, можно получать доступ к любым атрибутам и значениям элементов формы, осуществлять проверку на валидность и отправлять информацию на сервер требуемым методом без перезагрузки страницы.

Методы отправки POST, GET, PUT

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

HTTP-метод GET предназначен для получения данных с сервера на основании сравнительно коротких параметров запроса. Например, отправка запроса с параметрами
/html-form-get?length=120&width=80&height=60 может инициировать на сервере отправку клиенту результата вычисления объема.

Метод PUT схожий возможностями с POST, но более универсален: предназначен для обновления данных на сервере, но может использоваться и для их создания.

Данные при POST и PUT отправках добавляются к телу страницы после основного HTML-контента, при GET отправках параметры прикрепляются после URL страницы в виде строки запроса и их можно прочитать в строке адреса браузера.

HTML Form элементы на веб-странице

Поскольку элемент формы Checkbox предполагает тип Boolean, то можно не определять атрибут value, в таком случае, при установке галочки, на сервер будет отправляться значение on, а при пустом Checkbox форма проигнорирует отправку данных элемента.

Элементы формы Radio группируются по одинаковому атрибуту name. Группу радиокнопок можно сравнить с перечисляемым типом Enum, на сервер отправляется значение только одного элемента группы, отмеченного как checked.

@Html.AntiForgeryToken() - программная вставка движка Razor для создания невидимого элемента input со значением уникальной метки текущей страницы.

Веб-страница index.cshtml с элементами формы:
@{
    ViewData["Title"] = "JavaScript AJAX HTML Forms";
}
<div class="container">
    <div class="text-center mb-4">
        <h1 class="display-4">ASP.NET AJAX JavaScript</h1>
    </div>
    <div class="row">
        <div class="col-4">
            <h2>HTML Forms</h2>
            <h3>Ввод данных нового товара</h3>
            <form id="formProduct">
                @Html.AntiForgeryToken()
                <div class="mb-3">
                    <label for="input1" class="form-label">Название</label>
                    <input type="text" class="form-control" id="input1" placeholder="" name="nameProduct">
                </div>
                <div class="mb-3">
                    <label for="area1" class="form-label">Описание</label>
                    <textarea class="form-control" id="area1" rows="3" name="descriptionProduct"></textarea>
                </div>

                <div class="mb-3">
                    <h4 class="fs-5 text-muted">Опции</h4>
                    <div class="form-check form-check-inline">
                        <input class="form-check-input" type="checkbox" id="checkbox1" name="pack">
                        <label class="form-check-label" for="checkbox1">Упаковка</label>
                    </div>
                    <div class="form-check form-check-inline">
                        <input class="form-check-input" type="checkbox" id="checkbox2" name="delivery" value="delivery">
                        <label class="form-check-label" for="checkbox2">Доставка</label>
                    </div>
                </div>

                <div class="mb-3 text-muted">
                    <h4 class="fs-5">Выбор цвета</h4>
                    <div class="form-check form-check-inline">
                        <input class="form-check-input" type="radio" name="color" id="radio1" value="black" checked>
                        <label class="form-check-label" for="radio1">Черный</label>
                    </div>
                    <div class="form-check form-check-inline">
                        <input class="form-check-input" type="radio" name="color" id="radio2" value="white">
                        <label class="form-check-label" for="radio2">Белый</label>
                    </div>
                </div>

                <div class="mb-3 text-muted">
                    <h4 class="fs-5">Добавить фото</h4>
                    <input class="form-control" type="file" id="formFile1" name="photoProduct">
                </div>

                <button type="submit" id="btnSubmitForm" class="btn btn-primary me-2">Сохранить</button>

                <button type="button" id="btnSendFormManually" class="btn btn-primary">Сохранить-form-urlencoded</button>
            </form>
            <div><b>Отклик сервера: </b><span id="replyServer" class="fs-3"></span></div>
        </div>
    </div>
</div>

Вид элементов HTML формы на странице сайта:

HTML Forms на странице сайта

AJAX и Fetch API отправка данных методами POST, PUT

Отправляемые данные формы удобно формировать объектом типа FormData при помощи конструктора, принимающего в качестве параметра саму HTML-форму:
var formData = new FormData(myForm). Затем объект formData, содержащий все данные формы передается в функции JavaScript-интерфейсов фоновой отправки для передачи на сервер.

В настройках интерфейсов фоновой отправки данных при этом не указывается тип отправляемого контента, поскольку объект FormData подготавливает данные для типа кодировки multipart/form-data, как наиболее универсального Content-Type.

Методы отправки POST или PUT устанавливаются в значении опций интерфейсов AJAX и Fetch API, такой же выбранный метод устанавливается и для контроллера MVC в виде атрибута:
[HttpPost] или [HttpPut].

Отправка интерфейсом JavaScript Fetch API:
/**
 * Подготовка для отправки данных формы
 * @param myajax_this - текущий объект класса
 */
initHtmlForm(myajax_this: MyAjax) {
    // Регистрируем обработчик события 
    // отправки данных формы.
    const myForm = document.getElementById("formProduct") as HTMLFormElement;
    myForm?.addEventListener('submit', function (e) {
        // Отключаем обработку события браузером
        e.preventDefault();

        // Очищаем поле отклика сервера.
        myajax_this.replyServer("");

        // Получаем данные формы
        const formData = new FormData(this);

        // Валидация данных на стороне клиента
        const nameProduct = formData.get("nameProduct") as string;
        if (nameProduct.length == 0) {
            alert("Введите название товара.");
        }
        else {
            //myajax_this.formPostPut(formData);
            myajax_this.formPostPutAjax(formData);
        }
    });
}
/**
  * Отправка данных формы методами POST PUT
  * @param fd – входные данные формы
  */
 formPostPut(fd: FormData) {
     // Адрес отправки данных формы
     const url = '/html-form';

     // Настройка отправки
     const options = {
         method: 'POST', // 'PUT'
         body: fd
     };

    // Асинхронная отправка, основанная на Promise (Обещаниях).
     fetch(url, options)
         .then(response => response.text())
         .then(data => {
             // Обработка ответа сервера
             this.replyServer(data);
         })
         .catch(error => {
             // Обрабатываем ошибку
             console.error("error: " + error);
         });

 }
Отправка интерфейсом JavaScript AJAX:
/**
 * Отправка данных формы методами POST PUT
 * интерфейсом AJAX
 * @param fd - входные данные формы
 */
formPostPutAjax(fd: FormData) {
    // Адрес отправки данных формы
    const url = '/html-form';

    // Для исключения конфликтов this внутри объекта XMLHttpRequest.
    const _this = this;

    // Создаем объект AJAX XMLHttpRequest
    var xhr = new XMLHttpRequest();
    xhr.open('POST', url, true);
    // Возврат токена на сервер для проверки на
    // подделку запросов от стороннего сайта(XSRF или CSRF).
    xhr.setRequestHeader('RequestVerificationToken',
        fd.get('__RequestVerificationToken').toString());
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4 && xhr.status == 200) {
            // Получаем ответ сервера
            _this.replyServer(xhr.responseText);
        }
    };

    // Отправка данных формы
    xhr.send(fd);
}

Извлечение данных в приложении ASP.NET MVC

HTTP-методы на клиентской и серверной сторонах должны строго совпадать: отправляем POST – получаем POST. При получении данных в action-методах контроллеров ASP.NET MVC значения элементов формы автоматически привязываются к однотипным и одноименным параметрам, при этом названия переменных не чувствительны к регистру.

Переменные серверной стороны можно заключить в объектно-ориентированную оболочку: класс или структуру, тогда в качестве параметра рациональней использовать объект такого типа. При сопоставлении класса с извлекаемыми данными html формы достаточно наличие в этом классе неполного состава переменных: какие есть с совпадением имен, те и будут заполняться значениями.

По умолчанию контроллеры ASP.NET MVC настроены на получение данных из веб-форм, поэтому перед параметром action-метода атрибут [FromForm] может не указываться.
[HttpPost] - необходимый атрибут для получения данных, отправленных методом POST.
[ValidateAntiForgeryToken] – атрибут, требующий проверку подлинности запроса. При провале проверки сервер отправляет ошибку "HTTP 400 Bad Request".

C# код action-метода контроллера:
[Route("/html-form")]
[HttpPost]
//[HttpPut]
[ValidateAntiForgeryToken]
public IActionResult FormPostPut(RecvData recvData)
{
    // Прямой способ получения данных формы.
    //var httpRequest = HttpContext.Request;
    //var df = httpRequest.Form["nameProduct"].ToString();

    // Место валидации данных.
    if (recvData.NameProduct == null || recvData.NameProduct.Length > 250)
    {
        return Content("Incorrect data");
    }

    // Использование проверенных данных.

    // Отправка отклика успешности
    return Content("Ok");
}
Класс для хранения полученных данных формы:
public class RecvData
{
    public string? NameProduct { get; set; }
    public string? DescriptionProduct { get; set; }
    public string? Pack { get; set; }
    public string? Delivery { get; set; }
    public string? Color { get; set; }
    public IFormFile? PhotoProduct { get; set; }
}

Отправка данных типа application/x-www-form-urlencoded

Для специфических и небольших текстовых данных, в которых нет символов разделителей текстового потока:
=, &
можно экономично отправлять данные формы типом контента
application/x-www-form-urlencoded

Отправляемая строка в таком случае имеет вид
name1=value1& name2=value2& name3=value3 и т. д.
Например, при работе с интерфейсом JavaScript Fetch API и используя объект FormData можно формировать строку таким кодом:
var str = "nameProduct=" + fd.get("nameProduct") + "&descriptionProduct=" + fd.get("descriptionProduct")

Программный код на TypeScript отправки данных формы посредством вручную сформированной строки типа application/x-www-form-urlencoded:
/**
 * Отправка вручную сформированных данных формы 
 * @param fd
 */
 formManually(fd: FormData) {
     // Алрес отправки данных формы
     const url = '/html-form-manually';

     // Настройки отправки
     // Вариант ручного формирование отправки данных формы
     const options = {
         method: 'POST',
         headers: {
             // Заголовок для исключения фальшивых запросов 
             // от сторонних сайтов.
             'RequestVerificationToken': 
                fd.get('__RequestVerificationToken').toString(),
             'Content-Type': 'application/x-www-form-urlencoded'
         },
         // Формирование отправляемой строки.
         body: "nameProduct=" + fd.get("nameProduct") + 
               "&descriptionProduct=" + fd.get("descriptionProduct")
     };
     // Отправка данных, получение отклика сервера, обработка ошибок.
     fetch(url, options)
         .then(response => response.text())
         .then(data => {
             // Обрабатываем ответ сервера
             this.replyServer(data);
         })
         .catch(error => {
             // Обрабатываем ошибку
             console.error("error: " + error);
         });
 }