اعتبار سنجی مدل ها در نود جی اس توسط کتابخانه Joi

اعتبار سنجی مدل ها در نود جی اس توسط کتابخانه Joi

اعتبار سنجی مدل ها در نود جی اس توسط کتابخانه Joi

Validation Models in Nodejs With Joi

توسط : admin
در این آموزش با شما هستیم تا به بررسی یکی از کاربردی ترین کتابخانه های جاوا اسکریپت به نام Joi که کار اعتبار سنجی مدل ها قبل از ورود اطلاعات به دیتابیس ، رو برامون انجام میده ، بپردازیم . چون دیتابیس ما در این آموزش مونگو دیبی هست اعتبار سنجی رو بر رو ی این دیتا بیس متمرکز میکنیم .

 

سلام دوباره به شما دوستان گلم .

در ادامه آموزش ها لازم شد تا به بررسی کتابخانه معروف Joi که وظیفه اعتبار سنجی مدل ها رو به عهده داره بپردازیم . این کتابخونه قبلا در پایگاه پکیج ها با دستور زیر نصب میشد .

npm i joi

 

ولی این ماژول اکنون کهنه شده و دیگه بروز رسانی نمیشه و شما ورژن جدید اون رو میتونید از طریق توسعه دهنده اون یعنی hapi با دستور زیر در پروژه خودتون نصب کنید .

npm i @hapi/joi

 

قصد داریم در این پروژه  اطلاعات یک کاربر رو اعتبار سنجی و ثبت کنیم . سعی میکنم از همه انواع فیلد استفاده کنیم تا با انواع اعتبار سنجی  آشنا بشیم .

فایل اتصال به مونگو دیبی به نام  mongoose.js رو با محتویات زیر  به پروژه اضافه کنید . توضیحات این فایل در آموزش قبلی چهار عمل اصلی در مونگو دی بی موجود هست .

const mongoose = require("mongoose");
const fs = require("fs");


const connectionString = "mongodb://localhost:27017/test-db";

mongoose.Promise = global.Promise;
mongoose
  .connect(connectionString, {
    useNewUrlParser: true,
    useFindAndModify: false,
    reconnectTries: Number.MAX_VALUE,
    reconnectInterval: 1000,
    useCreateIndex: true
  })
  .then(() => {
    fs.writeFile("message.txt", "connected success", err => {
      if (err) throw err;
      console.log("The file has been saved!");
    });
  })
  .catch(ex => {
    fs.writeFile("error.txt", "connected un success", err => {
      if (err) throw err;
      console.log("The file has been saved!");
    });
  });
module.exports = {
  mongoose
};

 

فایلی با نام user.js جهت ایجاد مدل کاربر که قرار هست به صورت مثال  انواع داده هایی رو برای یک کاربر درج کنیم با محتویات زیر اضافه کنید .

const { mongoose } = require("./mongoose");
const Joi = require("@hapi/joi");

let userSchema = new mongoose.Schema({
  email: {
    type: String,
    minlength: 5,
    maxlength: 255,
    required: true,
  },
  userName: {
    type: String,
    minlength: 5,
    maxlength: 150,
    required: true,
  },
  password: {
    type: String,
    minlength: 5,
    maxlength: 20,
    required: true,
  },
  birthDay: {
    type: Date,
    required: false,
  },
  skills: [
    {
      _id: false,
      nameSkill: String,
      rate: Number,
    },
  ],
  tags: [String],
  webSite: {
    type: String,
    required: false,
  },
  mobile: {
    type: String,
  },
  isActive: Boolean,
  userIp: {
    type: String,
    required: true,
  },
  address: {
    province: String,
    city: String,
    postalCode: Number,
    address: {
      type: String,
      maxlength: 500,
    },
  },
});

const validate = (model) => {
  const schema = Joi.object({
    email: Joi.string().email().required().min(5).max(255),
    userName: Joi.string()
      .pattern(new RegExp("[a-z]"))
      .message("نام کاربر باید فقط حروف کوچک باشد")
      .required()
      .min(5)
      .message("حداقل طول نام کاربر 5 کاراکتر است")
      .max(150),
    password: Joi.string().alphanum().required().min(5).max(20),
    birthDay: Joi.date().allow(null),
    skills: Joi.array().items(
      Joi.object().keys({
        nameSkill: Joi.string(),
        rate: Joi.number(),
      })
    ),
    tags: Joi.array().items(Joi.string()),
    webSite: Joi.string().domain().allow(""),
    mobile: Joi.string().pattern(new RegExp("(^09)\\d{9}")),
    isActive: Joi.bool().required(),
    userIp: Joi.string().ip(),
    address: Joi.object().keys({
      province: Joi.string(),
      city: Joi.string(),
      postalCode: Joi.number(),
      address: Joi.string().max(500),
    }),
  });

  return schema.validate(model);
};

