ตัวอย่างการใช้งาน API ด้วย Kotchasan
จุดเด่นของ Kotchasan อย่างหนึ่งคือความเร็ว ทำให้การนำ Kotchasan ไปใช้งานเป็น API สามารถทำได้ดีที่สุด เนื่องจากจะทำให้ API สามารถรับจำนวน Request ได้มากกว่า แถมการเขียนโค้ดของ Kotchasan เพื่อใช้งานเป็น API ยังง่ายดายอีกด้วย
ในบทความนี้ผมจะแสดงตัวอย่างการสร้างและใช้งาน API สำหรับตรวจสอบการ Login และการสร้าง Refresh Token เพื่อนำไปใช้งานต่ออย่างง่ายๆ
ไฟล์แรก api.php เป็นจุดเริ่มต้นของ API เลย ผมไม่ได้ใช้ index.php เนื่องจากว่าปกติแล้ว เราจะใช้ index.php สำหรับงานเว็บทั่วไป เพื่อไม่ให้สับสนผมเลยแยกกันไปคนละไฟล์เลย หากนำไปใช้ร่วมกับโปรเจ็คอื่นๆ จะได้เอาไปง่ายๆ (จะใช้เป็น index.php ก็ได้นะครับหากเป็น API อย่างเดียว)
เนื้อหาของไฟล์ก็จะคล้ายๆกับ index.php ปกติ ต่างกันแค่
ไฟล์ที่สอง Gcms/Router.php หรือ Gcms\Router จริงๆก็เป็นการยกเอา \Kotchasan\Router มานั่นแหละ แค่เพิ่มกฏของ API เข้าไปบรรทัดเดียว
/(api)\.php\/([a-z0-9]+)\/([a-z]+)\/([a-z]+)/i จะถูกใช้กับ API ตามรูปแบบนี้เท่านั้น api.php/modules/method/action ซึ่งจะถูกเรียกไปยัง \Modules\Method\Model::action โดยอัตโนมัติ เช่น api.php/v1/user/login หมายถึง \V1\User\Model::login เป็นต้น
ไฟล์ที่สาม /modules/index/controllers/api.php หรือ \Index\Api\Controller เป็น Controller หลักของ Api ทำหน้าที่ในการตรวจสอบ Token หลัก และทำหน้าที่ในการส่งต่อไปยัง Model ปลายทางตาม URL ของ API
โค้ดด้านบนคือส่วนสำหรับการตรวจสอบ Token ที่มาจากการส่งผ่าน header ตามรูปแบบ Authorization : Bearer api_token ซึ่งไม่ได้มีอะไรซับซ้อน เป็นการตรวจสอบความถูกต้องตรงกันของ api_token กับตัวแปร $cfg->api_token ที่อยู่ใน \Gcms\Config.php เท่านั้น
ค่าของ $api_token สามารถเปลี่ยนได้นะครับ โดยมีเงื่อนไขคือ ต้องเป็นตัวเลขและตัวอักษรภาษาอังกฤษ เท่านั้น
ถ้าหาก Token ที่ถูกส่งมาถูกต้อง ก็จะเป็นการเรียกไปยัง Model ปลายทาง ตาม URL ที่ระบุมา
ไฟล์สุดท้าย /modules/v1/models/user.php หรือ \V1\User\Model เป็นคลาสปลายทางตาม URL ที่ระบุมา หากมี URL รูปแบบอื่นก็สร้าง Class ขึ้นมารับค่าเพิ่มเติมได้เลย เหตุผลที่ผมใช้โฟลเดอร์ v1 เนื่องมาจากว่าหากมีการอัปเกรด API ในภายหลังเป็น v2 v3 .... คนที่ใช้เวอร์ชั่นก่อนหน้าจะยังสามารถใช้งานได้อยู่ (เป็นรูปแบบที่นิยมกัน เช่น API ของ Facebook ก็มีแบบนี้)ส่วนโค้ดในคลาส ก็เป็นการดำเนินการกับฐานข้อมูลปกติ ผมคงไม่อธิบายนะครับ ไปเปิดดูกันเอา
ในส่วนของหน้าเว็บปกติ index.php ผมได้เขียนตัวอย่างการเรียกใช้งาน API ที่เราได้สร้างในบทความนี้ไว้เป็นตัวอย่าง สามารถดูโค้ดตัวอย่างได้ที่ /modules/index/controllers/index.php หรือ \Index\Index\Controller โดยตัวอย่างจะมีการทำงานอยู่ 2 ขั้นตอน
ในขั้นตอนแรก โค้ดด้านบนจะเรียกไปยัง URL api.php/v1/user/login โดยมีพารามิเตอร์ 3 ตัว คือ Authorization : Bearer api_token ที่ header และ username และ password ที่ต้องการตรวจสอบการ login ผลลัพท์จะได้เป็นข้อความรูปแบบ JSON ที่ $result ซึ่งถ้าสำเร็จจะมีการส่ง refreshToken กลับมา
ตัวแปร refreshToken หรือ Refresh Token ในที่นี้เปรียบเสมือน user_id ของคนที่ login ซึ่งผมใช้เป็นรูปแบบอักขระแบบสุ่มธรรมดาเข้ารหัสร่วมกับ username เพื่อไม่ให้รหัสที่ได้มันซ้ำกัน และ Refresh Token จะเปลี่ยนทุกครั้งที่มีการ login ซ้ำ ซึ่งรูปแบบการจัดการกับ Refresh Token ไม่ได้มีกฏอะไรตายตัวนะครับ ผมใช้วิธีนี้เพราะมันเหมาะสมเพียงพอแล้ว บางคนอาจสงสัยว่าทำไมผมไม่ใช้ JWT ซึ่งจริงๆก็ใช้ได้ไม่ได้มีข้อห้ามแต่ผมเห็นว่า JWT ไม่ได้มีประโยชน์อะไรในกรณีนี้เลยไม่ได้ใช้ (จุดประสงค์ของ JWT เพื่อลดการติดต่อกับ Database ซึ่งในกรณีนี้ยังไงก็ต้องตรวจสอบกับ Database อยู่แล้วเลยไม่รู้จะใช้ไปทำไม)
ขั้นตอนที่สองเป็นการเอา Refresh Token ที่ได้ไปเรียกข้อมูลของตัวเองออกมาที่ URL api.php/v1/user/me โดยมีพารามิเตอร์ 2 ตัว คือ Authorization : Bearer api_token ที่ header (ตามตัวอย่างกำหนดไปแล้วที่คำสั่งแรก) และ refreshToken ซึ่งเป็น Refresh Token ที่ได้จากการ login นั่นเอง
ผลลัพท์ที่ได้ ถ้าสำเร็จก็จะเป็นข้อมูลของ User เจ้าของ Refresh Token รูปแบบ JSON ที่ $result
ประโยชน์ของ API เองไม่ได้มีไว้เพื่อใช้ในไซต์ของตัวเองเท่านั้น ยังต้องสามารถเรียกใช้จากภายนอกได้ด้วย ตัวอย่างด้านล่างจะเป็นการทดสอบการเรียก API โดยใช้ Advance REST client ซึ่งเป็นส่วนขายของ Chrome
ในขั้นตอนแรก การตรวจสอบการ login ด้วย API โดยการเรียกไปยัง API api.php/v1/user/login โดยมีพารามิเตอร์ username : demo และ password : demo ผลลัพท์จาก API จะได้เป็น Refrsh Token ถ้าการ login สำเร็จ ตามรูปเลย ในขั้นตอนที่สอง นำ refreshToken ที่ได้มาส่งต่อ ไปยัง api.php/v1/user/me เพื่อขอข้อมูล user ได้ผลลัพท์ดังรูป
เว็บไซต์ตัวอย่าง https://api.kotchasan.com
ในบทความนี้ผมจะแสดงตัวอย่างการสร้างและใช้งาน API สำหรับตรวจสอบการ Login และการสร้าง Refresh Token เพื่อนำไปใช้งานต่ออย่างง่ายๆ
// load Kotchasan
include 'load.php';
// Initial Kotchasan Framework
$app = Kotchasan::createWebApplication('Gcms\Config');
$app->defaultRouter = 'Gcms\Router';
$app->defaultController = 'Index\Api\Controller';
$app->run();
ไฟล์แรก api.php เป็นจุดเริ่มต้นของ API เลย ผมไม่ได้ใช้ index.php เนื่องจากว่าปกติแล้ว เราจะใช้ index.php สำหรับงานเว็บทั่วไป เพื่อไม่ให้สับสนผมเลยแยกกันไปคนละไฟล์เลย หากนำไปใช้ร่วมกับโปรเจ็คอื่นๆ จะได้เอาไปง่ายๆ (จะใช้เป็น index.php ก็ได้นะครับหากเป็น API อย่างเดียว)
เนื้อหาของไฟล์ก็จะคล้ายๆกับ index.php ปกติ ต่างกันแค่
- มีการกำหนด Router ต่างหาก เพื่อให้ URL ของ API ดูสวยงาม
$app->defaultRouter = 'Gcms\Router'; - เปลี่ยน defaultController เป็น Index\Api\Controller (เพื่อแยกการทำงานออกจากเว็บไซต์ปกติ)
$app->defaultController = 'Index\Api\Controller';
ไฟล์ที่สอง Gcms/Router.php หรือ Gcms\Router จริงๆก็เป็นการยกเอา \Kotchasan\Router มานั่นแหละ แค่เพิ่มกฏของ API เข้าไปบรรทัดเดียว
protected $rules = array(
.........
'/(api)\.php\/([a-z0-9]+)\/([a-z]+)\/([a-z]+)/i' => array('', 'module', 'method', 'action'),
..........
);
/(api)\.php\/([a-z0-9]+)\/([a-z]+)\/([a-z]+)/i จะถูกใช้กับ API ตามรูปแบบนี้เท่านั้น api.php/modules/method/action ซึ่งจะถูกเรียกไปยัง \Modules\Method\Model::action โดยอัตโนมัติ เช่น api.php/v1/user/login หมายถึง \V1\User\Model::login เป็นต้น
ไฟล์ที่สาม /modules/index/controllers/api.php หรือ \Index\Api\Controller เป็น Controller หลักของ Api ทำหน้าที่ในการตรวจสอบ Token หลัก และทำหน้าที่ในการส่งต่อไปยัง Model ปลายทางตาม URL ของ API
if (!preg_match('/^Bearer\s'.self::$cfg->api_token.'$/', $request->getHeader('Authorization'))) {
// Token ไม่ถูกต้อง
} else {
// Token ถูกต้อง
}
โค้ดด้านบนคือส่วนสำหรับการตรวจสอบ Token ที่มาจากการส่งผ่าน header ตามรูปแบบ Authorization : Bearer api_token ซึ่งไม่ได้มีอะไรซับซ้อน เป็นการตรวจสอบความถูกต้องตรงกันของ api_token กับตัวแปร $cfg->api_token ที่อยู่ใน \Gcms\Config.php เท่านั้น
/**
* API Token
*
* @var string
*/
public $api_token = '080042cad6356ad5dc0a720c18b53b8e53d4c274';
ค่าของ $api_token สามารถเปลี่ยนได้นะครับ โดยมีเงื่อนไขคือ ต้องเป็นตัวเลขและตัวอักษรภาษาอังกฤษ เท่านั้น
ถ้าหาก Token ที่ถูกส่งมาถูกต้อง ก็จะเป็นการเรียกไปยัง Model ปลายทาง ตาม URL ที่ระบุมา
ไฟล์สุดท้าย /modules/v1/models/user.php หรือ \V1\User\Model เป็นคลาสปลายทางตาม URL ที่ระบุมา หากมี URL รูปแบบอื่นก็สร้าง Class ขึ้นมารับค่าเพิ่มเติมได้เลย เหตุผลที่ผมใช้โฟลเดอร์ v1 เนื่องมาจากว่าหากมีการอัปเกรด API ในภายหลังเป็น v2 v3 .... คนที่ใช้เวอร์ชั่นก่อนหน้าจะยังสามารถใช้งานได้อยู่ (เป็นรูปแบบที่นิยมกัน เช่น API ของ Facebook ก็มีแบบนี้)ส่วนโค้ดในคลาส ก็เป็นการดำเนินการกับฐานข้อมูลปกติ ผมคงไม่อธิบายนะครับ ไปเปิดดูกันเอา
ในส่วนของหน้าเว็บปกติ index.php ผมได้เขียนตัวอย่างการเรียกใช้งาน API ที่เราได้สร้างในบทความนี้ไว้เป็นตัวอย่าง สามารถดูโค้ดตัวอย่างได้ที่ /modules/index/controllers/index.php หรือ \Index\Index\Controller โดยตัวอย่างจะมีการทำงานอยู่ 2 ขั้นตอน
// init cURL
$curl = new Curl();
// set API Token
$curl->setHeaders(array(
'Authorization' => 'Bearer '.self::$cfg->api_token,
));
// ตรวจสอบ username และ password (demo+demo)
$result = $curl->post(WEB_URL.'api.php/v1/user/login', array(
'username' => 'demo',
'password' => 'demo',
));
ในขั้นตอนแรก โค้ดด้านบนจะเรียกไปยัง URL api.php/v1/user/login โดยมีพารามิเตอร์ 3 ตัว คือ Authorization : Bearer api_token ที่ header และ username และ password ที่ต้องการตรวจสอบการ login ผลลัพท์จะได้เป็นข้อความรูปแบบ JSON ที่ $result ซึ่งถ้าสำเร็จจะมีการส่ง refreshToken กลับมา
ตัวแปร refreshToken หรือ Refresh Token ในที่นี้เปรียบเสมือน user_id ของคนที่ login ซึ่งผมใช้เป็นรูปแบบอักขระแบบสุ่มธรรมดาเข้ารหัสร่วมกับ username เพื่อไม่ให้รหัสที่ได้มันซ้ำกัน และ Refresh Token จะเปลี่ยนทุกครั้งที่มีการ login ซ้ำ ซึ่งรูปแบบการจัดการกับ Refresh Token ไม่ได้มีกฏอะไรตายตัวนะครับ ผมใช้วิธีนี้เพราะมันเหมาะสมเพียงพอแล้ว บางคนอาจสงสัยว่าทำไมผมไม่ใช้ JWT ซึ่งจริงๆก็ใช้ได้ไม่ได้มีข้อห้ามแต่ผมเห็นว่า JWT ไม่ได้มีประโยชน์อะไรในกรณีนี้เลยไม่ได้ใช้ (จุดประสงค์ของ JWT เพื่อลดการติดต่อกับ Database ซึ่งในกรณีนี้ยังไงก็ต้องตรวจสอบกับ Database อยู่แล้วเลยไม่รู้จะใช้ไปทำไม)
ขั้นตอนที่สองเป็นการเอา Refresh Token ที่ได้ไปเรียกข้อมูลของตัวเองออกมาที่ URL api.php/v1/user/me โดยมีพารามิเตอร์ 2 ตัว คือ Authorization : Bearer api_token ที่ header (ตามตัวอย่างกำหนดไปแล้วที่คำสั่งแรก) และ refreshToken ซึ่งเป็น Refresh Token ที่ได้จากการ login นั่นเอง
// Refresh token
$refreshToken = $login->result->refreshToken;
// อ่านข้อมูลสมาชิกของ username และ password ที่ตรวจสอบ
$result = $curl->post(WEB_URL.'api.php/v1/user/me', array(
'refreshToken' => $refreshToken,
));
ผลลัพท์ที่ได้ ถ้าสำเร็จก็จะเป็นข้อมูลของ User เจ้าของ Refresh Token รูปแบบ JSON ที่ $result
ประโยชน์ของ API เองไม่ได้มีไว้เพื่อใช้ในไซต์ของตัวเองเท่านั้น ยังต้องสามารถเรียกใช้จากภายนอกได้ด้วย ตัวอย่างด้านล่างจะเป็นการทดสอบการเรียก API โดยใช้ Advance REST client ซึ่งเป็นส่วนขายของ Chrome
ในขั้นตอนแรก การตรวจสอบการ login ด้วย API โดยการเรียกไปยัง API api.php/v1/user/login โดยมีพารามิเตอร์ username : demo และ password : demo ผลลัพท์จาก API จะได้เป็น Refrsh Token ถ้าการ login สำเร็จ ตามรูปเลย ในขั้นตอนที่สอง นำ refreshToken ที่ได้มาส่งต่อ ไปยัง api.php/v1/user/me เพื่อขอข้อมูล user ได้ผลลัพท์ดังรูป
คำเตือน การใช้งาน API ให้ปลอดภัยเป็นสิ่งที่ต้องคำนึงถึงเป็นอันดับแรก ซึ่งมีวิธีการหลากหลายมาก หากต้องการใช้งานให้ปลอดภัยจริงๆ จะต้องดำเนินการอย่างรอบคอบ กฏที่สำคัญของ API คือ อย่าเชื่อถือ Input ใดๆที่ส่งเข้ามา
ก่อนจะจบบทความมีทิปเล็กๆน้อยๆ ที่หลายๆคนอาจจะต้องเจอหากต้องการใช้งาน API จริงจัง ดังนี้
- ไม่สามารถรับค่า Authorization : Bearer api_token ได้ ให้อ่านบทความนี้เลย การพิสูจน์ตัวตนบน PHP (HTTP Authentication with PHP)
- ไม่สามารถใช้ METHOD อื่นๆได้ (เช่น PUT DELETE OPTIONS) นอกจาก GET และ POST อันนี้เป็นกับ Server จำนวนมาก อันเนื่องมาจากการตั้งค่า Server ที่ไม่ได้อนุญาตไว้ ถ้าสามารถทำได้หรือจำเป็นต้องใช้ต้องไปอนุญาตก่อน หรือ ลองใส่โค้ดด้านล่างลงในไฟล์ .htaccess (โค้ดตัวอย่างใส่ไว้ให้แล้ว แต่ในบางไซต์อาจไม่มีผล ต้องไปตั้งค่าเพิ่มเติมที่ Server)
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS" - ไม่สามารถเรียกใช้ API จากเว็บไซต์อื่นได้ เนื่องจากปกติแล้ว API Server จะปฏิเสธคำขอหากไม่ได้เรียกมาจากภายในโดเมนเดียวกัน เราสามารถอนุญาตให้มีการเรียกมาจากโดเมนอื่นได้ โดยการใส่โค้ดด้านล่างลงในไฟล์ .htaccess (สามารถระบุเฉพาะโดเมนที่อนุญาต หรือใส่ * เพื่ออนุญาตทุกโดเมนก็ได้)
# Cross domain access for API
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
เว็บไซต์ตัวอย่าง https://api.kotchasan.com
ดาวน์โหลดโค้ดได้จาก Github
หากต้องการสนับสนุนผู้เขียน สามารถบริจาคช่วยเหลือค่า Server ได้ที่ธนาคาร กสิกรไทย สาขากาญจนบุรี
เลขที่บัญชี 221-2-78341-5
ชื่อบัญชี กรกฎ วิริยะ