Máy chủ web CRUD v Goli Golang

Tram Ho

bài trước chúng ta đã thiết lập chức năng đăng nhập, đăng ký cơ bản với Golang. Trong bài viết hôm nay, chúng ta sẽ hoàn thiện một webserver cơ bản với Golang bằng việc viết thêm các tính năng thêm, xóa, sửa, đọc (CRUD).

Để tiện theo dõi, chúng ta hãy cùng xem lại cấu trúc thư mục của ứng dụng.

Source code đầy đủ các bạn có thể tham khảo trên Github

1. Middlewares

Giống như khái niệm middlewares ở nhiều nền tảng khác, middlewares trong webserver golang là một hàm xử lý, đoạn code nằm giữa việc nhận request và trả response về cho client. Các công việc mà middlewares thường đảm nhận là xác thực người dùng, lọc request (tránh các request độc hại), validate dữ liệu, ….

Trong bài viết này, chúng ta sẽ chủ yếu sử dụng middlewares xác thực token gửi lên từ client, xác thực người dùng xem họ có được quyền thêm, xóa, sửa hay xem không ?

Đoạn code trên hơi phức tạp một chút, chúng ta sẽ dần dần tìm hiểu ngay sau đây.

httprouter.Handle Method

Chúng ta còn nhớ ở các bài trước, httprouter có các phương thức để xử lý các HTTP Method mà client gửi đến server như GET, POST, …

Thật ra, các method trên là cách viết ngắn gọn và tiện lợi hơn của method Handle

Type Handle

Ở phần trên, tham số cuối cùng của các method đều là kiểu dữ liệu Handle

Vậy các hàm có tham số đầu vào theo mẫu trên sẽ được coi là một hàm Handle. Bạn biết tại sao tham số http.Request lại ở dạng con trỏ không ? Lý do là data của request từ client gửi lên có thể đi được xử lý bởi một chuỗi các hàm Handle (chuỗi middlewares chẳng hạn). Do đó, dữ liệu cần được thay đổi trức tiệp qua con trỏ thay vì tạo một bản sao và thay đổi trên đó.

Tổng kết lại

Middlewares CheckJwt ở trên sẽ có nhiệm vụ Verify token do client gửi lên, nếu token không hợp lệ thì sẽ trả về lỗi. Ngược lại, hàm xử lý tiếp theo (Handle) sẽ được gọi để xử lý request.

Khi muốn dùng middlewares cho routes, ta sẽ thực hiện như sau ví dụ sau:

2. models/Post.go

Chúng ta sẽ tạo mới models Post, mỗi người dùng trên hệ thống sau khi đăng nhập sẽ có quyền thêm, sửa, xóa các post của bản thân. Có quyền xem tất cả các post hoặc xem các post do mình tạo.

Kết nối đến db

Tương tự với collection users, chúng ta viết hàm connect đến collection posts

Step routes với middlewares

3. Xử lý jsonwebtoken từ client gửi lên

Trích xuất thông tin ra từ jwt

  • Hàm Extract có nhiệm vụ lấy giá trị token trong header request , loại bỏ phần dư thừa trong header (Chuỗi “Bearer”)
  • Hàm ExtractUsernameFromToken có nhiệm vụ trích xuất ra username từ giá trị của token nhằm mục đích để phân quyền sau này cho các route thêm, xóa post.

Verify jwt

Token sau khi được client gửi lên sẽ được kiểm tra tính hợp lệ, giải mã với secret_key lưu trong biến môi trường. Hàm jwt.Parse có một chút phức tạp, nếu muốn tìm hiểu kỹ hơn, các bạn có thể đọc thêm tại GoDocs.

4. Query posts

Get all posts

  • Bước đầu là connect đến collection posts
  • Định nghĩa biến result dạng mảng bson.M (bson dạng map)
  • Dùng hàm Find của thư viện mongoDB để truy vấn toàn bộ bản ghi trong collection posts
  • Handle lỗi
  • Do biến data lưu giá trị trả về từ hàm Find ở dạng con trỏ, nên ta cần chuyển đổi thành dạng bson để trả về client. (vd: &{ 0xc0003c6840 <nil> 0xc0000d8c40 0xc00037cb40 <nil>})
    Prototype của hàm Find:

  • Ta dùng hàm for duyệt qua data và decode từng thành một thành dạng bson

Get my posts

  • Tương tự như phần trên, hàm GetMyPosts chỉ khác ở chỗ extract username từ token và thêm filter với điều kiện các bản ghi có giá trị creater là username extract ra ở trên.

5. Create post

  • Mỗi post sẽ có một id duy nhất (dạng uuidv4)
  • Sau khi tạo ra 1 uid bất kỳ với hàm uuid.NewV4(), chúng ta sử dụng hàm fmt.Sprintf để convert id thành dạng xxxx-xxxx-xxxx-xxx.
  • Phần sau chúng ta cũng đã khá quen thuộc khi connect db và gọi hàm InsertOne để them bản ghi vào db.

6. Edit post

  • Hàm lấy giá trị id ở params và title ở request body.
  • Validate data
  • Kết nối đến db
  • TÌm trong db xem có bản ghi nào có giá trị id trùng với biến id không, nếu không có thì trả về 404.
  • Nếu có thì chúng ta so sánh trường creater trong bản ghi với giá trị username extract từ token ra xem có trùng nhau hay không ? Nếu khác nhau thì trả về 403 cho client.
  • Tiếp theo chúng ta dùng hàm UpdateOne để cập nhật giá trị. Hàm trả về 2 giá trị, biến đầu tiên ở dạng con trỏ, chúng ta không cần xử lý nó nên sẽ để dấu _, chỉ cần check xem quá trình update có lỗi hay không thôi.

7. Delete post

  • Hàm DeletePost cũng tương tự như hàm edit ở trên, chỉ khác chỗ dùng hàm FindOneAndDelete

Tài liệu tham khảo

https://godoc.org/go.mongodb.org/mongo-driver/mongo
https://godoc.org/github.com/julienschmidt

https://www.alexedwards.net/blog/a-recap-of-request-handling
https://www.alexedwards.net/blog/making-and-using-middleware

Chia sẻ bài viết ngay

Nguồn bài viết : Viblo