Chuyển đến nội dung chính

React Hook Form: Xây dựng form component - Part 5

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

Bài đăng phổ biến từ blog này

[ASP.NET MVC] Authentication và Authorize

Một trong những vấn đề bảo mật cơ bản nhất là đảm bảo những người dùng hợp lệ truy cập vào hệ thống. ASP.NET đưa ra 2 khái niệm: Authentication và Authorize Authentication xác nhận bạn là ai. Ví dụ: Bạn có thể đăng nhập vào hệ thống bằng username và password hoặc bằng ssh. Authorization xác nhận những gì bạn có thể làm. Ví dụ: Bạn được phép truy cập vào website, đăng thông tin lên diễn đàn nhưng bạn không được phép truy cập vào trang mod và admin.

ASP.NET MVC: Cơ bản về Validation

Validation (chứng thực) là một tính năng quan trọng trong ASP.NET MVC và được phát triển trong một thời gian dài. Validation vắng mặt trong phiên bản đầu tiên của asp.net mvc và thật khó để tích hợp 1 framework validation của một bên thứ 3 vì không có khả năng mở rộng. ASP.NET MVC2 đã hỗ trợ framework validation do Microsoft phát triển, tên là Data Annotations. Và trong phiên bản 3, framework validation đã hỗ trợ tốt hơn việc xác thực phía máy khách, và đây là một xu hướng của việc phát triển ứng dụng web ngày nay.

Tổng hợp một số kiến thức lập trình về Amibroker

Giới thiệu về Amibroker Amibroker theo developer Tomasz Janeczko được xây dựng dựa trên ngôn ngữ C. Vì vậy bộ code Amibroker Formula Language sử dụng có syntax khá tương đồng với C, ví dụ như câu lệnh #include để import hay cách gói các object, hàm trong các block {} và kết thúc câu lệnh bằng dấu “;”. AFL trong Amibroker là ngôn ngữ xử lý mảng (an array processing language). Nó hoạt động dựa trên các mảng (các dòng/vector) số liệu, khá giống với cách hoạt động của spreadsheet trên excel.