Joi JS Joi- 노드의 훌륭한 코드 유효성 검사js 및 Express

주제 또는 개선에 대한 당신의 조언/Chris를 기꺼이 듣고 싶습니다.

Validation of data is an interesting topic, we tend to write code that looks really horrible in the sense that it contains a lot of checks. There are different situations when we need to perform these checks like validating a response from a backend endpoint or maybe verifying what goes into our REST API won’t break our code. We will focus on the latter, how to validate our API.


라이브러리를 검증하지 않을 때 작성해야 할 다음 코드를 고려해 보십시오.
if (!data.parameterX) { 
  throw new Exception('parameterX missing') 
} 
try { 
  let value = parseInt(data.parameterX); 
} catch (err) { 
  throw new Exception('parameterX should be number'); 
} 
if(!/[a-z]/.test(data.parameterY)) { 
  throw new Exception('parameterY should be lower caps text') 
}
나는 네가 위의 코드에서 이 생각을 얻었다고 생각한다.우리는 매개 변수가 정확하거나 허용되는 값을 포함하는지 확인하기 위해 대량의 테스트를 하는 경향이 있다.
개발자로서 우리는 흔히 이런 코드에 대해 매우 좋지 않다고 느낀다. 그래서 우리는 이를 위해 라이브러리를 작성하거나 오랜 친구인 NPM에게 도움을 청하기 시작한다. 다른 개발자들은 이미 이런 고통을 느꼈으면 한다. 그들은 수중에 너무 많은 시간을 가지고 사용할 수 있는 라이브러리를 만들었으면 한다.
그걸 해줄 수 있는 LIB가 많아요.나의 목표는 조이라는 구체적인 예를 묘사하는 것이다.

https://github.com/hapijs/joi


