Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Code
  2. WordPress
Code

Giới thiệu WP REST API

by
Difficulty:BeginnerLength:LongLanguages:
This post is part of a series called Introducing the WP REST API.
WP REST API: Setting Up and Using Basic Authentication

Vietnamese (Tiếng Việt) translation by Andrea Ho (you can also view the original English article)

Được lập vào năm 2003, WordPress đã phát triển từ một nền tảng blog đơn giản thành một hệ thống quản lý nội dung toàn diện. Trong những năm qua, WordPress đã hoàn thiện đủ để đáp ứng nhu cầu của đa số người dùng trực tuyến và đây là lý do nó hỗ trợ cho hơn 20% số lượng website hiện nay.

Với nhiều tính năng mới được bổ sung vào WordPress, một trong bổ sung gần đây nhất là REST API - cho phép các ứng dụng và nền tảng khác tương tác với WordPress. Đó là một sự bổ sung mang tính cách mạng sẽ giúp các nhà phát triển xây dựng các ứng dụng riêng và các hệ thống tích hợp với WordPress. Vì nó cung cấp khả năng tạo mới và lấy nội dung từ bất kỳ ứng dụng hoặc website nào khác, mà không cần cài đặt WordPress trên website đó, nó cho phép sử dụng WordPress cùng với bất kỳ ngôn ngữ lập trình hoặc nền tảng nào.

Trong loạt bài viết nhiều phần này, chúng ta sẽ xem xét API REST của WP và làm thế nào nó có thể tạo ra trải nghiệm người dùng mà trước đây không thể thực hiện hoặc khó thực hiện với WordPress. Đầu tiên chúng ta sẽ xem xét các khái niệm cơ bản của REST và JSON, sau đó khám phá các tùy chọn hỗ trợ sẵn thông qua WP API REST.

Dưới đây là một số resource mà tôi thấy hữu ích cho các khái niệm cơ bản bao gồm HTTP, REST và JSON. Tôi khuyên bạn nên xem chúng nếu bạn vẫn chưa xem qua:

Trước khi chúng ta bắt đầu với chủ đề này, ta hãy có một cái nhìn ngắn gọn về kiến ​​trúc REST thực sự và làm quen với thuật ngữ chung của nó.

Bắt đầu với REST

Để bắt đầu chủ đề này, chúng ta hãy xem qua kiến ​​trúc REST (Representational State Transfer) và một số khái niệm phổ biến nhất của nó. Hiểu về REST là việc cần thiết khi phát triển các ứng dụng bằng cách sử dụng kiểu kiến ​​trúc REST.

REST là một kiểu kiến ​​trúc giúp tạo và tổ chức một hệ thống phân tán. Nó mô tả các website như là một hypermedia phân tán (ứng dụng truyền thông với sự hỗ trợ của máy tính) có resource liên kết giao tiếp bằng cách trao đổi hình thức hiển thị của state (trạng thái) của resource.

Tài nguyên (resources) là các block chủ yếu của kiến ​​trúc REST. Trên thực tế, chúng là những block chủ yếu của bản thân website đến mức đô mà đội khi web được gọi là "resource-oriented".

Khi nói về WordPress, các resource này là các entity riêng biệt như post, page, comment, user và custom post type. Để tương tác với các resource, các URI (Uniform Resource Identifier) được sử dụng, và như tên gọi, đó là định danh cho một resource.

Một dịch vụ RESTful xử lý các URI như phương pháp chính để chỉ ra một resource cơ bản. Các resource này có thể có một số representation (hình thức hiển thị). Ví dụ: một file hình ảnh có thể có định dạng .JPG, .GIF hoặc .PNG. Mối quan hệ giữa resource và URI là one-tp-many (một-nhiều). Một URI chỉ có thể trỏ đến một resource cụ thể nhưng một resource có thể có nhiều hơn một URI.

Danh sách tất cả các resource hiện được hỗ trợ bởi WP REST API như sau:

  • Posts
  • Pages
  • Media
  • Custom Post Types
  • Post Meta
  • Revisions
  • Comments
  • Terms
  • Users

Chúng ta có thể thực hiện các hành động khác nhau trên các resource này bằng cách sử dụng các phương thức HTTP.

Các phương thức HTTP

