ExpressJS - RESTFul API

จำเป็นต้องใช้ API เสมอในการสร้างแอปพลิเคชันมือถือแอปพลิเคชันหน้าเดียวใช้การโทร AJAX และให้ข้อมูลแก่ลูกค้า รูปแบบสถาปัตยกรรมที่เป็นที่นิยมในการจัดโครงสร้างและตั้งชื่อ API เหล่านี้และจุดสิ้นสุดเรียกว่าREST(Representational Transfer State). HTTP 1.1ได้รับการออกแบบโดยคำนึงถึงหลักการ REST REST ได้รับการแนะนำโดยRoy Fielding ในปี 2000 ใน Paper Fielding Dissertations

URI และวิธีการ RESTful ให้ข้อมูลเกือบทั้งหมดที่เราต้องการในการดำเนินการตามคำขอ ตารางด้านล่างสรุปว่าควรใช้กริยาต่างๆอย่างไรและควรตั้งชื่อ URI อย่างไร เราจะสร้างภาพยนตร์ API ในตอนท้าย ตอนนี้ให้เราคุยกันว่าจะมีโครงสร้างอย่างไร

วิธี URI รายละเอียด ฟังก์ชัน
รับ /ภาพยนตร์ ปลอดภัยสามารถเข้าถึงได้ รับรายชื่อภาพยนตร์ทั้งหมดและรายละเอียด
รับ / movies / 1234 ปลอดภัยสามารถเข้าถึงได้ รับรายละเอียดของ Movie id 1234
โพสต์ /ภาพยนตร์ ไม่มี สร้างภาพยนตร์ใหม่พร้อมรายละเอียดที่ให้ไว้ การตอบกลับมี URI สำหรับทรัพยากรที่สร้างขึ้นใหม่นี้
วาง / movies / 1234 Idempotent แก้ไขรหัสภาพยนตร์ 1234 (สร้างใหม่หากยังไม่มี) การตอบกลับมี URI สำหรับทรัพยากรที่สร้างขึ้นใหม่นี้
ลบ / movies / 1234 Idempotent ควรลบ Movie id 1234 หากมีอยู่ การตอบกลับควรมีสถานะของคำขอ
ลบหรือวาง /ภาพยนตร์ ไม่ถูกต้อง ควรจะไม่ถูกต้อง DELETE และ PUT ควรระบุว่าทรัพยากรใดกำลังทำงานอยู่

ให้เราสร้าง API นี้ใน Express เราจะใช้ JSON เป็นรูปแบบข้อมูลการขนส่งของเราเนื่องจากใช้งานง่ายใน JavaScript และมีประโยชน์อื่น ๆ แทนที่ไฟล์index.js ไฟล์ด้วยไฟล์ movies.js ไฟล์ในโปรแกรมต่อไปนี้

index.js

var express = require('express');
var bodyParser = require('body-parser');
var multer = require('multer');
var upload = multer();

var app = express();

app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(upload.array());

//Require the Router we defined in movies.js
var movies = require('./movies.js');

//Use the Router on the sub route /movies
app.use('/movies', movies);

app.listen(3000);

ตอนนี้เราได้ตั้งค่าแอปพลิเคชันแล้วให้เรามีสมาธิในการสร้าง API

เริ่มต้นด้วยการตั้งค่าไฟล์ movies.js เราไม่ได้ใช้ฐานข้อมูลในการจัดเก็บภาพยนตร์ แต่กำลังจัดเก็บไว้ในหน่วยความจำ ดังนั้นทุกครั้งที่เซิร์ฟเวอร์รีสตาร์ทภาพยนตร์ที่เราเพิ่มเข้ามาจะหายไป สิ่งนี้สามารถเลียนแบบได้อย่างง่ายดายโดยใช้ฐานข้อมูลหรือไฟล์ (โดยใช้โมดูลโหนด fs)

เมื่อคุณนำเข้า Express แล้วให้สร้างเราเตอร์และส่งออกโดยใช้module.exports -

var express = require('express');
var router = express.Router();
var movies = [
   {id: 101, name: "Fight Club", year: 1999, rating: 8.1},
   {id: 102, name: "Inception", year: 2010, rating: 8.7},
   {id: 103, name: "The Dark Knight", year: 2008, rating: 9},
   {id: 104, name: "12 Angry Men", year: 1957, rating: 8.9}
];

//Routes will go here
module.exports = router;

รับเส้นทาง

ให้เรากำหนดเส้นทาง GET เพื่อรับภาพยนตร์ทั้งหมด -

router.get('/', function(req, res){
   res.json(movies);
});

หากต้องการทดสอบว่าใช้งานได้ดีหรือไม่ให้เรียกใช้แอปของคุณจากนั้นเปิดเทอร์มินัลแล้วป้อน -

curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET 
localhost:3000/movies

คำตอบต่อไปนี้จะปรากฏขึ้น -

