thảo luận Best practice cho việc throw exception?

error là lỗi của người dùng/dev, chỉ ảnh hưởng đến 1 số lượng người dùng nhất định
execption/panic là lỗi hệ thống, ảnh hưởng đến toàn bộ người dùng
vd về lỗi mở file:
  • file không tồn tại là file do người dùng/dev nhập sai đường dẫn: lỗi người dùng (error)
  • ổ cứng lăn ra hỏng: lỗi hệ thống (exeception)
Một số ngôn ngữ có vẻ được thiết kế dành cho Desktop app (1 người dùng) nên gộp 2 làm 1 cho tiện, nhưng mang lên web thì như mớ bòng bong, không biết đâu mà lần.
 
Thế bây giờ findUserById, trong đó nếu theo flow thì bắt buộc id này phải tồn tại (do flow từ client truyền lên), nếu id không get được user thì nó là error hay exception ? Thím nào định nghĩa giúp mình câu trả lời với ?
Còn mình hay throw các customize exception và global handle, từ đó build các http code và error trả về client, ngoài ra dễ monitor, logging nữa ./.

Gửi từ Xiaomi MI 8 bằng vozFApp
 
Thế bây giờ findUserById, trong đó nếu theo flow thì bắt buộc id này phải tồn tại (do flow từ client truyền lên), nếu id không get được user thì nó là error hay exception ? Thím nào định nghĩa giúp mình câu trả lời với ?
Còn mình hay throw các customize exception và global handle, từ đó build các http code và error trả về client, ngoài ra dễ monitor, logging nữa ./.

Gửi từ Xiaomi MI 8 bằng vozFApp
Cái findUserById trả về user hoặc là null, chứ không ném exception.

vd sử dụng Result pattern. Có thể tạo UserNotFoundResult để khỏi lặp lại cái lỗi thông báo.

C#:
public async Task<Result<UserDto>> Handle(UpdateUserCommand request, CancellationToken cancellationToken)
    {

        var user = await _userRepository.FindAsync(request.Id, cancellationToken: cancellationToken);

        if (user == null) return new NotFoundResult<UserDto>("User with id {id} is not found.", request.Id);


        user.SetFullname(request.Fullname);

        _userRepository.Update(user);

        await _userRepository.SaveEntityAsync(cancellationToken);


        return new SuccessResult<UserDto>(user.ProjectTo());

    }
 
Cái findUserById trả về user hoặc là null, chứ không ném exception.

vd sử dụng Result pattern. Có thể tạo UserNotFoundResult để khỏi lặp lại cái lỗi thông báo.

C#:
public async Task<Result<UserDto>> Handle(UpdateUserCommand request, CancellationToken cancellationToken)
    {

        var user = await _userRepository.FindAsync(request.Id, cancellationToken: cancellationToken);

        if (user == null) return new NotFoundResult<UserDto>("User with id {id} is not found.", request.Id);


        user.SetFullname(request.Fullname);

        _userRepository.Update(user);

        await _userRepository.SaveEntityAsync(cancellationToken);


        return new SuccessResult<UserDto>(user.ProjectTo());

    }
tại sao lại trả về null? k dùng exception có lợi gì đâu, toàn rãnh chế cho phức tạp ra
 
Xanh thì đúng rồi, nhưng cũng không cần quá chi tiết, có thể gom thành những nhóm lỗi chung kiểu DataInvalidException, AccountBussinessException, khi throw ra thì sẽ phân biệt chi tiết hơn bằng error code và message
Còn đỏ thì chưa đúng ý mình. trường hợp các exception mình định nghĩa riêng kia thì cứ throw ra như bình thường, còn các exception mà ko thuộc những cái mình định nghĩa, mình ko control được thì nên throw dạng lỗi chung

Anw, chủ đề này khá hay, cắm cọc chờ học hỏi từ cao nhân
Throw Exception là để mình kiểm soát lỗi một cách chủ động. Ví dụ nếu làm webapps, ngoại trừ các HTTP code status thì còn một đống lỗi có thể đến từ lớp logic do code mình viết ra thêm, SQL error,....

Còn vấn đề là tại sao phải tuân thủ Exception hợp lí ? Vì nếu throw một cách chung chung thì khi traceback nhìn log từ console sẽ rất là lú và không có tính logic để suy luận debug.