본 문서에서 우리는 다음과 같은 여정을 함께 겪을 것이다.

  • Joi의 기능 보기

  • 요청 파이프
  • 의 백엔드에서 Joi 사용 방법 이해

  • 노드의 Express를 위한 중간부품 구축을 통해 더욱 개선되었습니다.js
  • 조이 소개


    Joi 설치는 간단합니다.우리는 입력만 하면 된다.
    npm install joi
    
    이후에 우리는 그것을 사용할 수 있다.어떻게 사용하는지 빠르게 알아보겠습니다.우리가 해야 할 첫 번째 일은 그것을 가져온 다음에 몇 가지 규칙을 설정하는 것이다. 예를 들어:
    const Joi = require('joi'); 
    const schema = Joi.object().keys({ 
      name: Joi.string().alphanum().min(3).max(30).required(),
      birthyear: Joi.number().integer().min(1970).max(2013), 
    }); 
    const dataToValidate = { 
      name 'chris', 
      birthyear: 1971 
    } 
    const result = Joi.validate(dataToValidate, schema); 
    // result.error == null means valid
    
    우리가 위에서 본 것은 우리가 다음과 같은 일을 하고 있다는 것이다.

  • 조이를 부르는 모드를 만듭니다.객체(),

  • 검증 데이터, 호출Joi.validate()과 모드를 입력 매개 변수로 사용
  • 이제 우리는 기본 운동을 이해했다.우리가 뭘 할 수 있겠어?
    Joi는 모든 깊이에 중첩될 수 있는 다양한 원어와 정규 표현식을 지원합니다.지원되는 여러 구조를 살펴보겠습니다.

  • string, 이것은 반드시string 형식이어야 한다는 것을 나타낸다. 우리도 이렇게 사용한다. dataToValidate

  • 번호, 조이.number (),min () 과max () 같은 조수 작업도 지원합니다. Joi.string()

  • required, 우리는 필요한 방법을 통해 속성이 필요한지 여부를 판단할 수 있다. 예를 들어Joi.number().min(1).max(10)

  • any, 이것은 모든 종류가 가능하다는 것을 의미합니다. 보통 helper allow () 와 함께 사용하는 경향이 있습니다. 후자는 그것이 무엇을 포함할 수 있는지 지정합니다. 예를 들어 Joi.string().required()

  • 선택할 수 있다. 엄밀히 말하면 이것은 유형이 아니지만 재미있는 효과가 있다.예를 들어 prop:Joi.any().allow('a')을 지정하면만약 우리가 도구를 제공하지 않는다면, 모든 사람들이 매우 기뻐할 것이다.그러나 만약 우리가 그것을 제공하고 정수로 설정한다면 검증은 실패할 것이다

  • 그룹, 이 속성이say 문자열의 그룹인지 확인할 수 있습니다. 이렇게 보입니다. Joi.string().optional

  • regex, 이것은 regex의 패턴과 일치하고 유사한 것을 지원합니다. Joi.array().items(Joi.string().valid('a', 'b')
  • Joi의 전체 API는 매우 큽니다.제가 보여드리지 않은 어떤 상황도 해결할 수 있는 조수 함수가 있는지 한번 보도록 하겠습니다.
    Joi API

    https://github.com/hapijs/joi/blob/v14.3.1/API.md


    네스트된 유형


    알겠습니다. 지금까지 우리는 1급 깊이의 모델을 어떻게 설명하는지 보여 드렸을 뿐입니다.이를 위해 우리는 다음과 같이 호소한다.
    Joi.object().keys({ });
    
    이것은 우리의 데이터가 하나의 대상이라는 것을 설명한다.그런 다음 다음과 같이 객체에 속성을 추가했습니다.
    Joi.object().keys({ 
      name: Joi.string().alphanum().min(3).max(30).required(),
      birthyear: Joi.number().integer().min(1970).max(2013) 
    });
    
    지금은 플러그 구조가 사실상 더욱 비슷하다.다음과 같이 새로운 모델, 블로그 게시물 모델을 만들겠습니다.
    const blogPostSchema = Joi.object().keys({ 
      title: Joi.string().alphanum().min(3).max(30).required(),
      description: Joi.string(), 
      comments: Joi.array().items(Joi.object.keys({ 
        description: Joi.string(), 
        author: Joi.string().required(), 
        grade: Joi.number().min(1).max(5) 
      })) 
    });
    
    Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/) 속성은 우리가 처음 보낸 외부 호출과 완전히 같아 보입니다.둥지를 짓는 것은 이렇게 간단하다.

    노드js Express 및 Joi


    이런 라이브러리는 좋지만, 만약 우리가 더 빈틈없는 방식으로 그것들을 사용할 수 있다면, 예를 들어 요청 파이프에서 그것들을 사용하는 것이 좋지 않겠습니까?먼저 노드의 Express 응용 프로그램에서 Joi를 사용하는 방법을 살펴보겠습니다.js:
    const Joi = require('joi'); 
    app.post('/blog', async (req, res, next) => { 
      const { body } = req; const 
      blogSchema = Joi.object().keys({ 
        title: Joi.string().required 
        description: Joi.string().required(), 
        authorId: Joi.number().required() 
      }); 
      const result = Joi.validate(body, blogShema); 
      const { value, error } = result; 
      const valid = error == null; 
      if (!valid) { 
        res.status(422).json({ 
          message: 'Invalid request', 
          data: body 
        }) 
      } else { 
        const createdPost = await api.createPost(data); 
        res.json({ message: 'Resource created', data: createdPost }) 
      } 
    });
    
    상술한 방법은 유효하다.그러나 모든 노선에 대해 우리는 반드시 다음과 같이 해야 한다.
  • 생성 모드
  • 전화 comments
  • 더 좋은 단어가 없기 때문에, 그것은 우아함이 부족하다.우리는 매끄러워 보이는 것을 원한다.

    중간부품 구축


    그것을 중간부품으로 재건할 수 있는지 봅시다.Express의 중간부품은 우리가 요청 파이프를 언제든지 삽입할 수 있는 것이다.우리의 경우, 우리는 우리의 요청을 시도하고 검증하며, 그것을 계속하거나 중단할 가치가 있는지 가능한 한 빨리 확인하기를 희망합니다.
    우리 중간부품을 하나 봅시다.이것은 단지 함수일 뿐이지, 그렇지?
    const handler = (req, res, next) = { // handle our request } 
    const middleware = (req, res, next) => { // to be defined } 
    app.post( '/blog', middleware, handler )
    
    만약 우리가 우리의 중간부품에 모델을 제공할 수 있다면, 우리가 중간부품 기능에서 해야 할 일은 바로 다음과 같다.
    (req, res, next) => { 
      const result = Joi.validate(schema, data) 
    }
    
    우리는 모든 모델을 위해 공장 함수와 모듈을 갖춘 모듈을 만들 수 있다.먼저 공장 기능 모듈을 살펴보겠습니다.
    const Joi = require('joi'); 
    const middleware = (schema, property) => { 
      return (req, res, next) => { 
      const { error } = Joi.validate(req.body, schema); 
      const valid = error == null; 
    
      if (valid) { 
        next(); 
      } else { 
        const { details } = error; 
        const message = details.map(i => i.message).join(',');
    
        console.log("error", message); 
       res.status(422).json({ error: message }) } 
      } 
    } 
    module.exports = middleware;
    
    다음은 모든 모드에 대한 모듈입니다.
    // schemas.js 
    const Joi = require('joi') 
    const schemas = { 
      blogPOST: Joi.object().keys({ 
        title: Joi.string().required 
        description: Joi.string().required() 
      }) 
      // define all the other schemas below 
    }; 
    module.exports = schemas;
    
    그러면 신청 서류로 돌아가겠습니다.
    // app.js 
    const express = require('express') 
    const cors = require('cors'); 
    const app = express() 
    const port = 3000 
    const schemas = require('./schemas'); 
    const middleware = require('./middleware'); 
    var bodyParser = require("body-parser"); 
    
    app.use(cors()); 
    app.use(bodyParser.json()); 
    app.get('/', (req, res) => res.send('Hello World!')) 
    app.post('/blog', middleware(schemas.blogPOST) , (req, res) => { 
      console.log('/update'); 
      res.json(req.body); 
    }); 
     app.listen(port, () => console.log(`Example app listening on port ${port}!`))
    

    테스트


    이 점을 검증할 수 있는 많은 방법이 있다.브라우저 콘솔에서 호출validate()하거나 cURL 등을 사용할 수 있습니다.우리는 fetch()라는 크롬 플러그인을 사용하기로 선택했다.
    POST 요청Advanced REST Client을 보내 보겠습니다.이 노선의 모델에 대해 제목과 묘사가 강제적이라는 것을 기억해라. 그래서 제목을 파괴해 보자. 제목을 무시하고 무슨 일이 일어날지 보자.

    아하, 우리는 /blog 상태 코드를 받았는데, 메시지 제목이 필수적이어서 조이가 해야 할 일을 했다.보안을 위해 제목을 다시 추가합니다.

    좋아, 즐거운 날이 또 작용했어.

    라우터 및 쿼리 매개변수 지원


    좋습니다. POST 요청에서 BODY, 공유기 파라미터와 조회 파라미터를 처리할 수 있습니다. 이것으로 무엇을 검증하고 싶습니까?

  • 페이지, 페이지 크기 같은 파라미터가 존재하는지, 유형이number인지 확인할 필요가 있습니다.상상해 보세요. 저희가 미친 요청을 하고 있는데 데이터베이스에 수백만 개의 제품이 포함되어 있습니다.AOUCH:)

  • 공유기 파라미터, 여기서 만약에 우리가 숫자(예를 들어 guid를 보낼 수 있다)를 얻어야 한다면 먼저 우리가 숫자를 얻었는지 확인하는 것이 의미가 있고 뚜렷한 오류를 보내지 않았는지 검사할 수 있다. 예를 들어 0이나 뭐
  • 질의 매개변수 지원 추가


    알겠습니다. Express의 검색 매개 변수는 422 아래에 있습니다.그래서 우리가 여기서 할 수 있는 가장 간단한 일은 우리request.query가 다른 매개 변수를 사용하도록 확보하는 것이다. 예를 들어 다음과 같다.
    const middleware = (schema, property) => { }
    
    따라서 middleware.js의 전체 코드는 다음과 같습니다.
    const Joi = require('joi'); 
    const middleware = (schema, property) => { 
      return (req, res, next) => { 
        const { error } = Joi.validate(req[property], schema); 
        const valid = error == null; 
        if (valid) { next(); } 
        else { 
          const { details } = error; 
          const message = details.map(i => i.message).join(',')
          console.log("error", message); 
          res.status(422).json({ error: message }) 
        } 
      } 
    } 
    module.exports = middleware;
    
    이것은 우리가 middleware.js를 보고 app.js 함수를 호출하는 방식을 변경해야 한다는 것을 의미한다.우선, 이제 POST 요청이 다음과 같아야 합니다.
    app.post(
      '/blog', 
      middleware(schemas.blogPOST, 'body') , 
      (req, res) => { 
      console.log('/update'); 
      res.json(req.body); 
    });
    
    보시다시피, 우리는 middleware() 호출에 다른 매개 변수체를 추가했습니다.
    이제 관심 있는 요청에 대한 질의 매개 변수를 추가합니다.
    app.get(
      '/products', 
      middleware(schemas.blogLIST, 'query'), 
      (req, res) => { console.log('/products'); 
        const { page, pageSize } = req.query; 
        res.json(req.query); 
    });
    
    보시다시피, 우리가 해야 할 일은 매개 변수 조회를 추가하는 것입니다.마지막으로 우리 middleware():
    // schemas.js 
    const Joi = require('joi'); 
    const schemas = { 
      blogPOST: Joi.object().keys({ 
        title: Joi.string().required(), 
        description: Joi.string().required(), 
        year: Joi.number() }), 
      blogLIST: { 
        page: Joi.number().required(), 
        pageSize: Joi.number().required() 
      } 
    }; 
    module.exports = schemas;
    
    위에서 보듯이 schemas.js 항목을 추가했습니다.
    테스트
    Advanced REST client로 돌아가서 쿼리 매개 변수를 추가하지 않고 blogLIST로 이동하려고 하면 어떻게 되는지 살펴보겠습니다.

    보시다시피 조이가 들어와서 우리에게 /products 없어졌다고 말했다.pagepage가 URL에 추가되었는지 확인하고 다시 시도하겠습니다.

    자, 모두들 또 기뻐했다.

    라우터 매개변수 지원 추가


    쿼리 매개 변수와 마찬가지로, 우리는 매개 변수의 위치를 가리키기만 하면 된다. Express에서 이 매개 변수는 pageSize 아래에 있다.req.params 우리가 이미 한 일 덕분에 우리는 우리의 새로운 노선 항목으로 갱신middleware.js만 하면 된다. 아래와 같다.
    // app.js 
    app.get(
      '/products/:id', 
      middleware(schemas.blogDETAIL, 'params'), 
      (req, res) =>  { 
        console.log("/products/:id"); 
        const { id } = req.params; 
        res.json(req.params); 
      }
    )
    
    이때 우리는 app.js에 들어가서 schemas.js 항목을 추가해야 하기 때문에 blogDetail는 지금 다음과 같아야 한다.
    // schemas.js
    
    const Joi = require('joi');
    
    const schemas = { 
      blogPOST: Joi.object().keys({ 
        title: Joi.string().required(), 
        description: Joi.string().required(), 
        year: Joi.number() }), 
      blogLIST: { 
        page: Joi.number().required(), 
        pageSize: Joi.number().required() 
      }, 
      blogDETAIL: { 
       id: Joi.number().min(1).required() 
      } 
    }; 
    module.exports = schemas;
    
    시험해 보다
    마지막 단계는 네비게이션 테스트schemas.js입니다.이것은 오류를 일으킬 것이다. 우리는 0 이상의 숫자만 받아들일 것이다.

    좋습니다. 이제 URL 선언/products/abc, 우리의 또 다른 요구 사항:

    그리고 예상대로 이것도 실패했다.

    요약


    검증 라이브러리 Joi에 대해 설명하고 몇 가지 기본 특성과 사용 방법에 대해 설명했습니다.마지막으로 Express에 중간부품을 만들고 Joi를 스마트하게 사용하는 방법을 연구했습니다.
    요컨대 나는 이것이 교육적 의의가 있기를 바란다.

    한층 더 읽다

  • Joi, 공식 문서Official docs
  • 조이 검증에 대한 상세한 논문입니다. 더 복잡한 예가 필요하시면 이것을 보십시오Blog post
  • Demo repository
  • 좋은 웹페이지 즐겨찾기