REST API về cơ bản cho phép thực hiện các thao tác CRUD (Tạo - Đọc - Cập nhật - Xóa) trên các resource sử dụng HTTP. Với mục đích này, REST sử dụng tập hợp các phương thức của yêu cầu HTTP như sau:

  1. GET: để đọc hoặc lấy dữ liệu từ một resource
  2. POST: được dùng để tạo resource mới
  3. PUT: được dùng để cập nhật một resource
  4. DELETE: được dùng để xoá một resource
  5. HEAD: được dùng để kiếm tra nếu một resource có tồn tại nhưng không trả về phần hiển thị của nó
  6. OPTIONS: dùng để lấy tất cả phương thức được resource hỗ trợ

Trong dịch vụ RESTful, các phương thức này có ý nghĩa được định nghĩa rõ ràng. Bốn phương thức đầu tiên trong danh sách trên là một phần của hoạt động CRUD tức là chúng đọc, tạo mới, cập nhật và xóa các entity. Hai phương thức cuối cùng hỗ trợ máy khách trong việc xác định liệu một resource có tồn tại hay không và những phương thức HTTP nào có sẵn để nó thực hiện các hoạt động tiếp theo.

Một yêu cầu GET lấy thông tin và là idempotent tức là một máy khách có thể gọi nó nhiều lần nhưng sẽ không làm ảnh hưởng đến trạng thái của một resource.

Để có được tất cả các bài viết bằng cách sử dụng API WP REST, chúng tôi sử dụng endpoint sau đây:

Endpoint bên trên sẽ trả về một collection tất cả các entity bài viết.

Khi endpoint sau được kích hoạt, nó sẽ trả về một entity cụ thể, tức là post có id là 100:

Yêu cầu POST tạo một entity mới và yêu cầu PUT sẽ thay thế entity đó bằng phiên bản mới.

Yêu cầu POST sau có thể được sử dụng để tạo một bài đăng mới (gửi đi cùng body của yêu cầu mà chúng ta sẽ xem xét trong phần sau của loạt bài này) bằng cách sử dụng API WP REST:

Và yêu cầu PUT sau sẽ cập nhật bài đăng có id là 100:

Yêu cầu DELETE xóa resource khỏi hệ thống. Kiểu yêu cầu này, cùng với yêu cầu PUT có thể lặp lại, có nghĩa là việc gọi các phương thức này sẽ có tác dụng tương tự trên hệ thống. Ví dụ, nếu bạn gọi một yêu cầu PUT nhiều lần trên một resource (với cùng các đối số), kết quả sẽ giống nhau. Điều này tương tự với yêu cầu DELETE. Xóa một resource nhiều lần sẽ có tác dụng tương tự tức là resource sẽ bị xóa (hoặc sẽ trả về một lỗi trong trường hợp resource đã bị xóa).

Ngoài các hành động CRUD này, một dịch vụ RESTful cung cấp thêm hai phương thức là OPTIONSHEAD. Các phương thức này hữu ích khi một máy khách cần kiểm tra những resource nào có sẵn trên hệ thống và hoạt động nào họ hỗ trợ, do đó cung cấp cách tự ghi tài liệu để máy khách khám phá thêm hệ thống và thực hiện các hành động. Chúng ta sẽ thấy hai phương thức này hoạt động trong phần sau hướng dẫn này.

Tìm hiểu thêm về route và endpoint

Lưu ý rằng trong ví dụ đầu tiên ở trên, chúng tôi đã sử dụng endpoint sau:

Endpoint là các hàm có sẵn thông qua API và chúng thực hiện một số hoạt động như truy xuất bài đăng (mà chúng ta đang thực hiện ở trên), tạo người dùng mới hoặc cập nhật thông tin meta cho bài đăng. Ngoài ra, chúng ta có thể nói rằng một endpoint kích hoạt một phương thức thực hiện một tác vụ cụ thể. Các endpoint này phụ thuộc vào phương thức HTTP được liên kết với chúng. Trong ví dụ trên, chúng tôi đang sử dụng phương thức GET để truy xuất tất cả các bài đăng.

Route cho endpoint ở trên là như sau:

Một route về cơ bản là một tên gọi để truy cập endpoint. Một route có thể có nhiều endpoint dựa trên phương thức HTTP. Vì vậy, các route trên có endpoint sau để tạo một bài mới:

