Hôm nay, Ngô Tôn IT xin hướng dẫn các bạn xây dựng 1 RESTful API trong PHP một cách cơ bản để làm nền tảng phát triển webservice sau này.
RESTful Là Gì?
REST là viết tắt của cụm từ Representational State Transfer là một kiểu kiến trúc được sử dụng trong việc giao tiếp giữa các máy tính (máy tính cá nhân và máy chủ của trang web) trong việc quản lý các tài nguyên trên internet. REST sử dụng các cách biểu diễn khác nhau để biểu diễn các nguồn tài nguyên như text, JSON, XML nhưng phổ biến nhất vẫn là JSON. REST được sử dụng rất nhiều trong việc phát triển các ứng dụng Web Services sử dụng giao thức HTTP trong giao tiếp thông qua mạng internet. Các ứng dụng sử dụng kiến trúc REST này thì sẽ được gọi là ứng dụng phát triển theo kiểu RESTful.
Một đặc tính quan trọng của dịch Web service RESTful là sử dụng một cách rõ ràng các phương thức HTTP theo cách một giao thức được xác định bởi RFC 2616. Ví dụ HTTP GET được xác định như là một phương thức sinh ra số liệu được sử dụng có chủ đích bởi các ứng dụng người dùng để thu thập tài nguyên, dữ liệu từ một máy chủ, hoặc thực thi một truy vấn mà máy chủ sẽ tìm kiếm và phản hồi cùng với một gói thông tin tương thích.
REST yêu cầu các nhà phát triển sử dụng phương thức HTTP một cách rõ ràng theo cách tương thích với giao thức chuẩn. Nguyên lý thiết kế REST cơ bản này thiết lập một ánh xạ 1-1 giữa các hành động tạo, đọc, cập nhật và xoá (CRUD) các quá trình vận hành và các phương thức HTTP. Theo cách ánh xạ này thì:
- Để truy xuất một tài nguyên, sử dụng GET.
- Để tạo một tài nguyên trên máy chủ, bạn cần sử dụng phương thức POST.
- Để thay đổi trạng thái một tài nguyên hoặc để cập nhật nó, sử dụng PUT.
- Để huỷ bỏ hoặc xoá một tài nguyên, sử dụng DELETE.
API là gì?
API là từ viết tắt của Application Programming Interface. Nó cho phép kết nối và trao đổi dữ liệu giữa hai hệ thống phần mềm riêng biệt. Một hệ thống phần mềm có thể nhúng các API bao gồm các hàm/thủ tục con (functions/sub-routines) mà có thể chạy bởi một hệ thống phần mềm khác.
Một sử dụng phổ biến của một API là khi bạn muốn để có được dữ liệu từ một ứng dụng (chẳng hạn như một công thức làm bánh) mà không cần phải thực sự truy cập các ứng dụng riêng của mình.
Để cho phép các hành động này diễn ra, ứng dụng đã xuất bản một API mà cụ thể cho phép cho các ứng dụng bên ngoài để thực hiện truy vấn đến các dữ liệu của nó và trả lại cho người dùng.
Trên trang web, điều này thường được thực hiện thông qua việc sử dụng RESTful URIs.
Ví dụ dưới đây dùng để lấy toàn bộ công thức làm bánh:
recipies.com/api/v1/recipe/cake
Xây dựng RESTful API
Với API chúng ta sẽ xây dựng ở đây sẽ bao gồm hai lớp. Một lớp trừu tượng (Abstract class in PHP) sẽ xử lý các phân tích của các URI và trả lại phản hồi, và một lớp con cụ thể sẽ chứa các điểm cuối (endpoints) cho API. Bằng cách này, chúng ta có được một lớp trừu tượng có thể tái sử dụng và có thể trở thành nền tảng của bất kỳ RESTful API khác.
Việc đầu tiên chúng ta sẽ tạo ra 1 file .htaccess với nội dung sau:
1 2 3 4 5 6 |
<IfModule mod_rewrite.c> RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule api/v1/(.*)$ api/v1/api.php?request=$1 [QSA,NC,L] </IfModule> |
Bây giờ chúng ta sẽ tạo 1 Abstract class.
Như đã đề cập trước đó, lớp này sẽ đóng vai trò như là một lớp bao phủ cho tất cả các endpoints tùy chỉnh thứ mà API sẽ sử dụng. Nó sẽ nhận request từ URI, xác định phương thức HTTP (GET, POST, PUT, DELETE) và lắp ráp dữ liệu được cung cấp trong header hoặc trong URI.
Khi đã hoàn thành, lớp trừu tượng sẽ đưa các thông tin yêu cầu vào một phương thức trong lớp cụ thể để thực hiện công việc. Sau đó lớp này sẽ xử lý và trả về HTTP response cho client.
Trước hết chúng ta sẽ khai báo class, properties, và constructor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
abstract class API { /** * Property: method * The HTTP method this request was made in, either GET, POST, PUT or DELETE */ protected $method = ''; /** * Property: endpoint * The Model requested in the URI. eg: /files */ protected $endpoint = ''; /** * Property: verb * An optional additional descriptor about the endpoint, used for things that can * not be handled by the basic methods. eg: /files/process */ protected $verb = ''; /** * Property: args * Any additional URI components after the endpoint and verb have been removed, in our * case, an integer ID for the resource. eg: /<endpoint>/<verb>/<arg0>/<arg1> * or /<endpoint>/<arg0> */ protected $args = Array(); /** * Property: file * Stores the input of the PUT request */ protected $file = Null; /** * Constructor: __construct * Allow for CORS, assemble and pre-process the data */ public function __construct($request) { header("Access-Control-Allow-Origin: *"); header("Access-Control-Allow-Methods: *"); header("Content-Type: application/json"); $this->args = explode('/', rtrim($request, '/')); $this->endpoint = array_shift($this->args); if (array_key_exists(0, $this->args) && !is_numeric($this->args[0])) { $this->verb = array_shift($this->args); } $this->method = $_SERVER['REQUEST_METHOD']; if ($this->method == 'POST' && array_key_exists('HTTP_X_HTTP_METHOD', $_SERVER)) { if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'DELETE') { $this->method = 'DELETE'; } else if ($_SERVER['HTTP_X_HTTP_METHOD'] == 'PUT') { $this->method = 'PUT'; } else { throw new Exception("Unexpected Header"); } } switch($this->method) { case 'DELETE': case 'POST': $this->request = $this->_cleanInputs($_POST); break; case 'GET': $this->request = $this->_cleanInputs($_GET); break; case 'PUT': $this->request = $this->_cleanInputs($_GET); $this->file = file_get_contents("php://input"); break; default: $this->_response('Invalid Method', 405); break; } } } |
Ở khai báo trên, chúng ta trả về header các thông tin như: Access-Control-Allow-Origin (header chỉ định domain được phép truy cập), Access-Control-Allow-Methods (xác định phương thức được phép truy cập), Content-Type (kiểu dữ liệu, ở đây chúng ta trả về là json).
Tiếp theo, chúng ta thêm 1 số hàm để xử lý dữ liệu trả về
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
abstract class API { ... public function processAPI() { if (method_exists($this, $this->endpoint)) { return $this->_response($this->{$this->endpoint}($this->args)); } return $this->_response("No Endpoint: $this->endpoint", 404); } private function _response($data, $status = 200) { header("HTTP/1.1 " . $status . " " . $this->_requestStatus($status)); return json_encode($data); } private function _cleanInputs($data) { $clean_input = Array(); if (is_array($data)) { foreach ($data as $k => $v) { $clean_input[$k] = $this->_cleanInputs($v); } } else { $clean_input = trim(strip_tags($data)); } return $clean_input; } private function _requestStatus($code) { $status = array( 200 => 'OK', 404 => 'Not Found', 405 => 'Method Not Allowed', 500 => 'Internal Server Error', ); return ($status[$code])?$status[$code]:$status[500]; } |
Chúng ta quan tâm đến hàm processAPI(). Công việc của nó là để xác định xem phương thức nào của lớp được thực thi khi nhận request từ phía client. Nếu có, nó sẽ gọi hàm được yêu cầu, nếu không sẽ trả về 404 response.
Đó là tất cả những gì cho lớp trừu tượng. Bây giờ, chúng ta sẽ làm một lớp Concrete class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
require_once 'API.class.php'; class MyAPI extends API { protected $User; public function __construct($request, $origin) { parent::__construct($request); // Abstracted out for example $APIKey = new Models\APIKey(); $User = new Models\User(); if (!array_key_exists('apiKey', $this->request)) { throw new Exception('No API Key provided'); } else if (!$APIKey->verifyKey($this->request['apiKey'], $origin)) { throw new Exception('Invalid API Key'); } else if (array_key_exists('token', $this->request) && !$User->get('token', $this->request['token'])) { throw new Exception('Invalid User Token'); } $this->User = $User; } /** * Example of an Endpoint */ protected function example() { if ($this->method == 'GET') { return "Your name is " . $this->User->name; } else { return "Only accepts GET requests"; } } } |
Ở trên có 2 lớp APIKey và User mình không nhắc tới trong bài này nhưng nó sẽ được sử dụng cho ứng dụng API với namespace là Models, sẽ thực hiện các chức năng về API key và dữ liệu user.
Đơn giản là vậy, với mỗi endpoint bạn muốn trong API bạn có thể thêm giống như MyAPI class này cho phù hợp yêu cầu.
Để thực hiện các API chúng ta cần phải tạo ra các tập tin PHP mà trong file .htaccess mình đã cấu hình. Trong ví dụ này mình đặt tên nó api.php:
1 2 3 4 5 6 7 8 9 10 11 |
// Requests from the same server don't have a HTTP_ORIGIN header if (!array_key_exists('HTTP_ORIGIN', $_SERVER)) { $_SERVER['HTTP_ORIGIN'] = $_SERVER['SERVER_NAME']; } try { $API = new MyAPI($_REQUEST['request'], $_SERVER['HTTP_ORIGIN']); echo $API->processAPI(); } catch (Exception $e) { echo json_encode(Array('error' => $e->getMessage())); } |
Nếu bạn truy cập vào địa chỉ /api/v1/example ( nếu có dữ liệu User và Token cần thiết) bạn sẽ thấy được kết quả.
Các bạn hãy tìm hiểu tiếp nhé. 😀
I enjoy, lead to I found exactly what I used to be taking a look for.
You have ended my four day lengthy hunt! God Bless you man. Have a
nice day. Bye