Giới thiệu
Thực ra phần này liên quan tới Backend nhiều hơn. Mình dùng .NET để Backend Server để gởi và nhận data từ browser.
Ví dụ
Tạo Project Web API, sau đó bạn thêm package Swagger
dotnet add package Swashbuckle.AspNetCore
Cấu hình ASP.NET Core
Bạn cần cấu hình dịch vụ trong file Program.cs:
- Bật CORS (Cross-Origin Resource Sharing) để cho phép frontend React gọi API.
- Cho phép phục vụ các Static Files (bao gồm ảnh đã upload).
- Cấu hình Swagger/OpenAPI.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseHttpsRedirection();
app.UseStaticFiles();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseCors("AllowSpecificOrigin");
app.UseAuthorization();
app.MapControllers();
app.Run();
builder.Services.AddSwaggerGen(): Dòng này đăng ký Swagger generator vào hệ thống Dependency Injection của ASP.NET Core. Swagger generator có nhiệm vụ đọc các Controller và Action của bạn, cùng với các Model, để tạo ra một API document theo định dạng OpenAPI
app.UseSwagger(): Dòng này thêm Swagger middleware vào pipeline xử lý HTTP request. Middleware này sẽ tạo ra endpoint swagger/v1/swagger.json
Định nghĩa Model
namespace ReactGettingStartedApi.Models;
public class AddressFormDto
{
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
}
public class UserFormDataDto
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public IFormFile? ProfileImage { get; set; }
public List<AddressFormDto> Addresses { get; set; } = new List<AddressFormDto>();
}
public class AddressDto
{
public int Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public string Country { get; set; }
}
public class UserDto
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public string? ProfileImagePath { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public List<AddressDto> Addresses { get; set; } = new List<AddressDto>();
}
InMemoryDataSource dùng để lưu tạm data, thay thế cho Database
using ReactGettingStartedApi.Models;
namespace ReactGettingStartedApi.Data;
public class InMemoryDataSource
{
private static List<UserDto> _users = new List<UserDto>();
private static int _nextUserId = 1;
private static int _nextAddressId = 1;
private static readonly object _lock = new object();
public static UserDto AddUser(UserFormDataDto userFormData, string? profileImagePath)
{
lock (_lock)
{
var newUser = new UserDto
{
Id = _nextUserId++,
FirstName = userFormData.FirstName,
LastName = userFormData.LastName,
Email = userFormData.Email,
Phone = userFormData.Phone,
ProfileImagePath = profileImagePath,
CreatedDate = DateTime.UtcNow,
ModifiedDate = DateTime.UtcNow
};
if (userFormData.Addresses != null)
{
foreach (var addressFormDto in userFormData.Addresses)
{
newUser.Addresses.Add(new AddressDto
{
Id = _nextAddressId++,
Street = addressFormDto.Street,
City = addressFormDto.City,
ZipCode = addressFormDto.ZipCode,
Country = addressFormDto.Country
});
}
}
_users.Add(newUser);
return newUser;
}
}
public static UserDto? GetUserById(int id)
{
lock (_lock)
{
return _users.FirstOrDefault(u => u.Id == id);
}
}
public static List<UserDto> GetAllUsers()
{
lock (_lock)
{
return [.. _users];
}
}
public static void ClearAllUsers()
{
lock (_lock)
{
_users.Clear();
_nextUserId = 1;
_nextAddressId = 1;
}
}
}Cú pháp [.. _users] là một tính năng mới trong C# được gọi là Collection Expressions (biểu thức tập hợp), được giới thiệu từ C# 12 (.NET 8).- Tạo một collection mới (ví dụ: List<T>, Array, Span<T>) một cách gọn gàng.
- Kết hợp các collection hiện có vào một collection mới.
Trong trường hợp return [.. _users];: [ ] biểu thị việc tạo một collection mới, cụ thể là một List<UserDto> (vì kiểu trả về là List<UserDto>).
.. là spread element. Nó có chức năng tương tự như spread operator (...) trong JavaScript: trải rộng các phần tử của một collection hiện có (_users) vào collection mới đang được tạo.
Tạo file Controller
using Microsoft.AspNetCore.Mvc;
using ReactGettingStartedApi.Data;
using ReactGettingStartedApi.Models;
namespace ReactGettingStartedApi.Controllers;
[Route("api/[controller]")]
[ApiController]
public class UsersDemoController : ControllerBase
{
private readonly IWebHostEnvironment _env;
public UsersDemoController(IWebHostEnvironment env)
{
_env = env;
}
[HttpPost("SubmitForm")]
public async Task<IActionResult> SubmitForm([FromForm] UserFormDataDto model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
string? profileImagePath = null;
if (model.ProfileImage != null)
{
try
{
var uploadsFolder = Path.Combine(_env.WebRootPath, "uploads", "temp");
Directory.CreateDirectory(uploadsFolder);
var uniqueFileName = Guid.NewGuid().ToString() + "_" + model.ProfileImage.FileName;
var filePath = Path.Combine(uploadsFolder, uniqueFileName);
using (var stream = new FileStream(filePath, FileMode.Create))
{
await model.ProfileImage.CopyToAsync(stream);
}
profileImagePath = $"/uploads/temp/{uniqueFileName}";
}
catch (Exception ex)
{
Console.WriteLine($"Error saving profile image: {ex.Message}");
return StatusCode(StatusCodes.Status500InternalServerError, new { Message = "Error saving profile image." });
}
}
var newUser = InMemoryDataSource.AddUser(model, profileImagePath);
return Ok(new { Message = "User data received and stored temporarily!", User = newUser });
}
[HttpGet("AllUsers")]
public IActionResult GetAllUsers()
{
var users = InMemoryDataSource.GetAllUsers();
return Ok(users);
}
[HttpGet("{id}")]
public IActionResult GetUserById(int id)
{
var user = InMemoryDataSource.GetUserById(id);
if (user == null)
{
return NotFound($"User with ID {id} not found.");
}
return Ok(user);
}
[HttpDelete("ClearAll")]
public IActionResult ClearAllUsers()
{
InMemoryDataSource.ClearAllUsers();
return Ok("All temporary user data cleared.");
}
}
Sửa lại hàm SubmitForm trong DynamicForm. Giả sử địa chỉ localhost là https://localhost:7152
const onSubmit: SubmitHandler<ComplexFormData> = async (data: ComplexFormData) => {
console.log("Form submitted:", data);
const profileImageFile = data.userInfo.profileImage?.[0];
if (profileImageFile) {
console.log("Image name:", profileImageFile.name);
console.log("Size of image:", profileImageFile.size, "bytes");
console.log("File type:", profileImageFile.type);
const formData = new FormData();
formData.append('firstName', data.userInfo.firstName);
formData.append('lastName', data.userInfo.lastName);
formData.append('email', data.userInfo.email);
formData.append('phone', data.userInfo.phone);
data.addresses.forEach((address, index) => {
formData.append(`addresses[${index}].street`, address.street);
formData.append(`addresses[${index}].city`, address.city);
formData.append(`addresses[${index}].zipCode`, address.zipCode);
formData.append(`addresses[${index}].country`, address.country);
});
if (profileImageFile) {
formData.append('profileImage', profileImageFile);
}
try {
const response = await fetch('https://localhost:7152/api/UsersDemo/SubmitForm', {
method: 'POST',
body: formData,
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
else{
const result = await response.json();
alert('Data was sent successfully and stored temporarily!');
console.log('Server response:', result);
}
} catch (error) {
console.error(error);
}
}
};
Bạn có thể thử các API khác với Swagger: https://localhost:7152/swagger/index.html
Nhận xét
Đăng nhận xét