RESTful API là gì? Cùng tìm hiểu về RESTful API

303

Chào bạn,

Có phải bạn là một developer mới, bạn thấy các developer lành nghề khác nói với nhau về restful. Bạn không biết restful là gì nên bạn lên google search “restful là gì?”. Bạn đọc khá nhiều bài viết rồi nhưng vẫn còn mông lung chưa hiểu mặt mũi thằng restful thế nào? Nếu như bạn đang cảm thấy như vậy, thì hãy đọc bài viết này của mình nhé.

Bài viết này mình sẽ trình bày những vẫn đề xung quanh restful api trước khi mình trình bày chi tiết về nó, thế nên bạn hãy kiên nhẫn đọc nhé.

I. Web service là gì?

Website thì ai cũng cũng biết rồi, như blog phambinh.net này chính là một website đó. Thế nhưng web service thì khác, không phải ai cũng có cơ hội được nghe thấy cụm từ này kể từ khi mà Restful trở nên phổ biến.

Web service cũng là một ứng dụng web hoạt động tương tự như một website, tức là để truy cập vào web service bạn cũng cần phải mở trình duyệt lên, gõ vào thanh địa chỉ url của web service đó. Tuy nhiên khác với website – web để cho người đọc, thì web service sinh ra để cho các cỗ máy, hoặc các ứng dụng khác đọc.

1.1. Tại sao web service ra đời?

Câu chuyện kể rằng có 2 anh bạn tên Quang và tên Bình chơi thân với nhau từ hồi nhỏ. Lớn lên, Quang mở một công ty giới thiệu việc làm cho nhân sự IT, Bình thì mở một công ty đào tạo nhân sự IT. Một hôm, Quang nói với Bình “mày ơi, công ty tao có mấy job PHP lương cao lắm, mày đặt quảng cáo cho tao trong mấy khóa học về PHP của máy nhé“, Bình nghe vậy liền đồng ý luôn, vì tốt cho cả hai mà. Nhưng vấn đề là website tuyển dụng của Quang và website đào tạo của Bình chạy ở trên 2 con server khác nhau, không có một “cây cầu” nào kết nối giữa hai website này cả.

Quang nghĩ đến cách sẽ chèn “cứng” quảng cáo của mình trong các khóa học của Bình, nhưng Bình gạt đi. Vì chèn cứng như vậy, lỡ job PHP của Quang hết hạn tuyển dụng thì sao, lại gỡ xuống à? Mà một job thì không sao, chứ 100 job thì chỉ có chèn lên với gỡ xuống cũng hết ngày. Bình bèn nghĩ ra cách này và bảo Quang, “bên website tuyển dụng của mày, mày tạo cho tao một trang riêng chỉ hiển thị các job PHP mà mày muốn quảng cáo, bên website của tao sẽ đọc nội dung trang web này rồi hiển thị lên“.

Vậy là Quang tạo một trang web riêng cho Bình ở địa chỉ http://webtuyendungit.com/job-php, khi truy cập vào đây sẽ chỉ nhìn thấy nội dung khó hiểu như sau

{
    "jobs": [
        {
            "url": "http://webtuyendungit.com/tuyen-lap-trinh-vien-php-luong-1000-dollar",
            "title": "Tuyển dụng lập trình viên PHP lương 1000 dollar"
        },
        {
            "url": "http://webtuyendungit.com/tuyen-lap-trinh-vien-php-khong-kinh-nghiem",
            "title": "Tuyển dụng lập trình viên PHP không kinh nghiệm"
        },
    ]
}

Sau đó, Bình sử dụng CURL để lấy nội dung trên website của Quang, phân tích thành dữ liệu và hiển thị ngon lành lên website đào tạo của mình. Giờ đây Quang muốn thay đổi nội dung quảng cáo thì chỉ cần thay đổi nội dung của trang web trên, vô cùng tiện lợi và chủ động.

Trên chính là một ví dụ điển của web service. Khi các ứng dụng không liên quan tới nhau, nhưng vẫn muốn trao đổi dữ liệu với nhau thì người ta sẽ nghĩ ngay tới việc sử dụng web service. Một web service sẽ trả về dữ liệu theo một cấu trúc nào đó (XML hoặc JSON,…) để các ứng dụng khác có thể đọc, phân tích và sử dụng được. Như ví dụ trên thì http://webtuyendungit.com/job-php chính là endpoint của một web service.