Endpoint này, khi được kích hoạt với các tham số được cung cấp, sẽ tạo một entity bài đăng mới.

Xem xét route sau:

Route này trỏ đến entity Bài đăng có id là 100. Nó có ba endpoint sau:

  1. GET wp/v2/posts/100: Có thể được sử dụng để truy xuất bài đăng có id là 100. Nó kích hoạt phương thức get_item().
  2. PUT wp/v2/posts/100: Có thể được sử dụng để cập nhật bài đăng có id là 100. Nó kích hoạt phương thức update_item().
  3. DELETE wp/v2/posts/100: Xóa bài đăng có id là 100. Nó kích hoạt phương thức delete_item().

Chúng ta sẽ tìm hiểu thêm về bên trong của WP REST API, đó là cấu trúc class và các phương thức trong đó ở phần cuối cùng của loạt bài này.

Bây giờ hãy nhắc lại kiến ​​thức của chúng ta về một số mã phản hồi HTTP phổ biến và chúng có ý nghĩa gì.

Các mã phản hồi HTTP

Máy chủ phản hồi yêu cầu bằng cách trả về phản hồi chứa status code (mã trạng thái) của HTTP. Các mã này là những con số có ý nghĩa được định nghĩa trước. Ví dụ: bất kỳ ai sử dụng web sẽ quen thuộc với status code 404 tóm tắt rằng resource, người dùng đang tìm kiếm, không được tìm thấy.

Phản hồi của máy chủ cũng phụ thuộc vào kiểu phương thức HTTP mà chúng tôi sử dụng trong yêu cầu được gửi đi, như chúng ta sẽ thấy tiếp theo đây.

Sau đây là một số mã phản hồi HTTP phổ biến cùng với ý nghĩa của chúng, chúng ta sẽ gặp phải khi làm việc với WP API REST và ý nghĩa của chúng:

  • 200 - OK: Có nghĩa là yêu cầu đã được hoàn tất thành công và máy chủ đã trả về phản hồi. Thường được trả lại sau khi yêu cầu GET thành công.
  • 201 - Created: Thường được trả lại sau khi yêu cầu POST thành công. Tóm tắt rằng resource đã được tạo ra.
  • 400 - Bad Request: được trả về từ máy chủ khi một yêu cầu được gửi cùng với một số thông số bị thiếu hoặc không hợp lệ. Thường được trả về theo yêu cầu POST hoặc PUT.
  • 401 - Unauthorized: Nghĩa là người dùng đó không được phép thực hiện một số hành động nhất định. Ví dụ: người dùng đã cố gắng tạo mới hoặc xóa một resource mà không cung cấp xác thực.
  • 403 - Forbidden: Có nghĩa là máy chủ đã hiểu yêu cầu nhưng từ chối hoàn thành nó do chưa xác thực. Điều này xảy ra khi người dùng cung cấp bằng chứng xác thực nhưng họ không có đủ quyền để thực hiện hành động.
  • 404 - Not Found: nổi tiếng nhất trong các status code. Tóm tắt rằng không thể tìm thấy resource mà dùng đang tìm kiếm.
  • 405 - Method not allowed: Có nghĩa là một phương thức HTTP được cung cấp trong yêu cầu không được resource hỗ trợ. Một ví dụ là một người dùng cố gắng cập nhật resource chỉ cho phép đọc.
  • 410 - Gone: Có nghĩa là resource đã được chuyển đến một vị trí khác. Một ví dụ là về việc đang cố xóa một resource đã bị xóa mất và nó đã được bỏ đi.
  • 500 - Internal Server Error: lỗi này được trả về khi máy chủ gặp phải tình trạng không mong muốn và không thể hoàn thành yêu cầu.
  • 501 - Not Implemented: Có nghĩa là máy chủ không hỗ trợ chức năng để hoàn thành yêu cầu. Thường xảy ra khi một máy chủ nhận được một phương thức yêu cầu mà nó không nhận ra.

Chúng ta sẽ xem xét các phương thức HTTP này và status code tỉ mỉ hơn hơn khi chúng ta thực sự bắt đầu làm việc với API. Nhưng trước đó, chúng ta hãy xem các lý do để sử dụng REST API với WordPress và những lợi thế mà nó cung cấp cho nhà phát triển lẫn người dùng. Sau tất cả, tôi cần bạn thực sự chú tâm để làm theo cùng tôi trong loạt bài này.

