۲۹ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

مقدمه Hapi: چارچوب Node.js

Hapi یک فریمورک Node.js است که دارای پشته کد با کیفیت بالا، پیکربندی قدرتمند، و قابلیت توسعه‌پذیری شماره‌گیری شده است—همه بدون وابستگی اضافه. بیایید هاپی را برای چرخش در نظر بگیریم.

Hapi یک فریمورک Node.js است که دارای پشته کد با کیفیت بالا، پیکربندی قدرتمند، و قابلیت توسعه‌پذیری شماره‌گیری شده است—همه بدون وابستگی اضافه. بیایید هاپی را برای چرخش در نظر بگیریم.

Hapi یک فریمورک Node.js در قالب Express است اما با تعدادی ایده متمایز، از جمله پشته کد بدون وابستگی و با کیفیت بالا. گزینه های پیکربندی قدرتمند؛ و توسعه پذیری دقیقی که بسیاری از موارد استفاده را پوشش می دهد. Brendan Eich خالق JavaScript درباره Hapi گفت که “مجموعه مناسبی از APIهای اصلی و پلاگین های قابل توسعه را برای پشتیبانی از الزامات یک سرویس مدرن ارائه می دهد.” API سرور تمیز و توانمند Hapi بهترین توصیه از همه است. بیایید دستمان را روی هاپی بگیریم و دریابیم که چه چیزی روی میز می آورد.

شروع به کار با Hapi

برای شروع، یک دایرکتوری پروژه جدید به نام /hapi ایجاد می‌کنیم و با وارد کردن دستور در فهرست ۱ به آن منتقل می‌کنیم.


npm init -y
npm install @hapi/hapi

npm به طور خودکار یک فایل package.json جدید اضافه می کند و Hapi را به عنوان یک وابستگی نصب می کند. اکنون، می‌توانید تأیید کنید که هاپی در واقع فقط خودش را به عنوان یک وابستگی شامل می‌شود. این کار را با فهرست کردن محتویات /node_modules انجام دهید، که فقط یک فهرست فرعی دارد، /hapi.

یک نقطه پایانی ساده اضافه کنید

بعد، اجازه دهید کدی را برای مدیریت یک نقطه پایانی ساده اضافه کنیم. این نقطه پایانی تعداد انگشت شماری از دستور العمل های ماکارونی را با فرمت RESTful JSON فهرست می کند. با فرض اینکه در فهرست /hapi هستید، یک فایل index.js جدید ایجاد کنید و کد نشان داده شده در فهرست ۲ را اضافه کنید.


const Hapi = require('@hapi/hapi');

const pastaRecipes = [
  { id: 1, name: 'Spaghetti Bolognese' },
  { id: 2, name: 'Fettuccine Alfredo' },
  { id: 3, name: 'Penne Arrabiata' }
];