[{"id":101,"name":"Fight Club","year":1999,"rating":8.1},
{"id":102,"name":"Inception","year":2010,"rating":8.7},
{"id":103,"name":"The Dark Knight","year":2008,"rating":9},
{"id":104,"name":"12 Angry Men","year":1957,"rating":8.9}]

เรามีเส้นทางในการรับชมภาพยนตร์ทั้งหมด ตอนนี้ให้เราสร้างเส้นทางเพื่อรับภาพยนตร์เฉพาะโดยใช้รหัส

router.get('/:id([0-9]{3,})', function(req, res){
   var currMovie = movies.filter(function(movie){
      if(movie.id == req.params.id){
         return true;
      }
   });
   if(currMovie.length == 1){
      res.json(currMovie[0])
   } else {
      res.status(404);//Set status to 404 as movie was not found
      res.json({message: "Not Found"});
   }
});

สิ่งนี้จะทำให้เราได้รับภาพยนตร์ตาม id ที่เราให้ไว้ ในการตรวจสอบผลลัพธ์ให้ใช้คำสั่งต่อไปนี้ในเทอร์มินัลของคุณ -

curl -i -H "Accept: application/json" -H "Content-Type: application/json" -X GET 
localhost:3000/movies/101

คุณจะได้รับคำตอบดังต่อไปนี้ -

{"id":101,"name":"Fight Club","year":1999,"rating":8.1}

หากคุณไปที่เส้นทางที่ไม่ถูกต้องมันจะสร้างไฟล์ cannot GET error ในขณะที่หากคุณไปยังเส้นทางที่ถูกต้องโดยมีรหัสที่ไม่มีอยู่จะทำให้เกิดข้อผิดพลาด 404

เราทำเส้นทาง GET เสร็จแล้วให้เราไปที่ POST เส้นทาง.

เส้นทาง POST

ใช้เส้นทางต่อไปนี้เพื่อจัดการไฟล์ POSTed ข้อมูล -

router.post('/', function(req, res){
   //Check if all fields are provided and are valid:
   if(!req.body.name ||
      !req.body.year.toString().match(/^[0-9]{4}$/g) ||
      !req.body.rating.toString().match(/^[0-9]\.[0-9]$/g)){
      
      res.status(400);
      res.json({message: "Bad Request"});
   } else {
      var newId = movies[movies.length-1].id+1;
      movies.push({
         id: newId,
         name: req.body.name,
         year: req.body.year,
         rating: req.body.rating
      });
      res.json({message: "New movie created.", location: "/movies/" + newId});
   }
});

การดำเนินการนี้จะสร้างภาพยนตร์ใหม่และจัดเก็บไว้ในตัวแปรภาพยนตร์ ในการตรวจสอบเส้นทางนี้ให้ป้อนรหัสต่อไปนี้ในอาคารผู้โดยสารของคุณ -

curl -X POST --data "name = Toy%20story&year = 1995&rating = 8.5" http://localhost:3000/movies

คำตอบต่อไปนี้จะปรากฏขึ้น -

{"message":"New movie created.","location":"/movies/105"}

หากต้องการทดสอบว่าสิ่งนี้ถูกเพิ่มลงในวัตถุภาพยนตร์หรือไม่ให้เรียกใช้คำขอรับ /movies/105อีกครั้ง. คำตอบต่อไปนี้จะปรากฏขึ้น -

{"id":105,"name":"Toy story","year":"1995","rating":"8.5"}

ให้เราไปสร้างเส้นทาง PUT และ DELETE

PUT เส้นทาง

เส้นทาง PUT เกือบจะเหมือนกับเส้นทาง POST เราจะระบุรหัสสำหรับวัตถุที่จะอัปเดต / สร้างขึ้น สร้างเส้นทางด้วยวิธีต่อไปนี้

router.put('/:id', function(req, res){
   //Check if all fields are provided and are valid:
   if(!req.body.name ||
      !req.body.year.toString().match(/^[0-9]{4}$/g) ||
      !req.body.rating.toString().match(/^[0-9]\.[0-9]$/g) ||
      !req.params.id.toString().match(/^[0-9]{3,}$/g)){
      
      res.status(400);
      res.json({message: "Bad Request"});
   } else {
      //Gets us the index of movie with given id.
      var updateIndex = movies.map(function(movie){
         return movie.id;
      }).indexOf(parseInt(req.params.id));
      
      if(updateIndex === -1){
         //Movie not found, create new
         movies.push({
            id: req.params.id,
            name: req.body.name,
            year: req.body.year,
            rating: req.body.rating
         });
         res.json({message: "New movie created.", location: "/movies/" + req.params.id});
      } else {
         //Update existing movie
         movies[updateIndex] = {
            id: req.params.id,
            name: req.body.name,
            year: req.body.year,
            rating: req.body.rating
         };
         res.json({message: "Movie id " + req.params.id + " updated.", 
            location: "/movies/" + req.params.id});
      }
   }
});