let User = mongoose.model("Users", userSchema);

module.exports = {
  User,
  validate,
};

 

خوب دوستان دقت کنید خود دیتا بیس مونگو  دیبی مثل بقیه دیتا بیس ها اعتبار سنجی هایی رو برای ورود و نوع داده ها داره ، ولی خوب شما طبق همیشه نباید بگذارید خطا به دیتا بیس برسه و قبل از اونکه هر اقدامی انجام بشه از قبیل ذخیره ، بروز رسانی و ... ، باید داده های ارسالی کاربر رو اعتبار سنجی کنید . ماژول Joi بسیار منعطف و قدرتمند هست و برای هر کاری ، راهی داره . من سعی کردم در این مثال نوع های پر استفاده رو با این ماژول بررسی کنم که شامل این موارد هستند :

 

در زمان اعتبار سنجی اگر مقداری خارج  از قواعد مشخص شده شناسایی بشه متن خطا توسط Joi برگردونده میشه ولی اگر دوست داشتید به جای متن خطای پیش فرض ارسالی از Joi خودتون به صورت سفارشی متن خطا رو مشخص کنید در ادامه هر پارت از متدهای اعتبار سنجی تابع message رو به همراه متن خطای دلخواه قرار بدید .

به این صورت که در مثال زیر  ما به Joi گفتیم اگر pattern   معتبر نبود خطای دلخواه و اگر min معتبر نبود نیز متن خطای دلخواه خودمون رو به کاربر بر گردون . و شما بعد از هر کدوم از پارت ها میتونید یک message دلخواه درج کنید .

 userName: Joi.string()
      .pattern(new RegExp("[a-z]"))
      .message("نام کاربر باید فقط حروف کوچک باشد")
      .required()
      .min(5)
      .message("حداقل طول نام کاربر 5 کاراکتر است")
      .max(150),

 

اعتبار سنجی داده هایی غیر آرایه ای مشابه نمونه زیر نیاز به توضیح خاصی ندارند و در کد مشخص هستند ولی داده های آرایه ای همونطور که کد میبینید به ترتیب گفته Joi از نوع array که دارای آیتم هایی items است که هر کدام از این آیتم ها از نوع آبجک یا رشته هستند که آرایه از نوع  joi.object  است که این آبجکت دارای کلید / مقدار است keys که حالا در این قسمت نام هر کلید و اعتبار سنجی که قرار هست برای اون کلید انجام بشه رو مشخص میکنیم .

 skills: Joi.array().items(
      Joi.object().keys({
        nameSkill: Joi.string(),
        rate: Joi.number(),
      })
    ),

 

در نهایت فایل اجرایی ما app.js هست که حاوی کدهای زیر میشه 

const express = require("express");
const http = require("http");
const app = express();
const bodyParser = require("body-parser");
app.use(bodyParser.json());
const { User, validate } = require("./user");

app.post("/adduser", async (req, res) => {
  req.body.userIp = req.ip;
 //مقدار آی پی کاربر را خودمان پر میکنیم

  const { error } = validate(req.body);
 // اعتبار سنجی داده های ارسال شده توسط کاربر

  
//اگر خطایی بود اون خطا رو به کاربر نمایش میدیم
  if (error) {
 
    return res.status(400).send(error.details[0].message);
  }

  
//در غیر اینطورت اقدام به ذخیره اطلاعات میکنیم
  const user = new User(req.body);
  user
    .save()
    .then(() => {
      res.status(200).send("کاربر جدید ثبت شد");
    })
    .catch((error) => {
      console.log(error);
      res.status(400).send("عملیات ثبت انجام نشد");
    });
});

app.get("/getusers", async (req, res) => {
  const users = await User.find();
  res.status(200).send(users);
});

const server = http.createServer(app);

server.listen(4000, () => {
  console.log("server running on port 4000");
});

 

در نهایت طبق روال همیشه سرویس رو با استفاده از postman تست میکنیم . من براتون نمونه آبجکت  ارسالی  با فرمت json که باید در قسمت body  قرار بدید  رو این پایین آوردم . شما میتونید با تغییر مقادیر خطای های ارسال شده از سرویس رو در خروجی مشاهده کنید .

{
    "userName": "aaaa",
    "password": "sssss",
    "email": "info@nilootech.com",
    "birthDay": "2012-03-19T07:22Z",
    "skills": [
        {
            "nameSkill": "c#",
            "rate": 22
        }
    ],
    "tags": [
        "user"
    ],
    "isActive": "true",
    "webSite": "mmm.com",
    "mobile": "09166505423",
    "address": {
        "province": "خوزستان",
        "city": "ماهشهر",
        "postalCode": 1234567896,
        "address": "خیبان اول"
    }
}

 