Tại sao sử dụng JSON REST API cho WordPress?

REST và JSON cùng nhau cung cấp một cơ chế để tạo các ứng dụng mạnh mẽ bằng cách sử dụng back-end của WordPress. Các ví dụ quan trọng nhất là các ứng dụng di động yêu cầu trao đổi dữ liệu giữa máy khách (thiết bị) và máy chủ. Theo dõi giới hạn băng thông khi sử dụng dữ liệu di động, JSON cung cấp một giải pháp nhỏ gọn để thay thế cho các giải pháp dựa trên XML.

Vì JSON là một định dạng dựa văn bản để lưu trữ dữ liệu, nó có thể được sử dụng linh hoạt với đa số các ngôn ngữ lập trình. Do đó, JSON đóng vai trò như một connector khi trao đổi dữ liệu giữa các nền tảng khác nhau mà cả máy móc lẫn con người đều có thể đọc được.

Với việc sử dụng API giống như đã đề cập, nội dung của website WordPress của bạn không chỉ bị giới hạn bởi chính nó mà còn có thể được truy cập bởi các website và ứng dụng máy khách khác. Khi API hiển thị một số phần của chức năng nội bộ, các ứng dụng khách từ xa có thể tương tác với website của bạn để cập nhật hoặc tạo nội dung mới. Nó cũng cho phép để lấy một số nội dung từ một website WordPress hiện có và hiển thị nó trên một số website khác.

Với sự bùng phát của các framework JavaScript từ máy khách như Angular, Backbone hoặc Ember, bây giờ nó đã có thể sử dụng một trong số framework này để tạo ra trải nghiệm người dùng phong phú trong khi vẫn sử dụng back-end của WordPress.

Như đã đề cập, một số trường hợp có thể sử dụng WP REST API là:

  • Ứng dụng di động
  • Bảng quản trị tùy chỉnh cho WordPress
  • Ứng dụng một trang (SPA)
  • Tích hợp với các nền tảng máy chủ khác (như Ruby, .NET và Django, v.v.)
  • và nhiều nữa…

WP REST API thực sự mở ra một thế giới mới về khả năng mà giới hạn duy nhất là trí tưởng tượng của một người.

Lịch sử ngắn gọn về WP REST API trong WordPress

Trước khi REST API dựa vào JSON, API thường được sử dụng để tương tác từ xa với WordPress là API XML-RPC và nó vẫn là một phần của WordPress core. Vấn đề của XML là nó không gọn nhẹ như định dạng JSON và việc phân tích cú pháp của nó không hiệu quả. Traversing XML cũng là một vấn đề lớn gây đau đầu, trong khi duyệt qua một đối tượng JSON dễ dàng như việc xử lý một đối tượng native JavaScript.

Plugin REST API đầu tiên được giới thiệu cho WordPress là JSON API đã được phát hành vào năm 2009. Nó được xây dựng tại The Musemum of Modern Art cho trang blog của họ Inside/Out. Front-end của blog này dùng Ruby on Rails, do đó, để lấy các bài đăng và thêm các bình luận vào back-end của WordPress, một API đã được phát triển. Plugin này cung cấp các giao diện để truy xuất nội dung và gửi các nhận xét tới phần cuối của WP. Mặc dù, không được cập nhật trong hơn hai năm, plugin này vẫn còn hiện diện trong kho lưu trữ chính thức và có sẵn.

Ngoài plugin JSON API, WordPress.com đã cung cấp JSON API thông qua plugin JetPack.

Như chúng ta đã biết, ngày nay API WP REST là một plugin nổi bật được đề xuất bởi Ryan McCue như một phần của GsoC (Google Summer of Code) 2013. Nó vẫn chưa được tích hợp (hoàn toàn) với WordPress core trong một bản phát hành trong tương lai. Phiên bản hiện tại 2.0 của plugin đang ở trạng thái beta và bao gồm một phần của WordPress core trong phiên bản 4.4. Đó là một dự án cộng đồng do Ryan McCueRachel Baker dẫn đầu. Trang kho lưu trữ chính thức của plugin nằm trên GitHub và tài liệu chính thức có thể được tìm thấy trên website của nó.

Hiện trạng của WP REST API