เส้นทางนี้จะทำหน้าที่ตามที่ระบุไว้ในตารางด้านบน มันจะอัปเดตออบเจ็กต์ด้วยรายละเอียดใหม่หากมีอยู่ ถ้าไม่มีก็จะสร้างวัตถุใหม่ ในการตรวจสอบเส้นทางให้ใช้คำสั่ง curl ต่อไปนี้ การดำเนินการนี้จะอัปเดตภาพยนตร์ที่มีอยู่ หากต้องการสร้างภาพยนตร์ใหม่เพียงแค่เปลี่ยนรหัสเป็นรหัสที่ไม่มีอยู่

curl -X PUT --data "name = Toy%20story&year = 1995&rating = 8.5" 
http://localhost:3000/movies/101

Response

{"message":"Movie id 101 updated.","location":"/movies/101"}

ลบเส้นทาง

ใช้รหัสต่อไปนี้เพื่อสร้างเส้นทางการลบ -

router.delete('/:id', function(req, res){
   var removeIndex = movies.map(function(movie){
      return movie.id;
   }).indexOf(req.params.id); //Gets us the index of movie with given id.
   
   if(removeIndex === -1){
      res.json({message: "Not found"});
   } else {
      movies.splice(removeIndex, 1);
      res.send({message: "Movie id " + req.params.id + " removed."});
   }
});

ตรวจสอบเส้นทางในลักษณะเดียวกับที่เราตรวจสอบเส้นทางอื่น ๆ เมื่อการลบสำเร็จ (เช่น id 105) คุณจะได้ผลลัพธ์ต่อไปนี้ -

{message: "Movie id 105 removed."}

ในที่สุด movies.js ไฟล์จะมีลักษณะดังต่อไปนี้

var express = require('express');
var router = express.Router();
var movies = [
   {id: 101, name: "Fight Club", year: 1999, rating: 8.1},
   {id: 102, name: "Inception", year: 2010, rating: 8.7},
   {id: 103, name: "The Dark Knight", year: 2008, rating: 9},
   {id: 104, name: "12 Angry Men", year: 1957, rating: 8.9}
];
router.get('/:id([0-9]{3,})', function(req, res){
   var currMovie = movies.filter(function(movie){
      if(movie.id == req.params.id){
         return true;
      }
   });
   
   if(currMovie.length == 1){
      res.json(currMovie[0])
   } else {
      res.status(404);  //Set status to 404 as movie was not found
      res.json({message: "Not Found"});
   }
});
router.post('/', function(req, res){
   //Check if all fields are provided and are valid:
   if(!req.body.name ||
      !req.body.year.toString().match(/^[0-9]{4}$/g) ||
      !req.body.rating.toString().match(/^[0-9]\.[0-9]$/g)){
      res.status(400);
      res.json({message: "Bad Request"});
   } else {
      var newId = movies[movies.length-1].id+1;
      movies.push({
         id: newId,
         name: req.body.name,
         year: req.body.year,
         rating: req.body.rating
      });
      res.json({message: "New movie created.", location: "/movies/" + newId});
   }
});

router.put('/:id', function(req, res) {
   //Check if all fields are provided and are valid:
   if(!req.body.name ||
      !req.body.year.toString().match(/^[0-9]{4}$/g) ||
      !req.body.rating.toString().match(/^[0-9]\.[0-9]$/g) ||
      !req.params.id.toString().match(/^[0-9]{3,}$/g)){
      res.status(400);
      res.json({message: "Bad Request"});
   } else {
      //Gets us the index of movie with given id.
      var updateIndex = movies.map(function(movie){
         return movie.id;
      }).indexOf(parseInt(req.params.id));
      
      if(updateIndex === -1){
         //Movie not found, create new
         movies.push({
            id: req.params.id,
            name: req.body.name,
            year: req.body.year,
            rating: req.body.rating
         });
         res.json({
            message: "New movie created.", location: "/movies/" + req.params.id});
      } else {
         //Update existing movie
         movies[updateIndex] = {
            id: req.params.id,
            name: req.body.name,
            year: req.body.year,
            rating: req.body.rating
         };
         res.json({message: "Movie id " + req.params.id + " updated.",
            location: "/movies/" + req.params.id});
      }
   }
});

router.delete('/:id', function(req, res){
   var removeIndex = movies.map(function(movie){
      return movie.id;
   }).indexOf(req.params.id); //Gets us the index of movie with given id.
   
   if(removeIndex === -1){
      res.json({message: "Not found"});
   } else {
      movies.splice(removeIndex, 1);
      res.send({message: "Movie id " + req.params.id + " removed."});
   }
});
module.exports = router;

เสร็จสมบูรณ์ REST API ของเรา ตอนนี้คุณสามารถสร้างแอปพลิเคชันที่ซับซ้อนมากขึ้นโดยใช้รูปแบบสถาปัตยกรรมที่เรียบง่ายและ Express