خیلی خوب دوستان فایل کامل پروژه رو میتونید از اینجا دانلود کنید . npm i رو فراموش نکنید . امیدوارم این آموزش به شما کمک کرده باشه . خوشحال میشم با ثبت نام در سایت نیلوتک ، من رو از نظرات خودتون آگاه کنید .

نظرات :

meysams

فروردین ۲۲ ۱۳۹۹

با عرض سلام و تشکر از سایت خوبتون. ممنون میشم راهنمایی بفرمایید طریقه validation مربوط به Password و confirmPassword این کامپوننت چطوریه. من هرکاری کردم مشد !!! از مثال های مختلف تو اینترنت استفاده کردم ولی وقتی تو تکست باکس دوم پسورها رو یکی وارد میکنم باز هم خطای مچ نبودن پسورد میده ! "confirmation" must be [ref:password]

پاسخ مدیر سایت :

سلام دوست عزیزم . 

کتابخانه Joi  فرمت و تایپ انتخاب شده برای فیلد مورد نظر رو جهت ورود اطلاعات به دیتا بیس ، اعتبار سنجی میکنه . هر چند که امکان بررسی شرطی در این ماژول هست ولی عموما مقایسه رمز عبور  به روش زیر در چند مرحله انجام میشه  :

اول در سمت کلاینت توسط ماژول validator و متد :

import validator from "validator";
if (!validator.equals(model.password, model.confirmPassword)) {
   //body
}

معمولا رمز عبور در سمت کلاینت به صورت  hash شده مقایسه نمیشه و فقط صرفا بررسی میشه که کاربر رمز و تکرار رمز رو یکسان وارد کرده باشه .

سپس در سمت سرور :

 پس از ثبت نام اگر رمز عبور رو به صورت hash در دیتا بیس ذخیره کرده باشید برای مقایسه باید رمز وارد شده توسط کاربر رو ابتدا با همون الگو یا الگوریتمی که زمان ثبت نام hash کرده اید ، هش کنید و بعد با رمز موجود در دیتا بیس مقایسه کنید برای مثال اگر bcrypt برای هش استفاده میکنید، به صورت زیر عمل میشه  :

//در مرحله ثبت نام
 const salt = await bcrypt.genSalt(15);
 این رمز در دیتا بیس ذخیره میشود
 const cryptPassword = await bcrypt.hash(req.body.password, salt); 
 
 
 //در زمان لاگین مقایسه بهصورت زیر هست
 const validPassword = await bcrypt.compare(
      req.body.password,
 //رمز عبور وارد شده توسط کاربر
      user.password
 //رمز عبور ذخیره شده
    );

 if (!validPassword)
      return res.status(401).send("نام کاربری یا رمز عبور صحیح نمیباشد");

 

 

پاسخ به @meysams

meysams

مرداد ۰۵ ۱۳۹۹