Như đã đề cập, WP REST API hiện đang ở trạng thái plugin và đang được phát triển tích cực trên GitHub. Nó đã được bao gồm một phần trong WordPress core trong phiên bản 4.4 và Ryan đã mô tả kế hoạch trong đề xuất hợp nhất của mình trên WordPress.org.

Theo đề nghị hợp nhất, WP REST API sẽ được tích hợp vào WP core trong hai giai đoạn như được mô tả dưới đây:

Kết hợp mã cơ sở hạ tầng

Theo đề xuất, ở giai đoạn ban đầu, chỉ có code cấp nền tảng mới được sáp nhập vào WP core trong bản phát hành 4.4. Code cơ sở nền tảng này là cơ sở thực sự của WP REST API vì nó bao gồm JSON serialization/deserialization, linking , embedding và quan trọng nhất - lớp routing (định tuyến) dẫn hướng API. Code này không bao gồm các endpoint và các class controller của chúng, nhưng nó mang đến cơ sở để xây dựng các API bên trong WordPress.

Tại thời điểm viết bài, state này đã được hoàn thành và code nền tảng đã được tích hợp trong WordPress core trong phiên bản 4.4.

Hợp nhất các endpoint

Endpoint cho post, page, users và taxonomy sẽ được tích hợp trong WP core trong bản phát hành 4.5, tức là một bản phát hành sau khi hợp nhất code nền tảng. Endpoint là diều khiến API trở nên hữu ích cho đa số máy khách. Chúng có nhiều điều phức tạp, gồm mapping dữ liệu ngoại tuyến trong định dạng JSON sang kiểu dữ liệu nguyên thuỷ của WordPress, và ngược lại. Chúng chiếm 2/3 code của bản thân API với khoảng 5500 dòng.

Chiến lược ở đây là xây dựng tự tin của nhà phát triển trong API bằng cách trước tiên cung cấp code nền tảng trong core. Điều này cho phép các nhà phát triển theme và plugin xây dựng các API tùy chỉnh để đưa vào theme và plugin của họ. Nhưng kể từ đó, các endpoint sẽ không được bao gồm trong giai đoạn này, điều này sẽ hạn chế tiện ích ban đầu của API.

Khoảng cách giữa hai bản phát hành sẽ cung cấp cho đội ngũ committer WP core đủ thời gian để xem xét các endpoint API.

Một ưu điểm khác mà WP REST API sẽ mang đến cho cộng đồng WordPress là việc sử dụng GitHub trong việc kiểm soát các phiên bản của dự án. Vì tất cả những đóng góp cho WordPress được thực hiện thông qua SVN và Trac, đội ngũ WP REST API khá tự tin về việc cải thiện quá trình đóng góp bằng cách kết nối giữa Trac và GitHub.

Sau khi hợp nhất API vào core, chúng ta có thể hy vọng thấy sự phát triển nhanh chóng trong các lĩnh vực khác bao gồm xác thực OAuth 1.0a.

Những công cụ thương mại

Để bắt đầu thử nghiệm với WP REST API , chúng ta sẽ cần một máy khách HTTP sẽ được sử dụng để gửi các yêu cầu đến máy chủ và xem phản hồi. Đây là vấn đề chọn lựa của bạn nhưng tôi sẽ sử dụng Postman cho Google Chrome xuyên suốt loạt bài này. Các lựa chọn thay thế khác cho Postman là:

Postman cho phép chúng ta tạo các yêu cầu HTTP nhanh chóng với các phương thức khác nhau, xem phản hồi từ máy chủ và kiểm tra cấu hình để xác thực. Tất cả các tính năng này sẽ cực kỳ tiện dụng khi làm việc với WP REST API.

Điều tiếp theo chúng ta cần có trên máy chủ là WP-CLI. Với WP-CLI, chúng ta có thể quản lý từ xa các cài đặt WordPress của mình từ bên trong console mà không cần phải mở cửa sổ trình duyệt. Chúng ta sẽ sử dụng WP-CLI trong phần tiếp theo của loạt bài này khi thiết lập xác thực OAuth 1.0a. Bạn có thể tìm thấy hướng dẫn cài đặt trên website chính thức.