Khi nào cần Exception ? Khi mà trong logic có một số trường hợp nhất định luôn gặp phải và cách hiển thị message từ hệ thống quá chung chung nên cần verify ra. Ví dụ: System báo running error xxx nhưng có thể chỉ rõ ra record nào bị lỗi, lỗi ở vị trí nào, cần sửa như thế nào,....ra message box để tăng hiệu quả của message và tăng trải nghiệm của người dùng.
 
ý em là
Java:
class UserService {
    public User getUserInfo(int id) {
        // logic
        if (...) {
             throw UserNotFoundException("user not found");
        }

    }
}

Java:
class UserHttpController {
    private UserService userService;

    @GetMapping("/users/{id}")
    public Response<User> getUser(@RequestParam int id) {
 try {
    User user = userService.getUser(id);
    return Response(Http.Ok, ...);
 } catch (UserNotFound e) {
    return Response(Http.NotFound, ...);

 } catch (Exception e) {
    return Response(Http.Internal, ...);
   }
}

}

hay là các bác dùng kiểu gì khác kiểu này để tối ưu code hơn vậy
Spring thì dùng @ControllerAdvice để handle exception và trả về http status theo từng loại exception, Quarkus có ExceptionMapper, nói chung các framework nó đều có phần xử lý này rồi không if else không try catch.
 
Tôi thấy code findId ra exception nhiều chỗ làm mà nhỉ. Ruby on Rails chẳng hạn. trả về 404 not found
 
https://www.on-time.com/ddj0011.htm
Lợi ích của exception đủ lớn để người ta tìm cách mang lên C. Thế nên chê exception thì xiaomi quá
Each function returns a value indicating success or failure. However, with a nontrivial function call hierarchy, this approach clutters the code significantly. Every function must check the return code of every function call it makes and take care of errors. In most cases, the function will merely pass any errors back up to its caller
 
Em hay làm như này. Tạo một custom exception class extend Exception class của Java, sau đó global handle trả ra client luôn như này
Xem tệp đính kèm 1986794

Hay là làm theo kiểu check if-else như này các bác
Xem tệp đính kèm 1986798

Cái này trừ khi BA kieu làm. Tớ toàn trả về empty hoăc null. Exeption có 3 chiến lược . Remove error from exist code. Masking exception. Aggeration exception. Có nhiều code delete mà đi check có mới delete, ko thì đi show exeption nó hài hài

Sent using vozFApp
 
Mấy project mình làm qua thương sẽ custom lại Middleware để Mapping từng loại Exception vs HttpStatusCode. Trong quá trình code ko cần quan tâm quá nhiều tới HttpStatusCode này nữa lỗi thì cứ throw đúng cái Exception + Message là ok. :shame: Trước maintain dự án ko bắt dạng middleware này mà return Result(stattusCode, message). Code đúng bựa ra ngoài controller thì if else các kiểu con đà điều để mapping lại :v
1691314013719.png
 
Mấy project mình làm qua thương sẽ custom lại Middleware để Mapping từng loại Exception vs HttpStatusCode. Trong quá trình code ko cần quan tâm quá nhiều tới HttpStatusCode này nữa lỗi thì cứ throw đúng cái Exception + Message là ok. :shame: Trước maintain dự án ko bắt dạng middleware này mà return Result(stattusCode, message). Code đúng bựa ra ngoài controller thì if else các kiểu con đà điều để mapping lại :v Xem tệp đính kèm 1998947
cách này project mình cũng hay xài. cứ quăng đúng exception là có middleware catch để trả về đúng http status code tương ứng
 
cách này project mình cũng hay xài. cứ quăng đúng exception là có middleware catch để trả về đúng http status code tương ứng
việc dùng exception để tách riêng 1 layer xử lý mình thấy hợp lý.
kể cả ko dùng exception thì có thể dùng event để tập trung xử lý các lỗi/event sẽ làm code clear dễ handle hơn.
việc sửa đổi sẽ ko chung chạ nhiều.
 
//Mình code C#
Mình có 1 số thắc mắc về việc throw exception trong code sau.

Bài toán đặt ra là project mình là web api, có các layer là controller => service => repository.

1. Khi validate business logic, ví dụ validate mật khẩu user sai/ đúng thì khi sai có nên throw exception không ? Nếu không throw thì sẽ viết code xử lý trường hợp sai như nào