با تشکر از پاسختون. من اصلا از سمت سرور مشکلی ندارم و دقیقا هدفم از این سوال مربوط به مقایسه مقدار وارد شده کاربر بوده. منتها من چون خواستم به صورت ماژولار از ولیدیشن استفاده کنم مدیریت ولیدیشن مربوط به خطاها رو به صورت داینامیک نوشتم. validate = () => { const { data } = this.state; const validationResult = Joi.object(this.schema).validate(data, { abortEarly: false }); // console.log("validationResult", validationResult); if (!validationResult.error) { return null; } const errors = {}; for (let item of validationResult.error.details) { errors[item.path[0]] = item.message; } console.log(errors); return errors; } validateProperty = ({ name, value }) => { const userInputObject = { [name]: value }; const propertySchema = Joi.object({ [name]: this.schema[name] }); const { error } = propertySchema.validate(userInputObject, { abortEarly: true

پاسخ به @meysams

meysams

مرداد ۰۵ ۱۳۹۹

این هم کد مربوط به ست کردن ولیدیشن فرم ثبت نام schema = { username: Joi.string() .alphanum() .min(5) .max(15) .messages({ "string.empty": "نام کاربری وارد نشده است", "string.min": "حداقل تعداد حروف 5 کاراکتر می باشد", "string.max": "حداکثر تعداد حروف مجاز 15 کاراکتر می باشد", "string.alpha": "از حروف و عدد استفاده نمایید" }), password: Joi.string(), confirmation: Joi.any().valid(Joi.ref('password')) , phonenumber: Joi.string() .min(11) .max(11) .required() .messages({ "string.empty": 'تلفن همراه وارد نشده است', "string.max": 'حداکثر 11 کاراکتر مجاز می باشد', "string.min": 'حداقل 11 کاراکتر مجاز می باشد', }) };

پاسخ مدیر سایت :

 بسیار خوب در این کدها مشکلی نیست و به درستی نوشته شدند و مشکل نباید از این قسمت باشه .

امکانش هست کدهای مربوط به نحوه مقدار دهی  state  بر اساس دو فیلد پسوورد رو ارسال کنید ؟ ظاهرا مقدار صحیح به این ماژول نمیرسه .

 

پاسخ به @meysams

meysams

مرداد ۰۵ ۱۳۹۹

//input.jsx import React from 'react'; const Input = ({ name, label, value, onChange, error, icon, ...rest }) => { if (icon) { icon = <i style={error && { color: 'red' }} className={icon}></i> } console.log("ERR", error); return ( <div className="form-group"> <div className="floating-label-wrap"> <input {...rest} name={name} value={value} onChange={onChange} className={error ? "floating-label-field floating-label-field--s3 form-control is-invalid" : "floating-label-field floating-label-field--s3"} id={name} /> <label htmlFor="{name}" className="floating-label">{icon} {label}</label> {error && <span className="invalid-feedback">{error}</span>} </div> </div> ); } export default Input;

پاسخ مدیر سایت :

بسیار عالی زحمت همه بخش ها رو کشیدید غیر از قسمت اصلی رندر صفحه

پاسخ به @meysams

meysams

مرداد ۰۵ ۱۳۹۹

ببینید مهندس عزیز همه ولیدیشن ها و فرما و ... درست کار میکنه. تنها مشکل موجود confirmPassword هست. قسمت اصلی بررسی validate هم متد validateProperty = ({ name, value }) => { const userInputObject = { [name]: value }; const propertySchema = Joi.object({ [name]: this.schema[name] }); const { error } = propertySchema.validate(userInputObject, { abortEarly: true }); return error ? error.details[0].message : null; } هست

پاسخ مدیر سایت :

اصل متد اعتبار سنجی رو بنده هم د ر کدهای خودم دقیقا مشابه سایت مرجع استفاده کردم و به درستی کار میکنه

برای همین خواستم بخش رندر صفحه رو ببینم
وقتی کامپوننت Input برای بقیه فیلد های درون state درست عمل میکنه 

و همینطور بقیه اعتبار سنجی ها درست کار میکنن همونطور که خدمتت گفتم  پس مشکلی در این قسمت ها نیست

فقط میمونه رندر صفحه که name و id هایی که برای هر input ست کردید 

اون قسمت هم بررسی کردید name و id  ورودی password یا confirmPassword به درستی مقدار داده شده باشند؟ و با فیلدهای state برابر باشند از نظر case senestive ؟ 
 اگر مایل بودید میتونم کانکت بشم و این قسمت صفحه رو بررسی کنم
 

پاسخ به @meysams

meysams

مرداد ۰۵ ۱۳۹۹

ممنون از پاسختون. مشکل رو به صورت کلک رشتی فعلا حل کردم. به این صورت جهت رفع مشکل عدم تطابق گذرواژه‌ها می‌توان متد ValidateProperty رو به صورت زیر تغییر داد. فعلا تنها راه حلی بود که تونستم پیدا کنم. validateProperty = ({ name, value }) => { const userInputObject = { [name]: value }; const propertySchema = Joi.object({ [name]: this.schema[name] }); const { error } = propertySchema.validate(userInputObject, { abortEarly: true }); if (Object.keys(userInputObject)[0] === 'confirmation') { if (value !== this.state.data.password) return "گذرواژه‌ها با هم تطابق ندارند" } return error ? error.details[0].message : null; } فیلد confirmation در schema هم به غیر از ست کردن Joi.ref(password) سایر validation‌ها رو میتونید بزارید. confirmation: Joi.string() .min(5) .max(12)

پاسخ مدیر سایت :

خواهش میکنم .

توی برنامه نویسی معمولا قطعیت هست یا کدی کار میده یا نمیده . ولی بعضی وقتها اتفاقاتی می افته که واقعا غیر از دور زدن ن اون مشکل میشه کاریش کرد . بازهم خدا رو شکر که حل شد .

 

در عرض چند دقیقه برای ایجاد حساب

کاربری خود اقدام کنید


اکنون حساب کاربری خود را ایجاد کنید!


ایجاد حساب کاربری

با ثبت نام در نیلوتک از آخرین بروز رسانی های آموزش ها و مقالات سایت مطلع شوید