const init = async () => {
  const server = Hapi.server({
    port:5173,
    host: '0.0.0.0' // Remove to only bind to localhost
  });

 server.route({
    method: 'GET',
    path: '/recipes',
    handler: (request, h) => {
      return pastaRecipes;
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};

init();

 می‌توانید نقطه پایانی را با دستور cURL آزمایش کنید: curl http://localhost:5173/recipes، یا با باز کردن نقطه پایانی در مرورگر. (اگر می خواهید از راه دور متصل شوید، باید میزبان: ۰.۰.۰.۰ را به پیکربندی سرور اضافه کنید تا به تمام رابط های شبکه متصل شود.)

کد موجود در فهرست ۲ خود توضیحی است. ما با وارد کردن وابستگی Hapi شروع می کنیم و سپس از آن برای پیکربندی خود سرور با Hapi.server({}) استفاده می کنیم، که یک شی JSON را می پذیرد و فقط به پورت نیاز دارد تا به آن متصل شود.

مایکروسافت دات نت 7 کانتینرها و ابر را به صفر می رساند

بعد، یک آرایه در حافظه ایجاد می کنیم تا دستور العمل ها را نگه دارد، سپس یک مسیر با server.route() اضافه می کنیم. مجدداً، این با یک شی JSON پیکربندی شده است که متد get() HTTP، مسیر URL (/recipes) و کنترل‌کننده تابعی را نگه می‌دارد، که به سادگی دستور غذا را برمی‌گرداند. آرایه.

یک تماس ناهمزمان با server.start() سرور را شروع می کند.

افزودن یک پست تأیید شده

حالا، یک نقطه پایانی POST تأیید شده اضافه می کنیم که می توانیم از آن برای افزودن یک دستور غذا استفاده کنیم. مدیریت پست ساده است، همانطور که در لیست ۳ نشان داده شده است. ما می توانیم این نقطه پایانی را به مسیر GET بعدی اضافه کنیم.


server.route({
  method: 'POST',
  path: '/recipes',
  handler: (request, h) => {
    const recipe = request.payload;
    recipe.id=pastaRecipes.length+1;
    pastaRecipes.push(recipe);
    return recipe;
  }
});

اکنون می‌توانیم درخواستی را به آن POST کنیم، همانطور که در فهرست ۴ نشان داده شده است.


curl -X POST -d '{"name":"Penne alla Vodka"}' http://localhost:5173/recipes -H 'Content-Type: application/json' 

ارسال یک درخواست GET به نقطه پایانی /recipes اکنون دستور العمل ها را با افزودنی جدید برمی گرداند.

احراز هویت اولیه با Hapi

فرض کنید باید فقط به مدیران اجازه دهیم به این نقطه پایانی POST دسترسی داشته باشند. روش‌های زیادی برای احراز هویت وجود دارد، اما برای این کار، اجازه دهید از Hapi برای اجرای یک مکانیسم اصلی احراز هویت HTTP استفاده کنیم. سپس می‌توانیم آن را با یک پست cURL آزمایش کنیم، هم با و هم بدون مجموعه اعتبار، همانطور که در فهرست ۵ نشان داده شده است.


const Hapi = require('@hapi/hapi');
const Bcrypt = require('bcrypt');

const users = {
  john: {
    username: 'john',
    password: '$2a$10$iqJSHD.BGr0E2IxQwYgJmeP3NvhPrXAeLSaGCj6IR/XU5QtjVu5Tm',   // 'secret'
    name: 'John Doe',
    id: '2133d32a'
  }
};

const validate = async (request, username, password) => {
  const user = users[username];
  if (!user) {
    return { credentials: null, isValid: false };
  }

  const isValid = await Bcrypt.compare(password, user.password);
  const credentials = { id: user.id, name: user.name };

  return { isValid, credentials };
};

const init = async () => {
  const server = Hapi.server({
    port:5173,
    host: '0.0.0.0'
  });

  await server.register(require('@hapi/basic'));
  server.auth.strategy('simple', 'basic', { validate });

  server.route({
    method: 'POST',
    path: '/recipes',
    options: {
      auth: 'simple'
    },
    handler: (request, h) => {
      const recipe = request.payload;
      recipe.id=pastaRecipes.length+1;
      pastaRecipes.push(recipe);
      return recipe;
    },
  });
  await server.start();
  console.log('Server running at:', server.info.uri);
};

init();

ما احراز هویت را با مراحل زیر اجرا کردیم:

  • کتابخانه bcrypt را (برای رمزگذاری) وارد کنید.
  • یک شی JSON به نام users تعریف کنید تا به عنوان “پایگاه داده” اعتبار ما عمل کند.
  • افزونه احراز هویت داخلی را از Hapi ثبت کنید تا احراز هویت اولیه را انجام دهید: await server.register(require('@hapi/basic'));.
  • یک تابع اعتبارسنجی برای بررسی اعتبار کاربر در برابر پایگاه داده تعریف کنید.
  • یک مکانیسم احراز هویت اولیه به نام 'simple' را به سرور اضافه کنید (auth.strategy). این را به تابع validate از مرحله قبل واگذار کنید.
  • استراتژی auth را در مسیر /recipes با تعریف options.auth='simple' اعمال کنید.
بنابراین شما می خواهید یک توسعه دهنده وب 3 باشید

توجه داشته باشید که Hapi میان‌افزار عمومی مانند Express ندارد. در عوض، از افزونه‌های هدفمندتری مانند افزونه auth که در اینجا استفاده می‌کنیم، استفاده می‌کند.

قبل از اجرای این کد، سرور را متوقف کنید و وابستگی bcrypt را اضافه کنید: $ npm i bcrypt. کد را با: $ node index.js اجرا کنید.

نقطه پایانی POST را آزمایش کنید

اکنون، همانطور که در فهرست ۶ نشان داده شده است، می توانیم نقطه پایانی ایمن را آزمایش کنیم.


$ curl -X POST -H "Content-Type: application/json" -d '{"name": "Penne alla Vodka"}' http://localhost:5173/recipes
{"statusCode":401,"error":"Unauthorized","message":"Missing authentication"}

$ curl -X POST -u john:badsecret -H "Content-Type: application/json" -d '{"name": "Penne alla Vodka"}' http://localhost:5173/recipes
{"statusCode":401,"error":"Unauthorized","message":"Bad username or password","attributes":{"error":"Bad username or password"}}

$ curl -X POST -u john:secret -H "Content-Type: application/json" -d '{"name": "Penne alla Vodka"}' http://localhost:5173/recipes
{"name":"Penne alla Vodka","id":5}

مسیریابی پیشرفته با Hapi

بیایید به یک مثال پیچیده تر از مسیریابی نگاه کنیم. در این مورد، ما برنامه را برای بازیابی یک ظرف پاستا با استفاده از شناسه آن تنظیم می‌کنیم.


server.route({
    method: 'GET',
    path: '/recipes/{id}',
    handler: (request, h) => {
      const recipeId = parseInt(request.params.id);
      const recipe = pastaRecipes.find(recipe => recipe.id === recipeId);
      if (recipe) {
        return recipe;
      } else {
        return h.response({ error: 'Recipe not found' }).code(404);
      }
    }
  });

$ curl http://localhost:5173/recipes/1
{"id":1,"name":"Fettuccine Alfredo"}

فهرست ۷ کد بازیابی یک دستور غذا را با شناسه نشان می دهد، سپس دستور cURL برای آزمایش آن استفاده می شود. ویژگی اصلی که باید به آن توجه داشت این است که چگونه یک پارامتر URL را با پرانتزهای فرفری شناسایی کنیم و سپس آن مقدار را در روش handler بازیابی کنیم. در مورد ما، از مقدار {id} برای برداشتن یک دستور از آرایه و برگرداندن آن استفاده می‌کنیم.

کش حافظه داخلی هاپی

Hapi شامل پشتیبانی از حافظه پنهان خارج از جعبه است. بیایید نگاهی به استفاده از آن با کش داخلی داخلی، به نام CatBoxMemory بیاندازیم. در یک برنامه واقعی، شما می توانید چیزی مانند Redis یا Memcached را تعویض کنید.

استفاده از افزونه کش شامل ایجاد کش با روش server.cache() و سپس اعمال آن در مسیرها است. (توجه داشته باشید که هنگام تعریف خود سرور، به جای فراخوانی ثبت نام، می توانید افزونه ها را به صورت درون خطی تعریف کنید.)

فهرست ۸ مثالی از استفاده از پشتیبانی کش دارد. بقیه کد ثابت می ماند.


const CatboxMemory = require('@hapi/catbox-memory');

const cache = server.cache({
  segment: 'recipes',
  expiresIn: 60 * 60 * 1000, // Cache for 1 hour
  generateFunc: async (recipeId) => {
    const recipe = pastaRecipes.find((recipe) => recipe.id === recipeId);
    return recipe ? { ...recipe, cacheHit: true } : null;
  },
  generateTimeout: 2000
});

server.route({
  method: 'GET',
  path: '/recipes',
  handler: async (request, h) => {
    const cachedRecipes = await cache.get('all');
    if (cachedRecipes) {
      return cachedRecipes;
    }
    await cache.set('all', { ...pastaRecipes, cacheHit: true });
    return pastaRecipes;
  }
})

در این مثال، نقطه پایانی GET از cache.get با کلید 'all' برای کشیدن دستور العمل ها استفاده می کند. اگر آنها پیدا نشدند، اشیاء را از پایگاه داده برمی گرداند. در غیر این صورت، از حافظه پنهان خارج می شود. (ما یک فیلد cacheHit اضافه می‌کنیم تا تأیید کنیم که از حافظه پنهان می‌آید.) در فهرست ۹، می‌توانید خروجی آزمایش حافظه پنهان را ببینید.


$ curl localhost:5173/recipes
[{"id":0,"name":"Spaghetti Bolognese"},{"id":1,"name":"Fettuccine Alfredo"},{"id":2,"name":"Penne Arrabiata"}]
$ curl localhost:5173/recipes
{"۰":{"id":0,"name":"Spaghetti Bolognese"},"1":{"id":1,"name":"Fettuccine Alfredo"},"2":{"id":2,"name":"Penne Arrabiata"},"cacheHit":true}

روش سرور Hapi—یک حافظه پنهان ساده شده

Hapi همچنین شامل چیزی است که روش سرور نامیده می‌شود، روشی پاک‌تر برای دستیابی به کش برای موقعیت‌های ساده. فرض کنید می‌خواهیم حافظه پنهان را به نقطه پایانی که برای دریافت دستور العمل‌ها با شناسه استفاده می‌کنیم اضافه کنیم. ما می توانیم این کار را همانطور که در لیست ۱۰ نشان داده شده است انجام دهیم.



const init = async () => {
  const server = Hapi.server({
    port:5173,
    host: '0.0.0.0'
  });

  server.method('getRecipe', (id)=>{
    const recipe = pastaRecipes.find(recipe => recipe.id === id);
    return recipe ? recipe : { error: 'Recipe not found' };
  }, {
    cache: {
      expiresIn: 10 * 1000,
      generateTimeout: 2000
    }
  });

  
  server.route({
    method: 'GET',
    path: '/recipes/{id}',
    handler: async (request, h) => {
      return await server.methods.getRecipe(parseInt(request.params.id));
    }
  });

  await server.start();
  console.log('Server running at:', server.info.uri);
};

init();

در فهرست ۱۰، نحوه ایجاد یک روش قابل استفاده مجدد با server.method را می بینیم. با این رویکرد، از ویژگی cache برای ذخیره خودکار نتایج روش آرگومان استفاده می کنیم. ویژگی cache همان فیلدهای شی را به عنوان شی cache می پذیرد. ما از ویژگی cache به جای یک تابع معمولی در روش کنترل کننده مسیر /recipes/{id} GET استفاده می کنیم. این یک روش بسیار ساده است و صفحه دیگ مورد نیاز برای ایجاد یک تابع ذخیره شده را حذف می کند.

نحوه استفاده از typesafe enums در جاوا

نتیجه گیری

Hapi وعده یک API سمت سرور راحت و قابل توسعه را ارائه می دهد. این به اندازه کافی ساده و معقول است که بتوان آن را خیلی سریع از سرور دیگری دریافت کرد، و رویکرد افزودن افزونه ها با یک سیستم هدفمند منطقی است و از برخی کشمکش ها با میان افزارهایی که می توانند با سرورهای دیگر ارائه شوند، جلوگیری می کند. همچنین خوب است بدانید که Hapi به هیچ وابستگی خارج از جعبه نیاز ندارد، به این معنی که پایگاه کد آن کاملاً بررسی و ایمن است.