Web service ra đời như là một điều hiển nhiên, bởi vì ngày càng có nhiều hệ thống chạy đa nền tảng như Facebook, Youtube,.. ra đời. Đặc điểm của các hệ thống chạy đa nền tảng này là luôn yêu cầu khả năng đồng bộ dữ liệu. Ví dụ bạn like một status facebook trên web, thì trên app cũng phải được thể hiện, bạn đăng một bức ảnh lên facebook từ mobile, thì trên web cũng phải nhìn thấy. Để làm được điều này, người ta sẽ tạo ra một con web service, để khi bạn đăng ảnh, like hay thực hiện bất kỳ hành động gì đều phải gọi tới web service này cho dù hành động đó được thực hiện từ web hay mobile. Mặt khác, ứng dụng web và mobile sẽ kết nối vào chung web service để đọc dữ liệu, vì vậy sẽ đảm bảo được dữ liệu là giống nhau cho dù trên các nền tảng khác nhau.

Tóm lại web service ra đời nhằm giải quyết một vấn đề sau

  1. Giúp các hệ thống không liên quan tới nhau vẫn có thể giao tiếp được với nhau
  2. Đồng bộ dữ liệu giữa các nền tảng
Web service nằm giữa

1.2 Endpoint của web service

Mỗi URL kèm HTTP method của web service thì được gọi là một endpoint, như ví dụ trên thì mình có http://webtuyendungit.com/job-phpchính là một endpoint. Khi làm một endpoint cho web service, bạn sẽ phải quan tâm tới một số vấn đề sau:

Endpoint sử dụng cấu trúc dữ liệu nào để trả về?

XML hoặc JSON là hai lựa chọn cho bạn để sử dụng làm dữ liệu trả về cho endpoint. Như ví dụ trên là mình sử dụng JSON.

URL được viết như thế nào?

Ví dụ mình có 1 endpoint trả về thông tin chi tiết của một user dựa vào ID của user được gửi lên, thì mình có thể lựa chọn một trong hai cách viết sau (giả sử ID của user là 1).

  • http://webservice.com/users?id=1
  • http://webservice.com/users/1

Hoặc bạn có thể dùng một cách viết khác cũng được, nói chung là endpoint được viết như thế nào là do bạn, không có quy tắc chung nào cho cách viết endpoint cả.

URL sử dụng HTTP Method nào?

Giả sử mình chọn URL có dạng là http://webservice.com/users?id=1, thế HTTP method là gì được nhỉ? GET hay POST? Câu trả lời cũng là tùy bạn, GET hay POST cũng được, không có quy tắc nào cả.

An toàn dữ liệu

Web service trao đổi dữ liệu với các ứng dụng khác thông qua môi trường mạng. Nếu để lộ các endpoint, thì khả năng cao dữ liệu trả về trong các endpoint đó cũng bị lộ. Thực tế đã có rất nhiều vụ lộ thông tin người dùng mà nguyên nhân là do các endpoint kém bảo mật.

1.3 Các loại web service

Các endpoint của web service quá “tự do”, dữ liệu trả về, cách viết url, http method đều do bạn tự quyết định. Nhận thấy điều này không hợp lý, nên người ta đưa ra hai loại chuẩn cho web service như sau:

SOAP web service

Simple Object Access Protocol là một dạng giao thức (cũng có thể coi là một chuẩn). SOAP sử dụng XML làm cấu trúc dữ liệu trả về. Tuy nhiên SOAP không có quy ước về cách viết url cũng như http method. Nhưng bù lại, SOAP lại có WS-Security SOAP – là một chuẩn giúp an toàn dữ liệu, giải quyết được vấn đề an toàn dữ liệu mà mình đề cập ở trên.

RESTful web service

REpresentational State Transfer, là một chuẩn của web service. RESTful có thể sử dụng JSON, XML, plain text, html,.. làm cấu trúc dữ liệu trả về, có quy ước rõ ràng về cách viết url và http method. Nhưng RESTful không cung cấp cơ chế bảo vệ thông tin trong các endpoint như SOAP. Tuy nhiên bạn có thể sử dụng Json Web Token kết hợp với RESTful để giải quyết vấn đề này, nên việc không có sẵn cơ chế an toàn thông tin không phải là điều đáng lo ngại khi sử dụng RESTful.

SOAP vs RESTful

