Koa.js-RESTful API

모바일 애플리케이션, 단일 페이지 애플리케이션을 만들고 AJAX 호출을 사용하고 클라이언트에 데이터를 제공하려면 API가 필요합니다. 이러한 API와 엔드 포인트를 구조화하고 이름을 지정하는 방법에 대한 인기있는 아키텍처 스타일을REST(Representational Transfer State). HTTP 1.1은 REST 원칙을 염두에두고 설계되었습니다. REST는Roy Fielding 2000 년 그의 논문 Fielding Dissertations에서.

RESTful URI와 메서드는 요청을 처리하는 데 필요한 거의 모든 정보를 제공합니다. 다음 표에는 다양한 동사를 사용하는 방법과 URI의 이름을 지정하는 방법이 요약되어 있습니다. 마지막으로 영화 API를 만들 예정이므로 어떻게 구성할지 논의 해 보겠습니다.

방법 URI 세부 함수
가져 오기 /영화 산업 안전하고 캐서 블 모든 영화 목록과 세부 정보를 가져옵니다.
가져 오기 / movies / 1234 안전하고 캐서 블 영화 ID 1234의 세부 사항을 가져옵니다.
우편 /영화 산업 N / A 제공된 세부 정보를 사용하여 새 영화를 만듭니다. 응답에는 새로 생성 된 리소스의 URI가 포함됩니다.
놓다 / movies / 1234 멱 등성 영화 ID 1234를 수정합니다 (아직 존재하지 않는 경우 생성). 응답에는 새로 생성 된 리소스의 URI가 포함됩니다.
지우다 / movies / 1234 멱 등성 영화 ID 1234가있는 경우 삭제해야합니다. 응답에는 요청 상태가 포함되어야합니다.
DELETE 또는 PUT /영화 산업 유효하지 않음 유효하지 않아야합니다. DELETE 및 PUT는 작업중인 리소스를 지정해야합니다.

이제 Koa에서이 API를 만들어 보겠습니다. JavaScript에서 작업하기 쉽고 다른 많은 이점이 있으므로 JSON을 전송 데이터 형식으로 사용할 것입니다. index.js 파일을 다음으로 바꿉니다.

INDEX.JS

var koa = require('koa');
var router = require('koa-router');
var bodyParser = require('koa-body');

var app = koa();

//Set up body parsing middleware
app.use(bodyParser({
   formidable:{uploadDir: './uploads'},
   multipart: true,
   urlencoded: true
}));

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

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

app.listen(3000);

이제 애플리케이션을 설정 했으므로 API 생성에 집중하겠습니다. 먼저 movies.js 파일을 설정하십시오. 우리는 영화를 저장하는 데 데이터베이스를 사용하지 않고 메모리에 저장하고 있으므로 서버가 다시 시작할 때마다 추가 한 영화가 사라집니다. 이것은 데이터베이스 또는 파일 (node ​​fs 모듈 사용)을 사용하여 쉽게 모방 할 수 있습니다.

koa-router를 가져오고 라우터를 만들고 module.exports를 사용하여 내 보냅니다.

var Router = require('koa-router');
var router = Router({
  prefix: '/movies'
});  //Prefixed all routes with /movies

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 경로

모든 영화를 얻기위한 GET 경로를 정의하십시오.

router.get('/', sendMovies);
function *sendMovies(next){
   this.body = movies;
   yield next;
}

그게 다야. 이것이 잘 작동하는지 테스트하려면 앱을 실행 한 다음 터미널을 열고 다음을 입력하십시오.

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}]

모든 영화를 볼 수있는 방법이 있습니다. 이제 ID로 특정 영화를 가져 오는 경로를 만들어 보겠습니다.

router.get('/:id([0-9]{3,})', sendMovieWithId);

function *sendMovieWithId(next){
   var ctx = this;
   var currMovie = movies.filter(function(movie){
      if(movie.id == ctx.params.id){
         return true;
      }
   });
   if(currMovie.length == 1){
      this.body = currMovie[0];
   } else {
      this.response.status = 404;//Set status to 404 as movie was not found
      this.body = {message: "Not Found"};
   }
   yield next;
}

이것은 우리가 제공하는 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}

잘못된 경로를 방문하면 GET 할 수 없음 오류가 발생하고, 존재하지 않는 ID로 유효한 경로를 방문하면 404 오류가 발생합니다.

