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#.
Для отправки данных HTML-формой стандартно используются два метода POST и GET. Обмен сообщениями при помощи JavaScript не ограничивается двумя вышеуказанными методами отправки и может включать метод PUT.
Используя JavaScript для отправки данных форм, можно получать доступ к любым атрибутам и значениям элементов формы, осуществлять проверку на валидность и отправлять информацию на сервер требуемым методом без перезагрузки страницы.
HTTP-метод POST предназначен для отправки на сервер больших и разнообразных по составу порций данных, в том числе и двоичных, например: текстовые значения элементов HTML-форм для записи в базу данных, загрузка файловых байтов на сервер. Метод POST обычно служит для создания новых данных на сервере.
HTTP-метод GET предназначен для получения данных с сервера на основании сравнительно коротких параметров запроса. Например, отправка запроса с параметрами /html-form-get?length=120&width=80&height=60 может инициировать на сервере отправку клиенту результата вычисления объема.
Метод PUT схожий возможностями с POST, но более универсален: предназначен для обновления данных на сервере, но может использоваться и для их создания.
Данные при POST и PUT отправках добавляются к телу страницы после основного HTML-контента, при GET отправках параметры прикрепляются после URL страницы в виде строки запроса и их можно прочитать в строке адреса браузера.
Поскольку элемент формы Checkbox предполагает тип Boolean, то можно не определять атрибут value, в таком случае, при установке галочки, на сервер будет отправляться значение on, а при пустом Checkbox форма проигнорирует отправку данных элемента.
Элементы формы Radio группируются по одинаковому атрибуту name. Группу радиокнопок можно сравнить с перечисляемым типом Enum, на сервер отправляется значение только одного элемента группы, отмеченного как checked.
@Html.AntiForgeryToken() - программная вставка движка Razor для создания невидимого элемента input со значением уникальной метки текущей страницы.
Веб-страница index.cshtml с элементами формы:
Отправляемые данные формы удобно формировать объектом типа 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);
}
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
Отправляемая строка в таком случае имеет вид 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);
});
}
Исходник содержит код на C#, TypeScript, JavaScript, написан в MS Visual Studio 2022. Пример отправки методами POST, PUT данных формы, сформированных объектом FormData и вручную. Отправка Content-Type: multipart/form-data и application/x-www-form-urlencoded.