Ngày nay các dự án web service đa phần (thậm chí gần như tất cả) đều sử dụng RESTful thay vì sử dụng SOAP. Bởi như mình kể ra ở trên thì bạn thấy RESTful có quy ước rõ ràng hơn hẳn SOAP. Mặt khác RESTful có thể sử dụng nhiều loại dữ liệu để trả về, trong đó có cả XML, vậy xét ở góc độ nào đó có thể nói rằng RESTful bao gồm cả SOAP cũng không sai.

Tuy nhiên SOAP vẫn còn được sử dụng trong nhiều dự án cũ cần được bảo trì, nên bạn mà tìm hiểu được SOAP nữa thì càng tốt.


II. Tìm hiểu về RESTful

Bài viết viết về RESTful mà lại quá trời về web service. Mình trình bày như vậy là do theo đúng trình tự thì cái chúng ta biết trước phải là web service, sau đó mới là RESTful. Nhưng vì RESTful đang là một từ khóa hot, nên các bạn có xu hướng tìm hiểu về RESTful trước mà bỏ quên mất cái gốc web service.

Sau đây mới thật sự là những gì bạn muốn tìm hiểu về RESTful nhé.

RESTful có hệ thống quy tắc chặt chẽ về các viết endpoint và HTTP method, mình sẽ tóm tắt một vài nguyên tắc quan trọng làm nên “tên tuổi” của RESTful để bạn tiện theo dõi.

2.1 Quy tắc về HTTP method của endpoint

Nếu là web developer, chắc chắn bạn biết đến method GET và POST. Nhưng với RESTful thì có thêm một số method mới, kèm cách sử dụng tương ứng như sau:

  • GET: được sử dụng để lấy thông tin từ sever theo URI đã cung cấp.
  • POST: gửi thông tin tới sever thông qua các biểu mẫu http (đăng kí chả hạn..)
  • HEAD: giống với GET nhưng response trả về không có body, chỉ có header
  • PUT: ghi đè tất cả thông tin của đối tượng với những gì được gửi lên
  • PATCH: ghi đè các thông tin được thay đổi của đối tượng.
  • DELETE: xóa tài nguyên trên server.
  • CONNECT: thiết lập một kết nối tới server theo URI.
  • OPTIONS: mô tả các tùy chọn giao tiếp cho resource.
  • TRACE: thực hiện một bài test loop – back theo đường dẫn đến resource.

Thực tế thì mình mới chỉ làm việc với các method GET, POST, PUT, DELETE thôi, các method còn lại thì chưa làm bao giờ, nhưng tương lai chắc là sẽ gặp :p.

2.2 Quy ước về resource, endpoint

Resource có nghĩa là tài nguyên, nhưng đây là một khái niệm được nhắc đến nhiều trong RESTful, nên mình sẽ giữ nguyên từ khoá này mà không Việt hóa nhé.

Resource chính là dữ liệu mà chúng ta phải quản lý, có thể là customers, products, posts, images, videos… Mặt khác, tên resource cũng sẽ xuất hiện trong cách viết endpoint, nên nếu bạn đặt tên cho resource một cách khoa học, thì endpoint cũng trở nên dễ hiểu và dễ tiếp cận hơn.

Xem ví dụ trong bảng sau để hiểu rõ đâu là resource, và resource thường được viết như thế nào trong mỗi endpoint

EndpointResource
http://api.example.com/usersusers
http://api.example.com/users/1/accountsaccounts
http://api.example.com/users/1/imagesimages

Dưới đây là một vài quy tắc để bạn tham khảo về cách đặt tên resource sao cho hay.

Sử dụng danh từ để đặt tên cho resource

RESTful tổ chức resource dưới dạng các đối tượng, vì vậy resource nên được đặt tên dưới dạng dạng một danh từ chứ không phải một động từ. Giả sử mình có một số resource là: users, posts, thì resource tương ứng sẽ được viết trong các endpoint như sau:

http://api.example.com/users # Liệt kê tất cả user
http://api.example.com/users/1 # Chi tiết user có ID là 1
http://api.example.com/posts # Liệt kê tất cả post
http://api.example.com/posts/1 # Chi tiết post có ID là 1

Sử dụng đấu sượt (/) để thể hiện mối quan hệ phân cấp giữa các resources

Trong thực tế, các resource thường có mối quan hệ với nhau. Ví dụ mình có resource user, mỗi user lại có nhiều resource image. Thì minh có thể thiết kế các endpoint như sau

http://api.example.com/users # Liệt kê tất cả users
http://api.example.com/users/1 # Chi tiết user có ID là 1
http://api.example.com/users/1/images # Liệt kê tất cả images của user có ID là 1

Dùng dấu gạch ngang (-) để ngăn cách giữa các cụm từ