우리는 GET 경로로 끝났습니다. 이제 POST 경로로 이동하겠습니다.

POST 경로

POST 된 데이터를 처리하려면 다음 경로를 사용하십시오.

router.post('/', addNewMovie);

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

그러면 새 영화가 만들어져 movies 변수에 저장됩니다. 이 경로를 테스트하려면 터미널에 다음을 입력하십시오.

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

다음과 같은 응답을 받게됩니다.

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

이것이 movies 객체에 추가되었는지 테스트하려면 / movies / 105에 대한 get 요청을 다시 실행하십시오. 다음과 같은 응답을 받게됩니다.

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

PUT 및 DELETE 경로를 생성 해 보겠습니다.

PUT 경로

PUT 경로는 POST 경로와 거의 동일합니다. 업데이트 / 생성 할 개체의 ID를 지정합니다. 다음과 같은 방법으로 경로를 만듭니다-

router.put('/:id', updateMovieWithId);

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

이 경로는 위 표에서 지정한 기능을 수행합니다. 존재하는 경우 새로운 세부 정보로 개체를 업데이트합니다. 존재하지 않는 경우 새 개체를 만듭니다. 이 경로를 테스트하려면 다음 curl 명령을 사용하십시오. 기존 영화를 업데이트합니다. 새 영화를 만들려면 ID를 존재하지 않는 ID로 변경하면됩니다.

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

응답

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

경로 삭제

다음 코드를 사용하여 삭제 경로를 만듭니다.

router.delete('/:id', deleteMovieWithId);

function *deleteMovieWithId(next){
   var removeIndex = movies.map(function(movie){
      return movie.id;
   }).indexOf(this.params.id); //Gets us the index of movie with given id.
   
   if(removeIndex === -1){
      this.body = {message: "Not found"};
   } else {
      movies.splice(removeIndex, 1);
      this.body = {message: "Movie id " + this.params.id + " removed."};
   }
}

다른 사람들에게했던 것과 같은 방식으로 경로를 테스트합니다. 성공적으로 삭제되면 (예 : ID 105) 다음을 얻을 수 있습니다.

{message: "Movie id 105 removed."}

마지막으로 movies.js 파일은 다음과 같습니다.

var Router = require('koa-router');
var router = Router({
   prefix: '/movies'
});  //Prefixed all routes with /movies
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
router.get('/', sendMovies);
router.get('/:id([0-9]{3,})', sendMovieWithId);
router.post('/', addNewMovie);
router.put('/:id', updateMovieWithId);
router.delete('/:id', deleteMovieWithId);

function *deleteMovieWithId(next){
   var removeIndex = movies.map(function(movie){
      return movie.id;
   }).indexOf(this.params.id); //Gets us the index of movie with given id.
   
   if(removeIndex === -1){
      this.body = {message: "Not found"};
   } else {
      movies.splice(removeIndex, 1);
      this.body = {message: "Movie id " + this.params.id + " removed."};
   }
}

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

function *addNewMovie(next){
   //Check if all fields are provided and are valid:
   if(!this.request.body.name ||
      !this.request.body.year.toString().match(/^[0-9]{4}$/g) ||
      !this.request.body.rating.toString().match(/^[0-9]\.[0-9]$/g)){
      
      this.response.status = 400;
      this.body = {message: "Bad Request"};
   } else {
      var newId = movies[movies.length-1].id+1;
      
      movies.push({
         id: newId,
         name: this.request.body.name,
         year: this.request.body.year,
         rating: this.request.body.rating
      });
      this.body = {message: "New movie created.", location: "/movies/" + newId};
   }
   yield next;
}
function *sendMovies(next){
   this.body = movies;
   yield next;
}
function *sendMovieWithId(next){
   var ctx = this
   
   var currMovie = movies.filter(function(movie){
      if(movie.id == ctx.params.id){
         return true;
      }
   });
   if(currMovie.length == 1){
      this.body = currMovie[0];
   } else {
      this.response.status = 404;//Set status to 404 as movie was not found
      this.body = {message: "Not Found"};
   }
   yield next;
}
module.exports = router;

이것으로 REST API가 완성됩니다. 이제이 단순한 아키텍처 스타일과 Koa를 사용하여 훨씬 더 복잡한 애플리케이션을 만들 수 있습니다.