ChienDeepEyes.ResultLib
2.5.0
dotnet add package ChienDeepEyes.ResultLib --version 2.5.0
NuGet\Install-Package ChienDeepEyes.ResultLib -Version 2.5.0
<PackageReference Include="ChienDeepEyes.ResultLib" Version="2.5.0" />
<PackageVersion Include="ChienDeepEyes.ResultLib" Version="2.5.0" />
<PackageReference Include="ChienDeepEyes.ResultLib" />
paket add ChienDeepEyes.ResultLib --version 2.5.0
#r "nuget: ChienDeepEyes.ResultLib, 2.5.0"
#:package ChienDeepEyes.ResultLib@2.5.0
#addin nuget:?package=ChienDeepEyes.ResultLib&version=2.5.0
#tool nuget:?package=ChienDeepEyes.ResultLib&version=2.5.0
ResultLib - Result Pattern Library
Thư viện đóng gói Result pattern với xử lý MessageForUser và SystemError, tích hợp log hệ thống, global exception handler, và ErrorOr.
📦 Cài đặt
dotnet add package ResultLib
Hoặc reference trực tiếp:
dotnet add reference ../ResultLib/ResultLib.csproj
🚀 Bắt đầu nhanh
1. Configure Log (2 dòng ở Program.cs)
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Dùng ILoggerFactory (khuyên dùng) — log ra console / file / Seq / Elastic...
var loggerFactory = app.Services.GetRequiredService<ILoggerFactory>();
Log.Configure(loggerFactory);
Hoặc dùng EF Core để lưu log vào DB:
builder.Services.AddSingleton<ILogService, EfCoreLogService>();
var logService = app.Services.GetRequiredService<ILogService>();
Log.Configure(logService);
2. Bật Global Exception Handler (1 dòng)
// Chống unhandled exception — tự log + trả message chung cho user
app.UseResultLibExceptionHandler();
3. Dùng trong Controller
using ResultLib;
// Thành công
return Result.ResultOk;
return user.ToResultOk();
// Lỗi cho user (hiển thị trực tiếp)
return "Email không hợp lệ".ToResultErrorForUser();
// Lỗi hệ thống (log + message chung)
return Log.WriteErrorLogToResultSystem("Database timeout");
// Tự động xử lý: MessageForUser → show, không có prefix → log + message chung
return Log.ProcessAndToResultError(msg);
// Chuyển Result thành IActionResult (dùng trực tiếp trong controller)
return result.ToIActionResult();
Sử dụng ResultFilter (tùy chọn)
Thay vì gọi ToIActionResult() thủ công, bạn có thể dùng ResultFilter để tự động set status code từ Result:
// Trong Program.cs
builder.Services.AddResultFilter();
builder.Services.AddControllers(options => options.Filters.Add<ResultFilter>());
// Trong Controller — không cần gọi ToIActionResult()
[HttpGet("{id}")]
public Result GetUser(int id)
{
var user = _userService.GetUser(id);
if (user == null)
return Result.NotFound("Không tìm thấy người dùng");
return Result.Ok(user);
}
🎯 Core Concept
MessageForUser vs SystemError
| Loại Message | Mô tả | Ví dụ |
|---|---|---|
| MessageForUser | Lỗi an toàn, hiển thị cho user | "Email không hợp lệ".ToMessageForUser() |
| SystemError | Lỗi nội bộ, user thấy message chung + log ID | "DB timeout".ToUnexpectedError() |
Prefix System
[MessageForUser]— đánh dấu message dành cho user[MoreDetailLog]— thêm chi tiết log (ẩn với user, chỉ dùng để debug)
🔧 LogOptions — Tùy chỉnh hành vi log
Mặc định, chỉ log hệ thống. User errors (có prefix MessageForUser) không bị log.
// Log cả user error (nếu muốn)
Log.Configure(logService, new LogOptions { LogUserErrors = true });
Hoặc dùng ILoggerFactory:
Log.Configure(loggerFactory, new LogOptions { LogUserErrors = true });
📖 Cách sử dụng chi tiết
1. Kết quả thành công
return Result.ResultOk;
return user.ToResultOk();
return 42.ToResultOk();
2. Lỗi cho User
// Có prefix → user thấy message này, KHÔNG log DB
return "Email không được để trống".ToResultErrorForUser();
// Hoặc dùng prefix thủ công
return "Email không hợp lệ".ToMessageForUser().ToResultError();
3. Lỗi hệ thống
// Cách 1: Ghi log + trả message chung
return Log.WriteErrorLogToResultSystem("Database timeout");
// Cách 2: Ghi log riêng rồi tạo Result
var logId = Log.WriteErrorLog("Database timeout", new { userId });
return logId.ToResultErrorForSystem();
// User thấy: "Đã xảy ra lỗi. Vui lòng thử lại sau [#a3f8c2]"
4. ProcessError — Tự động phân loại
// Tự động: MessageForUser → show, không có prefix → log + message chung
string msg = _service.DoSomething();
if (msg.HasError())
return Log.ProcessAndToResultError(msg);
5. Meaningful Log Messages
// Log có context để debug dễ hơn
var msg = "User not found"
.ToMeaningfulMessage("UserService.GetById", new { userId });
return Log.ProcessAndToResultError(msg);
// Combo: Message cho user + Log detail
return Log.ProcessAndToResultError(
"Không tìm thấy người dùng".ToUserMeaningfulMessage("UserService.GetById", new { userId })
);
6. DomainException
// Ném trong service/business logic
throw new DomainException("Không tìm thấy đơn hàng");
// Global exception handler sẽ bắt → trả 400 + message cho user
7. Tích hợp với ErrorOr
using ErrorOr;
public ErrorOr<User> GetUser(int id)
{
if (id <= 0)
return Error.Validation("User.InvalidId", "ID phải lớn hơn 0".ToMessageForUser());
return user;
}
// Trong Controller — tự động xử lý MessageForUser / SystemError
[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
var result = _userService.GetUser(id).ToResult();
return result.ToIActionResult();
}
🛠️ Các Extension Methods chính
Kết quả & Chuyển đổi
| Method | Mô tả |
|---|---|
obj.ToResultOk() |
Trả về Result thành công (HTTP 200) |
msg.ToResultError() |
Strip prefix nếu có, trả Result error (HTTP 400) |
msg.ToResultErrorForUser() |
Thêm prefix MessageForUser, trả Result error |
msg.ToResultErrorForSystem() |
Trả Result error với message chung + log ID |
result.ToIActionResult() |
Chuyển Result thành IActionResult (giữ đủ trường, status theo Result.StatusCode) |
ResultFilter (tùy chọn)
| Method / Class | Mô tả |
|---|---|
services.AddResultFilter() |
Đăng ký ResultFilter tự động set status code từ Result |
ResultFilter |
IAsyncResultFilter — tự động set Response.StatusCode khi action trả Result |
MessageHelper
| Method | Mô tả |
|---|---|
msg.ToMessageForUser() |
Thêm prefix [MessageForUser] |
msg.IsMessageForUser() |
Kiểm tra có prefix MessageForUser không |
msg.GetMessageForUser() |
Lấy message cho user (bỏ prefix) |
msg.ToGeneralSystemErrorMessage() |
Tạo message chung kèm log ID |
msg.ToMeaningfulMessage(prefix, params) |
Thêm chi tiết log vào message |
msg.ToUserMeaningfulMessage(prefix, params) |
Combo: prefix user + log detail |
Exception Extensions
| Method | Mô tả |
|---|---|
ex.ToUnexpectedError() |
Tạo Error.Unexpected với full stack trace |
ex.ToFailureError() |
Tạo Error.Failure với full stack trace |
Log Static Class
| Method | Mô tả |
|---|---|
Log.Configure(ILogService) |
Đăng ký log service |
Log.Configure(ILoggerFactory) |
Đăng ký với ILogger (dễ dùng nhất) |
Log.WriteErrorLog(msg) |
Ghi log, trả log ID |
Log.ProcessError(msg) |
Tự động phân loại + log |
Log.ProcessAndToResultError(msg) |
ProcessError + trả Result error |
Log.WriteErrorLogToResultSystem(msg) |
Log + trả Result error hệ thống |
Log.WriteErrorLogToResultUser(msg) |
Log + trả Result error user (chỉ log khi LogUserErrors=true) |
Tích hợp
| Method | Mô tả |
|---|---|
ErrorOr<T>.ToResult() |
Chuyển ErrorOr<T> sang Result |
ErrorOr<T>.ToResult(successMessage) |
Chuyển ErrorOr<T> sang Result với custom message thành công (lưu ý: bỏ qua data T) |
ErrorOr<Success>.ToResult() |
Chuyển ErrorOr<Success> sang Result |
ValidationFailure.ToResult() |
Chuyển ValidationFailure sang Result |
📁 Cấu trúc files
ResultLib/
├── Core/
│ ├── Result.cs # Core Result class
│ ├── ResultExtensions.cs # ToResultOk, ToResultError...
│ └── DomainException.cs # Domain exception cho business errors
├── Logging/
│ ├── LogService.cs # ILogService + Log static class
│ ├── LoggerLogService.cs # ILogger-based implementation
│ ├── LogOptions.cs # Log configuration options
│ └── MessageHelper.cs # MessageForUser / SystemError helpers
├── Extensions/
│ ├── ErrorOrIntegration.cs # ErrorOr → Result mapping
│ ├── ExceptionExtensions.cs # ToUnexpectedError, ToFailureError
│ ├── FluentValidationIntegration.cs # ValidationFailure → Result
│ ├── ResultFilter.cs # IAsyncResultFilter — tự động set status code từ Result
│ ├── ResultLibApplicationBuilderExtensions.cs # UseResultLibExceptionHandler
│ ├── ResultLibServiceCollectionExtensions.cs # AddResultLibExceptionHandler, AddResultFilter
│ ├── ResultToIActionResultExtensions.cs # Result → IActionResult
│ └── ExceptionHandler/
│ └── ResultLibExceptionHandler.cs # Global exception handler (IExceptionHandler)
└── ResultLib.csproj
🔄 Flow xử lý Error
Error xảy ra
│
▼
Log.ProcessError(msg) / Log.ProcessAndToResultError(msg)
│
├──► msg có [MessageForUser]?
│ │
│ YES ──► Trả message cho user (KHÔNG log DB)
│ │ VD: "Email không hợp lệ"
│ │
│ NO ───► Ghi log DB + Trả message chung cho user
│ VD: "Đã xảy ra lỗi. Vui lòng thử lại sau [#123]"
│
▼
Result { IsError, StatusCode, ErrorMessage }
Global Exception Handler Flow
Unhandled Exception
│
▼
ResultLibExceptionHandler (IExceptionHandler)
│
▼
Log.ProcessError(exception details)
│
├──► System error → log DB + trả message chung
│
▼
HTTP 500 + Result error
🗃️ SQL Script tạo bảng LogError (nếu dùng EF Core)
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'LogError')
BEGIN
CREATE TABLE [dbo].[LogError]
(
[Id] BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
[CreateDate] DATETIME NOT NULL DEFAULT (GETDATE()),
[LogContent] NVARCHAR(4000) NOT NULL DEFAULT '',
[TypeLog] TINYINT NOT NULL DEFAULT(0), -- 0: System, 1: User
[IsProcessed] BIT NOT NULL DEFAULT(0),
[ProcessContent] NVARCHAR(MAX) NULL
)
END
🌐 Cấu hình Đa ngôn ngữ (i18n) với ResultLibOptions
ResultLib cung cấp cơ chế delegate toàn cục để ứng dụng tùy biến thông báo lỗi hệ thống và thông báo thành công mặc định (tích hợp mượt mà với IStringLocalizer hoặc đa ngôn ngữ theo từng request):
// Trong Program.cs / Startup
using ResultLib.Core;
// Đổi thông báo lỗi hệ thống chung (khi có Exception / lỗi không show chi tiết cho user)
ResultLibOptions.GetGeneralErrorMessage = () => localizer["GeneralErrorMessage"].Value;
// Đổi thông báo thành công mặc định cho ErrorOr<Success>.ToResult()
ResultLibOptions.GetDefaultSuccessMessage = () => localizer["SuccessMessage"].Value;
🛡️ Hằng số Validation & FluentValidation Extensions
1. ValidationConstants
Cung cấp Regex và message mặc định chuẩn hóa cho dự án:
using ResultLib.Utils;
// Regex mật khẩu mạnh (tối thiểu 8 ký tự, 1 thường, 1 số, 1 ký tự đặc biệt)
var passwordRegex = ValidationConstants.Password.StrongRegex;
// Regex số điện thoại Việt Nam (0xxxxxxxxx hoặc +84xxxxxxxxx)
var phoneRegex = ValidationConstants.PhoneNumber.VietnamRegex;
// Độ dài tối đa email
var maxEmail = ValidationConstants.Email.MaxLength; // 100
2. Tiện ích mở rộng FluentValidation
Dùng trực tiếp trong các class AbstractValidator<T>:
using FluentValidation;
using ResultLib.Extensions;
public class RegisterValidator : AbstractValidator<RegisterRequest>
{
public RegisterValidator()
{
// Kiểm tra mật khẩu mạnh với message chuẩn mặc định
RuleFor(x => x.Password).MatchesStrongPassword();
// Hoặc truyền message đa ngôn ngữ tùy biến
RuleFor(x => x.Password).MatchesStrongPassword(localizer["PasswordWeak"].Value);
// Kiểm tra số điện thoại Việt Nam
RuleFor(x => x.PhoneNumber).MatchesVietnamPhoneNumber();
}
}
Developed with ❤️ for robust .NET applications.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- ErrorOr (>= 2.1.1)
- FastEndpoints (>= 8.3.0)
- FluentValidation (>= 12.1.1)
- Microsoft.AspNetCore.OpenApi (>= 10.0.12)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.