Web API with rails and grape gem

      1 Comment on Web API with rails and grape gem

Bài này là bài đầu tiên trong loạt ba bài viết về vấn đề làm rails web service, các bài khác bạn có thể xem tại:
1. Web API with rails and grape gem
2. Unit test for grape on rails with airborne framework
3. Rails API’s documentation with Swagger

Grape

grape là một micro-framework hỗ trợ trong việc xây dựng Web API trên rails.
grape là một gem trên rails, nên trước tiên, ta phải tạo một dự án rails:

Cài đặt

Gemfile

Thêm những gem sau vào Gemfile

  • grape-rabl là một gem hỗ trợ việc định nghĩa cấu trúc response, format, và hỗ trợ việc phân tầng trong việc tạo response.
  • yajl-ruby là gem hỗ trợ việc parsing JSON.

Cập nhật Gemfile:

Cấu hình khởi tạo cho rabl

Vào thư mục /config/initializers tạo file rabl.rb và thêm nội dung sau:

Cấu hình cho application

Mở file config/application.rb và chỉnh sửa như sau:

Việc cấu hình này thực hiện việc load các thư mục của grape api ngay khi chạy ứng dụng.

Xây dựng cấu trúc thư mục API

Vào thư mục /app tạo thư mục api/, trong thư mục api vừa tạo, tạo tiếp hai thư mục con v1/concerns/.

  • Thư mục api/ chứa các file về xử lý logic cho API
  • Thư mục v1/ chứa code cho version 1
  • Thư mục concerns/ chứa các file về xử lý lỗi, các file hỗ trợ…

Các hàm hỗ trợ trong concerns

Trong thư mục concerns/, tạo file api_request_parameter_converter.rb và thêm vào nội dung sau:

Tạo tiếp file authorizable.rb và thêm vào nội dung:

Phương thức authenticate! thực hiện việc kiểm tra request header xem có hợp lệ hay không, nếu không, ném ra lỗi Unauthorized.

Tạo tiếp file api_error_handler.rb, file này thực hiện việc bắt các lỗi API và trả thông tin lỗi về cho client:

Tạo file api_extensions.rb để include tất cả những file hỗ trợ kia vào:

Quay trở ra thư mục /app và tạo thư mục errors/, thư mục này sẽ chứa các custom error.
Tạo file unauthorized.rb và thêm vào nội dung:

Unauthorized chính là lỗi mà mình đã fail khi authenticate!.

Cấu hình đường dẫn cho API

Vào thư mục config/routes.rb sửa như sau:

Lệnh này sẽ cấu hình class CoreAPI làmroot_path. CoreAPI sẽ được đề cập sau.

Database

Để cho API đơn giản nhất có thể, database một bảng books (tương ứng là Book model) với các trường:
title: string
description: string
price: decimal với 10 chữ số phần nguyên và 2 chữ số phần thập phân

Khởi tạo modeldatabase:

Phần nhân API

Book model

Rails tạo sẵn cho ta file models/book.rb, sửa file theo nội dung:

Ba dòng validates chỉ ra rằng các trường đó là bắt buộc (khi tạo mới một Book)
Phương thức sefl.add(params) thực hiện việc tạo và ghi Book vào database với thông tin được gửi lên từ client.
params.permit(:title, :description, :price) chỉ chấp nhận ba trường đó trong params.

Core api file

Vào thư mục api/ và tạo file core_api.rb, file này sẽ chứa những thông tin, cấu hình gốc cho api:

Code khá đơn giản và dễ hiểu, có một lệnh cần lưu ý là mount V1::BookAPI, lệnh này thực hiện việc cho phép tất cả các phương thức của lớp BookAPI có hiệu lực. Lớp này ta sẽ tạo và định nghĩa ngay sau đây.

BookAPI

Để ý câu lệnh ở trên: mount V1::BookAPI, grape thực hiện việc đồng bộ moduleclass theo thư mục, nên ta sẽ tạo file book_api.rb trong thư mục api/v1/:

Resouce này định nghĩa, các lời gọi đến resource này sẽ có path dạng: host:port/api/v1/books vd:

Hello API

Phương thức GETnày, với đường dẫn /, sẽ sử dụng đường dẫn của resource: http://localhost:3000/api/v1/books và trả về JSON {foo: 'bar'}.
Bạn có thể test API đầu tiên của mình bằng cách truy cập http://localhost:3000/api/v1/books trên trình duyệt và xem kết quả (trình duyệt sử dụng GET cho các request). Bạn có thể custom path cho phương thức:

=> http://localhost:3000/api/v1/books/foo/bar

Rails console

Rails cung cấp cho ta một công cụ hữu ích để tương tác trực tiếp với database thông qua terminal: rails console. Mở terminal, cd đến thư mục demo-api/, chạy lệnh rails console (hoặc rails c cho nhanh).
Chạy lệnh sau để thêm một book vào bảng:

Kiểm tra số lượng record trong bảng:

Lấy phần tử đầu tiên:

