12/08/2018, 16:12

Professional web api với rails

Chào mọi người, ngày nay lập trình web không chỉ là xây dựng một ứng dụng với front-ent và một tí backend xử ký ở dưới, mà còn 1 tá thứ đằng sau. Một trong số đó là xây dựng nên một API server. Việc xây dựng API cho một ứng dụng web ngày nay là hết sức cần thiết để ứng dụng có thể giao tiếp và mở ...

Chào mọi người, ngày nay lập trình web không chỉ là xây dựng một ứng dụng với front-ent và một tí backend xử ký ở dưới, mà còn 1 tá thứ đằng sau. Một trong số đó là xây dựng nên một API server. Việc xây dựng API cho một ứng dụng web ngày nay là hết sức cần thiết để ứng dụng có thể giao tiếp và mở rộng đáng kể. Vậy với người mới bắt đầu thì làm sao để viết cho mình như là một professional, làm sao để cấu trúc code cho có thể mở rộng sao này. Hôm nay mình xin chia sẻ lại cách mình đã tiếp cận và viết những API rails. Vậy một API tốt cần có những gì:

RESTful là 1 chuẩn đã được áp dụng cực kì rộng rãi. Chuẩn này lần đầu tiên được ứng dụng bởi Roy Fielding trong chương 5 của luạn án về kiến trức phần mềm dựa trên network của ông.

Các nguyên tắc chính của REST( Representational State Transfer ) liên quan đến việc tách API của bạn thành các resource hợp lý. Các resource này được quản lý và vận hành/thao tác bằng cách sử dụng các request HTTP, trong đó các method (GET, POST, PUT, PATCH, DELETE) có một ý nghĩa cụ thể.

  • Sử dụng danh từ số nhiều, không dùng động từ

Có một nguyên tắc trong việc đặt tên URL sao cho Restful là bạn hãy hướng đến resource nên việc đăt tên cho URL luôn bắt buộc các bạn dùng danh từ vì động từ nó đã có sẵn trong các HTTP method rồi.

Về cơ bản thì tên resource nên đặt là số nhiều, kể cả với phương thức show() chẳng hạn, tuy rằng ta chỉ show ra một đối tượng đơn lẻ ở đây, nhưng cũng đừng vì thế mà đặt url thành số ít. Cái này là chuẩn chung rồi, thiên hạ theo cả, đừng cãi.

  • Những hành động CRUD sử dụng những phương thức HTTP

Lí thuyết nói rằng, thiết kế theo chuẩn RESTful sẽ chỉ có GET, POST, PUT, PATCH, DELETE ..., thế nhưng với những ngữ cảnh cụ thể các bạn có thể viết làm sao như người sử dụng dễ hiểu nhất, Đôi khi bạn thực sự không có cách nào để map action đến một cấu trúc RESTful hợp lý. Ví dụ, một action tìm kiếm nhiều tài nguyên không thực sự có ý nghĩa để được áp dụng endpoint tới một tài nguyên cụ thể. Trong trường hợp này, /search sẽ có ý nghĩa nhất mặc dù nó không phải là tài nguyên. Điều này là OK - chỉ cần làm những gì bạn cảm thấy đúng từ quan điểm của sử dụng API và đảm bảo rằng nó được giải thích - làm tài liệu rõ ràng để tránh nhầm lẫn.

Các HTTP method bao gồm những rành buộc thống nhất để cung cấp những hành động đến resource. Ở đây thì hầu như mọi người đã biết các phương thức http thường dùng bao gồm các động như POST, GET, PUT, PATCH, DELETE Mình tóm tắt lại các method thường dùng

HTTP Verb CRUD Entire Collection (e.g. /customers) Specific Item (e.g. /customers/{id})
POST Create 201 (Created), 'Location' header với đường dẫn /customers/{id} chứa ID mới. 404 không tìm thấy, 409 (Conflict) nếu đã tồn tại customer, 422 Unprocessable Entity - Sử dụng cho các lỗi validation.
GET Read 200 (OK), trả về list các customer có thể sẽ kết hợp với phân trang, filter, sort. 200 (OK), có thể trả về 1 specific customer. 404 (Not Found), nếu ID không tìm thấy hoặc invalid.
PUT Update/Replace 405 (Method Not Allowed), trừ khi bạn muốn cập nhật / thay thế mọi tài nguyên trong toàn bộ resouce. 200 (OK) or 204 (No Content). 404 (Not Found), nếu ID không tìm thấy hoặc invalid, 422 Unprocessable Entity - Sử dụng cho các lỗi validation.
PATCH Update/Modify 405 (Method Not Allowed), trừ khi bạn muốn thay đổi chính resource đó. 200 (OK) or 204 (No Content). 404 (Not Found) ,nếu ID không tìm thấy hoặc invalid.
DELETE Delete 405 (Method Not Allowed), trừ khi bạn muốn xóa toàn bộ resource đó-không thường xuyên mong muốn. 200 (OK). 404 (Not Found), nếu ID không tìm thấy hoặc invalid.

429 Too Many Requests - Request bị reject do giới hạn rate limiting

Để đảm bảo an toàn cho Api của mình chắc chắn các bạn phải cần authentication cho api của mình. Hiện nay có khá nhiều cách để authentication API và ở đây mình giới thiệu các bạn JWT JWT là một dữ liệu chữ kí dưới dạng JSON. Bởi vì nó được "kí" nên phía nhận có thể xác minh tính xác thực của nó. Dung lượng của nó rất nhỏ vì nó là JSON Theo mình thì điểm mạng nhất của JWT là server không cần phải giữ bất kì record nào thê hiện user đã log in hay chưa ? Thay vì thế mọi request đều được tới server đều được đính kèm một token - cái này sẽ được server sử dụng để xác thực authen của request đó.

  1. User nhập thông tin đăng nhập
  2. Server xác minh thông tin đăng nhập chuẩn ko ? trả về token trong response
  3. Token lưu ở client - thường sẽ lưu ở storage local nhưng có thể lưu trong sessions storage or cookie.
  4. Các request tiếp theo đc gửi lên server sẽ chứa token , thường sẽ cho vào header Authorization
  5. Server decode JWTs và nếu token hợp lệ thì request đó sẽ được pass qua authentication
  6. Khi user log out, token sẽ bị destroy ở phía client, không cần tương tác với bên server.