2. Mình hay nghe senior team mình nói throw sai có thể làm lost stack trace khó debug, mình không hiểu ý này có bác nào giải thích mình với

3. Nên viết catch exception ở layer nào? Khi nào nên re-throw lại exception?
1. Khi validate business logic, ví dụ validate mật khẩu user sai/ đúng thì khi sai có nên throw exception không ? Nếu không throw thì sẽ viết code xử lý trường hợp sai như nào
Cách tốt nhất là throw exception khi validate business logic sai. Điều này sẽ giúp bạn dễ dàng debug và tìm ra nguyên nhân của lỗi. Nếu không throw exception, bạn sẽ phải viết code xử lý trường hợp sai, điều này có thể gây khó khăn cho việc debug và có thể dẫn đến các lỗi khác.

Ví dụ, nếu bạn đang validate mật khẩu user, bạn có thể throw exception nếu mật khẩu không đáp ứng các yêu cầu về độ dài, độ phức tạp, v.v. Điều này sẽ giúp bạn dễ dàng tìm ra nguyên nhân của lỗi và khắc phục nó.

2. Mình hay nghe senior team mình nói throw sai có thể làm lost stack trace khó debug, mình không hiểu ý này có bác nào giải thích mình với
Stack trace là một danh sách các phương thức đã được gọi dẫn đến việc ném ngoại lệ. Nó rất hữu ích cho việc debug ngoại lệ vì nó giúp bạn xác định phương thức mà lỗi đã xảy ra.

Nếu bạn throw exception sai, stack trace có thể bị mất. Điều này sẽ khiến việc debug ngoại lệ trở nên khó khăn hơn vì bạn sẽ không thể xác định phương thức mà lỗi đã xảy ra.

Ví dụ, nếu bạn đang throw exception từ một phương thức trong lớp Service, stack trace sẽ bao gồm tên của phương thức Service và các phương thức mà nó đã gọi. Nếu bạn throw exception từ một phương thức trong lớp Controller, stack trace sẽ không bao gồm tên của phương thức Service và các phương thức mà nó đã gọi. Điều này sẽ khiến việc debug ngoại lệ trở nên khó khăn hơn vì bạn sẽ không thể xác định phương thức mà lỗi đã xảy ra.

3. Nên viết catch exception ở layer nào? Khi nào nên re-throw lại exception?
Bạn nên viết catch exception ở layer gần nhất với nơi xảy ra lỗi. Điều này sẽ giúp bạn xử lý lỗi hiệu quả hơn và ngăn lỗi lan truyền sang các layer khác.

Ví dụ, nếu bạn đang validate mật khẩu user trong lớp Service, bạn nên viết catch exception trong lớp Service. Điều này sẽ giúp bạn xử lý lỗi hiệu quả hơn và ngăn lỗi lan truyền sang lớp Controller.

Bạn chỉ nên re-throw exception khi bạn không thể xử lý lỗi ở layer hiện tại. Ví dụ, nếu bạn đang validate mật khẩu user trong lớp Service, nhưng bạn không thể xử lý lỗi ở layer này, bạn có thể re-throw exception lên lớp Controller.
 
Sửa lần cuối:
mọi người ơi cho hỏi khi có token rồi, nếu token gửi kèm request là token đúng thì ok nhưng khi token sai hoặc không có token thì chạy vào catch nhưng không được bắt ở globalException @ControllerAdvicevà ứng dụng bị crash. Mọi người thường xử lý case này thế nào vậy
 
mọi người ơi cho hỏi khi có token rồi, nếu token gửi kèm request là token đúng thì ok nhưng khi token sai hoặc không có token thì chạy vào catch nhưng không được bắt ở globalException @ControllerAdvicevà ứng dụng bị crash. Mọi người thường xử lý case này thế nào vậy
Sao lại crash được bác, mặc định nó sẽ trả ra 403 mà nhỉ. Lúc này tệ nhất thì có thể để frontend tự chế message (backend mình chỉ cần trả ra 403 là được)
 

Thống kê chủ đề

Ngày tạo
Lập Trình Viên Trẻ,
Người trả lời cuối
nchhnchh,
Trả lời
85
Lượt xem
16.466
Quay lại
Lên đầu trang