Ngoài một máy khách HTTP và WP-CLI, chúng ta cũng cần dữ liệu demo để cài đặt WordPress. Tôi khuyên bạn nên thử Theme Unit Test (XML) hoặc plugin Demo Data Creator cho phép tạo nhiều bài đăng, trang và người dùng.

Cài đặt Plugin

Tại thời điểm viết hướng dẫn này, phiên bản ổn định hiện tại là 1.2.2 có thể được tìm thấy trên kho lưu trữ chính thức. Trong loạt bài này, chúng tôi sẽ làm việc với phiên bản 2.0 vì nó đã được viết lại từ đầu và mang lại nhiều thay đổi có tính đột phá. Bạn có thể lấy phiên bản beta 2 từ trang plugin chính thức của nó hoặc từ kho lưu trữ GitHub của nó.

Xin lưu ý rằng bạn không nên cài đặt bản beta hiện tại vào môi trường production. Tôi đã thiết lập một môi trường WordPress cục bộ và tôi khuyên bạn nên làm như vậy khi phát triển và thử nghiệm.

Để cài đặt plugin từ kho lưu trữ GitHub, hãy mở terminal của bạn và kéo plugin sau khi vào thư mục /wp-content/plugins/ của bạn:

Plugin sẽ được tải xuống trong thư mục /WP-API/.

Bạn có thể kích hoạt nó từ WordPress admin để tiếp tục dùng thử nó.

Để plugin WP REST API hoạt động, bạn cần phải kích hoạt pretty permalinks với WordPress. Tôi giả sử bạn đã biết thực hiện điều cơ bản này. Nếu bạn chưa biết, thì đừng lo lắng vì WordPress.org sẽ giúp bạn.

Đánh giá tính khả dụng của API

Sau khi plugin đã được cài đặt, chúng tôi có thể đánh giá tính khả dụng của API trên máy chủ của chúng tôi. Chúng tôi không giới hạn việc chỉ sử dụng API trên website của chúng tôi, trên thực tế, chúng tôi sử dụng API trên bất kỳ website nào đã được cài đặt và kích hoạt. Để kiểm tra API trên các website khác, chúng tôi có thể gửi yêu cầu HEAD tới website như sau bằng cách sử dụng máy khách HTTP của bạn:

Yêu cầu này sẽ phản hồi giông như sau trong phần header:

Response Header

Header Link dẫn đến root của WP API trong trường của chúng ta:

API cũng có thể được khai thác thông qua javaScript (hoặc jQuery) của trình duyệt bằng cách truy vấn DOM cho thành phần thích hợp <link> giống như dưới đây:

Từ giờ chúng ta có thể bắt đầu lấy nội dung từ site qua WP REST API. Xin chú ý rằng chỉ có những yêu cầu đã được xác thực mới có thể điều chỉnh hoặc cập nhật nội dung của site và tôi đang rút gọn chủ đề thiết lập xác thực cho hai phần tiếp theo của loạt bài này.

Tiếp theo là gì?

Trong phần giới thiệu của loạt bài này, chúng tôi đã tìm hiểu khá nhiều về kiến ​​trúc REST, WP API REST và HTTP. Đầu tiên chúng ta đã xem xét các khái niệm cơ bản như kiến ​​trúc REST là gì và cách nó có thể giúp các nhà phát triển tạo ra các ứng dụng tốt hơn. Sau đó, chúng tôi đã tìm hiểu về HTTP, các phương thức và các kiểu phản hồi của nó. Chúng ta cũng biết lịch sử ngắn gọn về các API trong WordPress và WP API REST khác biệt với các API trước đó ra sao.

Trong phần cuối cùng của hướng dẫn này, chúng ta đã cài đặt plugin từ GitHub. Sau đó, chúng tôi đã làm quen với yêu cầu HEAD để xác định tính khả dụng của API trên máy chủ của chúng ta và trên các máy chủ khác.

Sau khi thiết lập môi trường làm việc cơ bản, chúng ta sẵn sàng làm việc với các tính năng mà WP REST API cung cấp. Trong phần tiếp theo của loạt bài này, chúng tôi sẽ xem xét các cách thiết lập các phương thức xác thực do API hỗ trợ. Các phương thức xác thực này sẽ được yêu cầu khi gửi yêu cầu truy xuất, tạo mới, cập nhật hoặc xóa nội dung. Vậy nên hãy đón xem nhé.


Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.