http://api.example.com/banned-users # Tốt (1)
http://api.example.com/banned_users # Không tốt (2)
http://api.example.com/bannedUsers # Không tốt (3)

Trong ví dụ trên, cách (1) và (2) rõ ràng là dễ đọc hơn nhiều so với cách (3). Một số bạn theo chủ chương của camelcase có thể sẽ thấy cách (3) dễ đọc hơn. Tuy nhiên nếu mình có caiTenDaiNgoangNgoang thế này thì rõ ràng khó nhìn hơn cai-ten-dai-ngoang-ngoang thế này đúng không.

Vậy tại sao (1) lại tốt hơn (2)? Nguyên nhân là dâu gạch dưới (_) bị phụ thuộc nhiều vào font chữ hiển thị, trong một số trường hợp nó có thể bị che mất một phần, hoặc bị xóa, hoặc bị chuyển thành dấu cách trên một số trình duyệt. Điều này dễ dàng gây ra nhầm lẫn cho người sử dụng.

Sử dụng chữ thường cho toàn bộ endpoint

http://api.example.com/banned-users # Tốt (1)
HTTP://API.WEBSERVICE.COM/banned-users # Không tốt (2)
http://api.example.com/BANNED-USERS # Không tốt (3)

Trong mỗi endpoint, ngoại trừ phần giao thức và phần domain, thì các phần còn lại sẽ phân biệt chữ hoa chữ thường. Tức là ta có endpoint (1) sẽ tương tự với endpoint (2), nhưng endpoint (3) thì khác hoàn toàn.

Vậy để nhất quán và tránh nhầm lẫn, chúng ta nên viết các endpoint bằng chữ thường.

Không sử dụng đuôi mở rộng cho các endpoint

http://api.example.com/banned-users # tốt (1)
http://api.example.com/banned-users.json # không tốt (2)
http://api.example.com/banned-users.xml # không tốt (3)

Đuôi mở rộng chính là .json.xml trong endpoint (2) và (3) ở ví dụ trên. Một số bạn có thể thấy rằng như vậy là tường minh hơn, rằng khi tôi truyền .json nghĩa là tôi muốn lấy dữ liệu dạng json và tương tự với xml. Tuy nhiên làm như vậy không phải là cách hay vì:

  • Endpoint dài hơn và nhìn có vẻ xấu xí
  • Bạn sẽ phải bảo trì nhiều endpoint hơn

Nếu như bạn vẫn muốn endpoint có thể trả về nhiều kiểu dữ liệu khác nhau, thì bạn có thể sử dụng thuộc tính Content-Type của request header để xác định kiểu dữ liệu trả về.

Sử dụng query params để lọc kết quả

Giả sử mình có một endpoint /users để lấy ra danh sách toàn bộ users. Nhưng thực tế mình chỉ muốn lấy ra các users ở Việt Nam. Một số bạn sẽ nghĩ đến cách tạo một endpoint như kiểu như /users/vn để giải quyết yêu cầu này. Tuy nhiên bạn không cần phải làm thế, chúng ta có thể coi Việt Nam như một tiêu chí để lọc, và endpoint nên được viết như thế này

http://api.example.com/users?country=vn # tốt. Sử dụng query params country
http://api.example.com/users/vn # không tốt

Bạn cũng nên sử dụng query params để phân trang hoặc sắp xếp kết quả thay vì việc thiết kế một endpoint mới

http://api.example.com/users?page=1 # Tốt
http://api.example.com/users/pages/1 # Không tốt

http://api.example.com/users?orderBy=latest # Tốt
http://api.example.com/users/orderBy/latest # Không tốt
http://api.example.com/users/orderBy?latest # Không tốt

Sử dụng HTTP method để thể hiện CRUD

Bạn không nên thể hiện các thao tác với resource bằng việc chỉ ra trên URI, thay vào đó bạn hãy sử dụng các HTTP method tương ứng.

# Liệt kê danh sách users
HTTP GET     http://api.example.com/users # Nên
HTTP GET     http://api.example.com/users/all # Không nên

# Thêm một users mới vào danh sách
HTTP POST    http://api.example.com/users # Nên
HTTP POST    http://api.example.com/users/create # Không nên
HTTP POST    http://api.example.com/users/store # Không nên

# Cập nhật thông tin user có ID là 1
HTTP PUT     http://api.example.com/users/1 # Nên
HTTP POST    http://api.example.com/users/1/update # Không nên
HTTP POST    http://api.example.com/users/1/edit # Không nên

