@libs-ui/services-base-request-abstract
v0.2.356-7
Published
Abstract base class cung cấp toàn bộ infrastructure cho HTTP requests trong Angular: GET, POST, PUT, PATCH, DELETE, file upload với progress tracking, response normalization, error handling tự động, caching với IndexedDB và page zero conversion.
Downloads
740
Readme
Base Request Abstract Service
Abstract base class cung cấp toàn bộ infrastructure cho HTTP requests trong Angular: GET, POST, PUT, PATCH, DELETE, file upload với progress tracking, response normalization, error handling tự động, caching với IndexedDB và page zero conversion.
Tính năng
- ✅ HTTP methods đầy đủ: GET, POST, PUT, PATCH, DELETE
- ✅ URL-encoded form:
postUrlEndCode,patchUrlEndCode(content-typeapplication/x-www-form-urlencoded) - ✅ File upload với progress tracking qua XHR native (
sendWithFile) - ✅ Response normalization tự động — hỗ trợ nhiều backend format
- ✅ Pagination tự động từ nhiều format (paging, Spring pagination, cursor-based)
- ✅ Error handling: 401 → auto redirect, phân biệt 4xx vs 5xx
- ✅ Caching với IndexedDB (
cacheIndexDB) — phân biệt theo user - ✅ Page zero conversion (
isStartZeroPage) — UI 1-based ↔ API 0-based
Cài đặt
npm install @libs-ui/services-base-request-abstractImport
import { LibsUiBaseRequestAbstractService } from '@libs-ui/services-base-request-abstract';Cách sử dụng
Bước 1: Extend và implement 3 abstract methods bắt buộc
import { Injectable } from '@angular/core';
import { HttpHeaders, HttpParams } from '@angular/common/http';
import { Router } from '@angular/router';
import { LibsUiBaseRequestAbstractService } from '@libs-ui/services-base-request-abstract';
import { UtilsHttpParamsRequest } from '@libs-ui/utils';
@Injectable({ providedIn: 'root' })
export class ApiService extends LibsUiBaseRequestAbstractService {
protected override baseUrl = 'https://api.example.com';
constructor(private router: Router) {
super();
this.keyCacheUniqueByIdUserLogin = localStorage.getItem('userId') || '';
}
protected getOptions<T>(params: UtilsHttpParamsRequest<T>, contentType?: string) {
const token = localStorage.getItem('accessToken');
return {
headers: new HttpHeaders({
'Content-Type': contentType || 'application/json',
...(token ? { Authorization: `Bearer ${token}` } : {}),
}),
params,
};
}
protected redirectToLogin(): void {
this.router.navigate(['/login']);
}
protected replaceURLByPattern<T>(url: string, params: UtilsHttpParamsRequest<T>) {
let resultUrl = url;
const pattern = /:([\w]+)/g;
let match;
while ((match = pattern.exec(url)) !== null) {
const key = match[1];
if (params.has(key)) {
resultUrl = resultUrl.replace(`:${key}`, params.get(key)!);
params = params.delete(key);
}
}
return { url: resultUrl, params };
}
}Bước 2: Tạo feature service kế thừa ApiService
@Injectable({ providedIn: 'root' })
export class UserService extends ApiService {
getUsers(params?: { page?: number; size?: number }) {
return this.get<User[]>('/users', new UtilsHttpParamsRequest(undefined, params));
}
getUserById(id: string) {
return this.get<User>('/users/:id', new UtilsHttpParamsRequest(undefined, { id }));
}
createUser(body: CreateUserDto) {
return this.post<User>('/users', body);
}
updateUser(id: string, body: UpdateUserDto) {
return this.put<User>('/users/:id', body, new UtilsHttpParamsRequest(undefined, { id }));
}
deleteUser(id: string) {
return this.delete<void>('/users/:id', new UtilsHttpParamsRequest(undefined, { id }));
}
}File Upload với Progress Tracking
uploadAvatar(userId: string, file: File, onProgress?: (percent: number) => void) {
return this.sendWithFile<{ url: string }>(
'/users/:userId/avatar',
new UtilsHttpParamsRequest(undefined, { userId }),
{
method: 'POST',
bodyData: { file },
keepFileOfBody: true,
processUpload: onProgress ? ({ percent }) => onProgress(percent) : undefined,
}
);
}Caching với IndexedDB
getUsersCached(params?: any) {
const observable = this.get<User[]>('/users', new UtilsHttpParamsRequest(undefined, params));
return this.cacheIndexDB(observable, 'users-list', [params], 5 * 60 * 1000);
}
// Xóa cache sau mutation
async createUser(body: CreateUserDto) {
const result = await lastValueFrom(this.post<User>('/users', body));
await this.deleteCacheKeyStartWidth('users-list');
return result;
}API
Abstract Methods (bắt buộc implement)
| Method | Returns | Mô tả |
| ----------------------------------------- | --------------------- | ------------------------------------------------ |
| getOptions<T>(httpParams, contentType?) | { headers, params } | Inject auth token, Content-Type vào mỗi request |
| redirectToLogin() | void | Được gọi khi nhận 401 Unauthorized |
| replaceURLByPattern<T>(url, params) | { url, params } | Thay thế URL dynamic segments (:id, :userId) |
Public Methods
| Method | Returns | Mô tả |
| --------------------------------------------------- | ------------------------------ | ------------------------------------ |
| get<T, P>(path, params?) | Observable<IHttpResponse<T>> | HTTP GET |
| post<T, P, B>(path, body, params?) | Observable<IHttpResponse<T>> | HTTP POST |
| postUrlEndCode<T, P, B>(path, body, params?) | Observable<IHttpResponse<T>> | POST URL-encoded form |
| put<T, P, B>(path, body, params?) | Observable<IHttpResponse<T>> | HTTP PUT |
| patch<T, P>(path, body, params?) | Observable<IHttpResponse<T>> | HTTP PATCH |
| patchUrlEndCode<T, P>(path, body, params?) | Observable<IHttpResponse<T>> | PATCH URL-encoded form |
| delete<T, P>(path, params?) | Observable<IHttpResponse<T>> | HTTP DELETE |
| sendWithFile<T, P, B>(path, params, args) | Observable<IHttpResponse<T>> | Upload file với progress |
| cacheIndexDB<T>(obs, key, params, time?, reload?) | Observable<IHttpResponse<T>> | Cache IndexedDB ✅ |
| deleteCacheKeyStartWidth(key) | Promise<void> | Xóa cache theo prefix |
| cacheResponseData<T>(...) | Observable<IHttpResponse<T>> | Deprecated — dùng cacheIndexDB |
Protected Properties (override trong subclass)
| Property | Type | Default | Mô tả |
| ----------------------------- | ---------------------- | ----------- | ------------------------------ |
| baseUrl | string | '' | Base URL của API |
| keyCacheUniqueByIdUserLogin | string | '' | User ID suffix cho cache key |
| ignoreRedirect401 | boolean? | undefined | Tắt auto redirect khi 401 |
| isStartZeroPage | boolean | false | Convert page 1-based → 0-based |
| observeResponse | 'response' \| 'body' | 'body' | Mode nhận HTTP response |
Types
interface IHttpResponse<T> {
code: number;
message?: string;
data?: T;
paging?: IPaging;
feCloneCode?: any;
statusCodeHttp?: number;
}
interface IPaging {
page?: number;
per_page?: number;
total_items?: number;
total_pages?: number;
before?: string;
after?: string;
previous?: string;
next?: string;
}
interface IHttpProcessUpload {
loaded: number;
total: number;
percent: number;
}Lưu ý quan trọng
- Không inject trực tiếp — class là abstract, phải extend
- 3 abstract methods phải implement:
getOptions,redirectToLogin,replaceURLByPattern cacheIndexDB>cacheResponseData—cacheResponseDatađã deprecatedkeyCacheUniqueByIdUserLogin— set user ID để tránh cache conflict giữa các usersendWithFiledùng XHR native — không phải Angular HttpClient (để có progress events)- Response normalization hỗ trợ nhiều backend format: REST standard, Spring Boot, cursor-based pagination