Ngay cả khi bạn chỉ mới bắt đầu xây dựng sản phẩm và cũng không chắc chắn là có làm tiếp những version tiếp theo không. Nhưng việc đặt version cho api là việc rất quan trọng và nó còn không tốn kém và rất dễ dàng để thêm khi cần version mới khi sản phẩm đã thành công. Nếu sản phẩm bạn được update thì bạn sẽ luôn phải tương thích nó với cả version mới và cũ, và việc đổi một api đã được published và sử dụng là một thứ rất tệ, cực kì tệ. Client luôn dựa vào api interface, họ có thể có những business logic dựa trên API của bạn và nó fail khi bạn thay đổi code mà không có chia version, hậu quả là bạn sẽ mất khách hàng sẽ bị kiện, hoặc mất rất nhiều thời gian và lòng tin. Với rails thì việc versioning cực kì dễ dàng bạn chỉ cần đặt namspace in routing của bạn là đủ để định nghĩ các version khác nhau:

namespace :api do
  namespace :v1 do
    resources :items
  end
end

Như vậy là bạn đã có thể request đến /api/v1/items

Khi ứng dụng của bạn mới được sử dụng các bạn có thể không lo lắng về hiệu năng cũng như là giới hạn về các tài nguyên phần cứng. Tuy nhiên, đến một ngày ứng dụng thành công và có hàng nghìn người sử dụng bạn bắt đầu nghĩ đến việc khi api được nhúng vào các luồng của các dự án và nó được gọi lặp đi lặp lại trong các vòng lặp hoặc cron job với cùng URL. Đó là lý do bạn nên thiết lập sớm rate-limit cho chúng. Không chỉ vì có thể tránh máy chủ của bạn down bởi CI-servẻ, mà nó còn chỉ cho những người sử dụng API của bạn một số lượng request tốt đa có thể nhận trong 1 khoảng thời gian. Các bạn có thể implemented một rate limit bằng việc sử dụng redis hoặc với ác hệ thống lớn có thể cấu hình trên ng-inx. Tùy cách chọn lựa của bạn và giải pháp nhưng hãy chắc rằng bạn phải hiển thị con số rate-limit một cách thật rõ ràng cho những người sử dụng. Ví dụ response header của github đã làm

X-Rate-Limit-Limit: 1000
X-Rate-Limit-Remaining: 1920
X-Rate-Limit-Reset: 2015-03-10T13:00:00Z

Khi phát triển dự án đôi lúc bạni không thể tưởng tượng được lượng dữ liệu mà khách hàng muốn quản lý. Điều này gây ra vấn đề, vì một số thông tin trên các thiết bị di động chỉ cần hiển thị vài item và cho phép load more tiếp thep mà bạn lại không cung cấp phân trang trong. Vì vậy đối với các khách hàng lớn, API đã trả lại hàng trăm nghìn items cùng một lúc.

Điều này có thể dẫn đến những bất tiện nghiêm trọng đối với cơ sở dữ liệu, trình duyệt và máy chủ web. Một cách tốt để ngăn chặn điều này là để paginate tất cả các kết quả trả về một danh sách các mặt hàng.

Pagination in Rails có thể dễ dàng thực hiện bằng cách sử dụng một loại đá quý thích hợp như kaminari, nhưng bạn cũng có thể thực hiện nó bằng cách sử dụng các câu lệnh LIMIT và OFFSET trong các truy vấn của bạn hoặc trên các resource của bạn.

Việc bắt lỗi trong API nghe có vẻ rất dễ dàng nhưng nó đòi hỏi một kế hoạch và bàn bạc rõ ràng. Một khuyến cáo là luôn trả về thông điệp lỗi với format giống nhau để mà người sử dụng API không cần phải xử lý parse cho từng resource khác nhau . Hãy dùng HTTP status code để đẩy lên trên message trả về ví dụ trả về 422 khi validate fail. Và không có gì tệ hại hơn là một lỗi mà không có bất kì message lỗi nào nên hãy luôn đảm vào là bạn đã luôn trả về lỗi khi có lỗi xảy ra

Đây là một ví dụ cho message lỗi Status: 422

{
  "message": "Validation Failed",
  "errors": [
    {
      "resource": "Item",
      "field": "name",
      "message": "can't be blank"
    }
  ]
}

Với tất cả các dịch vụ web được nhiều nguòiwf sử dụng thì việc caching là rất quan trọng để đảm bảo performance. HTTP đi kèm với 1 cơ chế lưu trữ rất tuyệt với khi sử dụng với rails. Rails cũng hổ trợ rất nhiều cách cache như: cache keys, Russian Doll Caching and Reverse-Proxy-Caches (Varnish, CDNs)

Bạn có thể tham khảo thêm ở đây http://guides.rubyonrails.org/caching_with_rails.htm https://viblo.asia/p/tong-quan-ve-caching-trong-ruby-on-rails-ZjlvaldxkqJl

Cần chính xác, chính xác và dễ hiểu. Một lỗi đơn hoặc hiểu nhầm trong một tài liệu hướng dẫn có thể thúc đẩy các developer khác mất thời gian và mất niềm tin

0