Hướng dẫn: Gọi Web API ASP.NET Core bằng JavaScript
Bởi Rick Anderson
Hướng dẫn này trình bày cách gọi web API ASP.NET Core bằng JavaScript, sử dụng Fetch API.
Yêu cầu tiên quyết
- Hoàn thành Hướng dẫn: Tạo web API
- Quen thuộc với CSS, HTML và JavaScript
Gọi Web API bằng JavaScript
Trong phần này, bạn sẽ thêm một trang HTML chứa các form để tạo và quản lý các mục to-do. Các event handler (trình xử lý sự kiện) được gắn vào các phần tử trên trang. Các event handler tạo ra các HTTP request đến các action method của web API. Hàm fetch của Fetch API khởi tạo mỗi HTTP request.
Hàm fetch trả về một đối tượng Promise, chứa một HTTP response được biểu diễn dưới dạng đối tượng Response. Một pattern (mẫu) phổ biến là trích xuất response body JSON bằng cách gọi hàm json trên đối tượng Response. JavaScript cập nhật trang với các chi tiết từ response của web API.
Lời gọi fetch đơn giản nhất chấp nhận một tham số duy nhất đại diện cho route. Tham số thứ hai, gọi là đối tượng init, là tùy chọn. init được dùng để cấu hình HTTP request.
- Cấu hình ứng dụng để phục vụ file tĩnh (serve static files) và bật default file mapping. Code cần thêm trong
Program.cs:
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
var app = builder.Build();
if (builder.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();- Tạo thư mục wwwroot trong thư mục gốc của dự án.
- Tạo thư mục css bên trong thư mục wwwroot.
- Tạo thư mục js bên trong thư mục wwwroot.
- Thêm file HTML tên
index.htmlvào thư mục wwwroot. Thay thế nội dungindex.htmlbằng markup sau:
```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>To-do CRUD</title> <link rel="stylesheet" href="css/site.css" /> </head> <body> <h1>To-do CRUD</h1> <h3>Add</h3> <form action="javascript:void(0);" method="POST" onsubmit="addItem()"> <input type="text" id="add-name" placeholder="New to-do"> <input type="submit" value="Add"> </form>
<div id="editForm"> <h3>Edit</h3> <form action="javascript:void(0);" onsubmit="updateItem()"> <input type="hidden" id="edit-id"> <input type="checkbox" id="edit-isComplete"> <input type="text" id="edit-name"> <input type="submit" value="Save"> <a onclick="closeInput()" aria-label="Close">✖</a> </form> </div>
<p id="counter"></p>
<table> <tr> <th>Is Complete?</th> <th>Name</th> <th></th> <th></th> </tr> <tbody id="todos"></tbody> </table>
<script src="js/site.js" asp-append-version="true"></script> <script type="text/javascript"> getItems(); </script> </body> </html> ```
- Thêm file CSS tên
site.cssvào thư mục wwwroot/css. Thay thế nội dungsite.cssbằng các style sau:
```css input[type='submit'], button, [aria-label] { cursor: pointer; }
#editForm { display: none; }
table { font-family: Arial, sans-serif; border: 1px solid; border-collapse: collapse; }
th { background-color: #f8f8f8; padding: 5px; }
td { border: 1px solid; padding: 5px; } ```
- Thêm file JavaScript tên
site.jsvào thư mục wwwroot/js. Thay thế nội dungsite.jsbằng code sau:
```javascript const uri = 'api/todoitems'; let todos = [];
function getItems() { fetch(uri) .then(response => response.json()) .then(data => _displayItems(data)) .catch(error => console.error('Unable to get items.', error)); }
function addItem() { const addNameTextbox = document.getElementById('add-name');
const item = { isComplete: false, name: addNameTextbox.value.trim() };
fetch(uri, { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(item) }) .then(response => response.json()) .then(() => { getItems(); addNameTextbox.value = ''; }) .catch(error => console.error('Unable to add item.', error)); }
function deleteItem(id) { fetch(${uri}/${id}, { method: 'DELETE' }) .then(() => getItems()) .catch(error => console.error('Unable to delete item.', error)); }
function displayEditForm(id) { const item = todos.find(item => item.id === id);
document.getElementById('edit-name').value = item.name; document.getElementById('edit-id').value = item.id; document.getElementById('edit-isComplete').checked = item.isComplete; document.getElementById('editForm').style.display = 'block'; }
function updateItem() { const itemId = document.getElementById('edit-id').value; const item = { id: parseInt(itemId, 10), isComplete: document.getElementById('edit-isComplete').checked, name: document.getElementById('edit-name').value.trim() };
fetch(${uri}/${itemId}, { method: 'PUT', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(item) }) .then(() => getItems()) .catch(error => console.error('Unable to update item.', error));
closeInput();
return false; }
function closeInput() { document.getElementById('editForm').style.display = 'none'; }
function _displayCount(itemCount) { const name = (itemCount === 1) ? 'to-do' : 'to-dos';
document.getElementById('counter').innerText = ${itemCount} ${name}; }
function _displayItems(data) { const tBody = document.getElementById('todos'); tBody.innerHTML = '';
_displayCount(data.length);
const button = document.createElement('button');
data.forEach(item => { let isCompleteCheckbox = document.createElement('input'); isCompleteCheckbox.type = 'checkbox'; isCompleteCheckbox.disabled = true; isCompleteCheckbox.checked = item.isComplete;
let editButton = button.cloneNode(false); editButton.innerText = 'Edit'; editButton.setAttribute('onclick', displayEditForm(${item.id}));
let deleteButton = button.cloneNode(false); deleteButton.innerText = 'Delete'; deleteButton.setAttribute('onclick', deleteItem(${item.id}));
let tr = tBody.insertRow();
let td1 = tr.insertCell(0); td1.appendChild(isCompleteCheckbox);
let td2 = tr.insertCell(1); let textNode = document.createTextNode(item.name); td2.appendChild(textNode);
let td3 = tr.insertCell(2); td3.appendChild(editButton);
let td4 = tr.insertCell(3); td4.appendChild(deleteButton); });
todos = data; } ```
Có thể cần thay đổi cài đặt launch settings của dự án ASP.NET Core để kiểm thử trang HTML cục bộ:
- Mở Properties\launchSettings.json.
- Xóa thuộc tính
launchUrlđể buộc ứng dụng mở tạiindex.html— file mặc định của dự án.
Mẫu này gọi tất cả các phương thức CRUD của web API. Dưới đây là giải thích các request web API.
Lấy danh sách các mục to-do
Trong code sau, một HTTP GET request được gửi đến route api/todoitems:
fetch(uri)
.then(response => response.json())
.then(data => _displayItems(data))
.catch(error => console.error('Unable to get items.', error));Khi web API trả về mã trạng thái thành công, hàm _displayItems được gọi. Mỗi mục to-do trong tham số mảng được chấp nhận bởi _displayItems được thêm vào một bảng với các nút Edit và Delete. Nếu request web API thất bại, một lỗi được ghi vào console của trình duyệt.
Thêm một mục to-do
Trong code sau:
- Một biến
itemđược khai báo để xây dựng biểu diễn object literal của mục to-do. - Một Fetch request được cấu hình với các tùy chọn sau:
method— chỉ định action verb HTTP POST.body— chỉ định biểu diễn JSON của request body. JSON được tạo ra bằng cách truyền object literal được lưu trongitemvào hàm JSON.stringify.headers— chỉ định các HTTP request headerAcceptvàContent-Type. Cả hai header đều được đặt thànhapplication/jsonđể chỉ định media type được nhận và gửi tương ứng.- Một HTTP POST request được gửi đến route api/todoitems.
function addItem() {
const addNameTextbox = document.getElementById('add-name');
const item = {
isComplete: false,
name: addNameTextbox.value.trim()
};
fetch(uri, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(item)
})
.then(response => response.json())
.then(() => {
getItems();
addNameTextbox.value = '';
})
.catch(error => console.error('Unable to add item.', error));
}Khi web API trả về mã trạng thái thành công, hàm getItems được gọi để cập nhật bảng HTML. Nếu request web API thất bại, một lỗi được ghi vào console của trình duyệt.
Cập nhật một mục to-do
Cập nhật mục to-do tương tự như thêm mục; tuy nhiên, có hai điểm khác biệt đáng kể:
- Route được thêm hậu tố là định danh duy nhất của mục cần cập nhật. Ví dụ: api/todoitems/1.
- Action verb HTTP là PUT, được chỉ định bởi tùy chọn
method.
fetch(`${uri}/${itemId}`, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(item)
})
.then(() => getItems())
.catch(error => console.error('Unable to update item.', error));Xóa một mục to-do
Để xóa một mục to-do, đặt tùy chọn method của request thành DELETE và chỉ định định danh duy nhất của mục trong URL.
fetch(`${uri}/${id}`, {
method: 'DELETE'
})
.then(() => getItems())
.catch(error => console.error('Unable to delete item.', error));Tiến đến hướng dẫn tiếp theo để học cách tạo trang trợ giúp web API.