# Xóa user có ID là 1
HTTP DELETE  http://api.example.com/users/1 # Nên
HTTP POST    http://api.example.com/users/1/destroy # Không nên
HTTP POST    http://api.example.com/users/1/delete # Không nên

2.3 Có nhất thiết phải tuân theo 100% chuẩn RESTful không?

Thực tế mình trải qua các dự án sử dụng RESTful thì chưa có dự án nào thực hiện được 100% chuẩn của RESTful, bởi

  • Đa phần các developer chỉ thích dùng method POST và GET cho đơn giản
  • Một số ý kiến cho rằng /users/1 khó sử dụng hơn /users?id=1
  • Tập trung vào RESTful không làm cho sản phẩm chạy tốt hơn, chỉ làm cho code đẹp hơn, tuy nhiên một số trường hợp thì code đẹp không cần thiết.

Vậy có nhất thiết là phải chuẩn 100% RESTful không? Câu trả lời là không, chuẩn của RESTful là một chuyện nhưng nó cũng phải phù hợp với sự thống nhất của team, phù hợp với tính chất của dự án. Nhưng quan điểm của mình là càng chuẩn RESTful thì càng tốt.

III. API là gì và RESTful API là gì?

3.1. API là gì?

API là viết tắt của Application Programing Interface – giao diện lập trình ứng dụng. Giao diện ở đây không phải là giao diện của phần mềm, không phải là những khối màu, bố cục của phần mềm mà mắt bạn nhìn thấy đâu nhé. Giao diện ở đây giống như một chuẩn chung để kết nối vậy. Ví dụ như cái ổ cắm với cái phích cắm, mặc dù chúng có thể đến từ hai nhà sản xuất khác nhau nhưng khi cắm vào nhau thì chúng vẫn vừa khít, đấy là do chúng cùng tuân theo một giao diện kết nối.

Vì một phần mềm chứa rất nhiều logic phức tạp, nên người ta tìm cách chia nhỏ nó ra thành nhiều phần, mỗi phần này tạm gọi là một component. Mỗi component sẽ có tính độc lập cao, ít phụ thuộc hoặc có thể không phục thuộc vào các thành phần khác. Tuy là có tính độc lập cao, nhưng để có thể kết nối được với nhau mà một phần mềm hoàn chỉnh, buộc chúng vẫn phải tuân theo một hoặc một số chuẩn nào đó. Thì mỗi cái chuẩn đó được gọi là một giao diện lập trình ứng dụng – hay chính là một API.

3.2 RESTful API là gì?

RESTful API là những API của web service sử dụng theo chuẩn RESTful. Trước khi áp dụng RESTful để tạo API, người ta sẽ đưa ra các chuẩn (API) trước. Ví dụ mình quy định nếu thực hiện thêm users thành công, thì sẽ phải trả về header status là 200, kèm một tin nhắn có nội dung là “thành công” chẳng hạn, ai mà làm sai theo quy tắc này tức là sai API, và endpoint đó sẽ chỉ được coi là RESTful endpoint chứ không được coi là RESTful API.

IV. Lời kết

Kết luận lại, bài viết này mình muốn bạn lưu một số ý chính sau:

  • RESTful chỉ là một chuẩn của web service, muốn biết về RESTful thì phải tìm hiểu về web service trước.
  • RESTful không khó, nó chỉ là một tập các quy tắc thôi, tuân theo quy tắc này tức là bạn đã làm được RESTful

Bạn nên làm gì tiếp theo?

Nếu bạn đã từng làm việc với RESTful rồi, bạn đọc bài viết này chỉ để củng cố thêm kiến thức thì mình hy vọng bạn đã hiểu hơn về nó qua cách giải thích của mình. Còn nếu bạn chưa từng làm việc với RESTful, hoặc đây là lần đầu tiên bạn nghe thấy khái niệm này, thì bạn nên làm một demo web service nhỏ áp dụng RESTful để hiểu hơn nhé, chứ có giải thích hay thế nào thì bài viết này của mình cũng chỉ là lý thuyết mà thôi.

Bài viết được viết dựa trên kinh nghiệm làm việc của mình và tham khảo một số nguồn. Xin nhận mọi gạch đá.


Tài liệu tham khảo: https://restfulapi.net


(*) CURL: là bộ thư viện được sử dụng để giúp thực hiện việc chuyển dữ liệu thông qua nhiều giao thức khác nhau như HTTP, FPT…