Những lệnh khác bạn có thể tìm hiểu thêm tại đây.

Get all books

Để lấy tất cả các record trong bảng books, ta viết như sau:

desc chỉ đơn thuần là mô tả, có tác dụng khi làm documentation(sẽ nói trong bài khác).
Các lệnh khác hoàn toàn dễ hiểu với rails developer. Có một lệnh cần chú ý: rabl: "books/all", lệnh này thực hiện việc định nghĩa response sử dụng gem grape-rabl.

rabl

Như đã nói ở đầu bài, rabl giúp định nghĩa cấu trúc của response trả về và định nghĩa nội dung cần trả về cho mỗi request.

Cấu trúc

Cấu trúc chung
Ở thư mục app/views/, tạo thư mục api/, cd vào api/, tạo thư mục layouts/, cd vào layouts/, tạo file application.rabl với nội dung:

Cấu trúc này hoàn toàn dễ hiểu đúng không?

Cái tham số yield chính là đối tượng mình sẽ truyền lên để làm nội dung cho data, sẽ nói rõ ở phần sau.
Cấu trúc error
Trong thư mục app/views/api/layouts, tạo file error.rabl với nội dung:

Mẫu:

Vậy là xong phần cấu trúc chung, bây giờ là phần chính: books response

Books response

Trong thư mục app/views/api/, tạo thư mục books/, cd vào books/, tạo file _book.rabl, đây là file sẽ định nghĩa xem mỗi book model mình sẽ trả về những trường nào.

Nhớ lại khi get all books, ta có đoạn lệnh rabl: "books/all", lệnh này sẽ trỏ tới file views/api/books/all.rabl và file này sẽ lo việc hình thành response.
Tạo file views/api/books/all.rabl với nội dung:

@books bắt buộc phải giống với @books bên action gửi sang.
@books => :books sẽ tạo một JSON key cho kết quả trả về là books
extends "books/_book" để từng book trong collection được cấu trúc như nhau, định nghĩa ở _book.rabl
Kết quả như sau:

Vậy giờ tôi muốn custom một trường trả về thì sao?

Get sepecify book

Quay lại book_api.rb, thêm một phương thức:

Mọi câu lệnh đến bây giờ đều hoàn toàn dễ hiểu, chỉ cần chú ý rằng ta sẽ trỏ tới books/show để sinh response:
Tạo file views/api/books/show.rabl với nội dung:

node :cheap_book sinh ra một JSON key

định nghĩa JSON value:
Kết quả:

GET đủ rồi, giờ làm một cái POST hè?

Add new book

Phương thức trên bạn có thể đoán được là ở file nào đúng không?….
Đúng rồi book_api.rb.
converted_params là phương thức hỗ trợ nằm trong concerns/api_request_parameter_converter.rb.
Tạo file views/api/books/add.rabl với nội dung:

Quá đơn giản đúng không 😀
Để test phương thức này, bạn cần cài đặt Postman chrome extension. Thao tác khá đơn giản, bạn có thể xem thêm hình dưới:
Bạn hãy thử bỏ bớt một param đi để thấy thông báo lỗi, những thông báo lỗi này được dịnh nghĩa ở concerns/api_error_handler.rb.
Vậy còn chứng thực thì sao?
Ta sẽ áp dụng một phương pháp chứng thực đơn giản bằng cách gửi kèm header trong request.

Chứng thực bằng request header

Quay lại phương thức get all books, thêm vào authenticate! như sau:

Vào concerns/authorizable.rb xem:

Phương thức này kiểm tra xem trong request header có chứa Access-Token không, và nếu có thì giá trị nó có bằng foo hay không. Nếu không có thì báo lỗi Unauthorized. Nếu có thì OK.
Lưu ý Access-Token hay access_token hay access-token là tương đương.

Thêm chút nữa

Toàn bộ mã nguồn của dự án bạn có thể xem tại:

Source code on git hub

Source code on git hub

Bài viết hơi dài, tuy nhiên nó đưa đến cho bạn một cái nhìn từ tổng quan đến chi tiết trong việc xây dựng một bộ khung WEB API trên rails. Sau bài này, bạn hoàn toàn có thể tự xây dựng một bộ khung cho riêng mình 🙂
Phần tiếp theo: unit testing. Trong quy trình phát triển phần mềm, unit testing phải viết trước khi viết project, nhưng để cho bạn hiểu rõ hơn, mình xin phép đi ngược quy trình, đó là code => test.
Mời bạn đọc bài viết về Unit test for grape on rails with airborne framework.

Lập trình và hơn thế nữa

Spread the love
  • 1
  •  
  •  
  •  
  •  
    1
    Share

Leave a Reply

1 Comment on "Web API with rails and grape gem"

avatar
  Subscribe  
newest oldest most voted
Notify of
trackback

[…] viết này giả định bạn đã có kiến thức cơ bản về Web API with rails and grape gem, nếu chưa, vui lòng đọc bài viết của mình trước khi đi tiếp Bài viết cũng […]