برنامه نویسی

تولید مستند OpenAPI کلان ، کاملاً یکپارچه در زنگ زدگی با Ohkami

این یک پست متقاطع از رسانه است.

در Rust Web Dev ، Utoipa محبوب ترین جعبه برای تولید سند OpenAPI از کد سرور است. در حالی که این یک ابزار عالی است ، به دلیل استفاده بیش از حد ماکرو می تواند ناامید کننده باشد.

یک چارچوب وب جدید OHKAMI ارائه می دهد کلان کمتر ، بسیار یکپارچه راهی برای تولید سند OpenAPI با آن openapi ویژگی.

اوکامی جدید [狼] گرگ به زبان ژاپنی – چارچوب وب بصری و اعلامی است

  • از نظر کلان کمتر و از نوع ایمن API برای کد بصری و اعلامی
  • زمان های مختلف پشتیبانی می شوندtokioبا async-stdبا smolبا nioبا glommio وت worker (کارگران CloudFlare) ، lambda (AWS Lambda)
  • تست بسیار سریع ، بدون شبکه ، میانه های خوب ساختار یافته ، رویدادهای سرور-سرور ، WebSocket ، تولید اسناد OpenAPI بسیار یکپارچه ، …

شروع سریع

  1. اضافه کردن dependencies :
[dependencies]
ohkami = { version = "0.23", features = ["rt_tokio"] }
tokio  = { version = "1",    features = ["full"] }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

  1. اولین کد خود را با OHKAMI بنویسید: مثال/Quick_start
use ohkami::prelude::*;
use ohkami::typed::status;
async fn health_check() -> status::NoContent {
    status::NoContent
}

async fn hello(name: &str) -> String {
    format

حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نمونه

بیایید کد زیر را به عنوان نمونه بگیریم. این همان نمونه از بخش “OpenAPI” README است ، اما با قطعات مرتبط با OpenAPI حذف شده است:

use ohkami::prelude::*;
use ohkami::typed::status;

#[derive(Deserialize)]
struct CreateUser<'req> {
    name: &'req str,
}

#[derive(Serialize)]
struct User {
    id: usize,
    name: String,
}

async fn create_user(
    JSON(CreateUser { name }): JSON<CreateUser<'_>>
) -> status::Created<JSON<User>> {
    status::Created(JSON(User {
        id: 42,
        name: name.to_string()
    }))
}

async fn list_users() -> JSON<Vec<User>> {
    JSON(vec![])
}

#[tokio::main]
async fn main() {
    let o = Ohkami::new((
        "/users"
            .GET(list_users)
            .POST(create_user),
    ));

    o.howl("localhost:5000").await;
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در حالی که این کار به عنوان یک سرور مدیریت شبه کاربر کار می کند و کار می کند ، فعال می شود openapi ویژگی باعث خطای کامپایل می شود و این را بیان می کند User وت CreateUser پیاده سازی نکنید ohkami::openapi::Schemaبشر

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

use ohkami::openapi;

...

let o = Ohkami::new((
    "/users"
        .GET(list_users)
        .POST(create_user),
));

o.generate(openapi::OpenAPI {
    title: "Users Server",
    version: "0.1.0",
    servers: &[openapi::Server::at("localhost:5000")],
});
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

برای جمع آوری ابرداده در یک سند OpenAPI و خروجی آن به یک پرونده بدون ماکروهای ماتبشر

سپس ، چگونه ما پیاده سازی می کنیم Schema؟ در واقع ما به راحتی می توانیم impl Schema با دست ، یا فقط #[derive(Schema)] موجود است! در این حالت ، مشتق کافی است:

#[derive(Deserialize, openapi::Schema)] // <--
struct CreateUser<'req> {
   name: &'req str,
}

#[derive(Serialize, openapi::Schema)] // <--
struct User {
   id: usize,
   name: String,
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

همین است! فقط اضافه کردن این مشتقات اجازه می دهد Ohkami::generate برای خروجی پرونده زیر:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Users Server",
    "version": "0.1.0"
  },
  "servers": [
    {
      "url": "localhost:5000"
    }
  ],
  "paths": {
    "/users": {
      "get": {
        "operationId": "list_users",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "integer"
                      },
                      "name": {
                        "type": "string"
                      }
                    },
                    "required": [
                      "id",
                      "name"
                    ]
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "create_user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "integer"
                  },
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "id",
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer"
                    },
                    "name": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "name"
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

علاوه بر این ، تعریف آن آسان است User طرحواره به عنوان یک مؤلفه به جای کپی کردن طرحواره های درون خطی.
در مشتق ، فقط اضافه کنید #[openapi(component)] ویژگی یاور:

#[derive(Serialize, openapi::Schema)]
#[openapi(component)] // <--
struct User {
   id: usize,
   name: String,
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اکنون خروجی:

{
  "openapi": "3.1.0",
  "info": {
    "title": "Users Server",
    "version": "0.1.0"
  },
  "servers": [
    {
      "url": "localhost:5000"
    }
  ],
  "paths": {
    "/users": {
      "get": {
        "operationId": "list_users",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/User"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "create_user",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "integer"
                  },
                  "name": {
                    "type": "string"
                  }
                },
                "required": [
                  "id",
                  "name"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/User"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "User": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name"
        ]
      }
    }
  }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

وت در صورت اختیاری #[operation] ویژگی برای تنظیم در دسترس است summaryبا description، و نادیده گرفتن operationId و هر پاسخ description:

#[openapi::operation({
    summary: "...",
    200: "List of all users",
})]
/// This doc comment is used for the
/// `description` field of OpenAPI document
async fn list_users() -> JSON<Vec<User>> {
    JSON(vec![])
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

{
  ...

  "paths": {
    "/users": {
      "get": {
        "operationId": "list_users",
        "summary": "...",
        "description": "This doc comment is used for the\n`description` field of OpenAPI document",
        "responses": {
          "200": {
            "description": "List of all users",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/User"

  ...
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

چگونه کار می کند؟

بیایید نگاهی بیندازیم که چگونه این تولید سند کار می کند!

1 Schema

اول ، #[derive(Schema)]S به شرح زیر گسترش می یابد:

impl<'req> ::ohkami::openapi::Schema for CreateUser<'req> {
    fn schema() -> impl 
Into<::ohkami::openapi::schema::SchemaRef> {
        {
            let mut schema = ::ohkami::openapi::object();
            schema = schema
                .property(
                    "name",
                    ::ohkami::openapi::schema::Schema::<
                        ::ohkami::openapi::schema::Type::any,
                    >::from(
                        <&'req str as ::ohkami::openapi::Schema>::schema()
                            .into()
                            .into_inline()
                            .unwrap(),
                    ),
                );
            schema
        }
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

برابر با

impl openapi::Schema for CreateUser<'_> {
    fn schema() -> impl Into<openapi::schema::SchemaRef> {
        openapi::object()
            .property("name", openapi::string())
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

impl ::ohkami::openapi::Schema for User {
    fn schema() -> impl Into<::ohkami::openapi::schema::SchemaRef> {
        ::ohkami::openapi::component(
            "User",
            {
                let mut schema = ::ohkami::openapi::object();
                schema = schema
                    .property(
                        "id",
                        ::ohkami::openapi::schema::Schema::<
                            ::ohkami::openapi::schema::Type::any,
                        >::from(
                            <usize as ::ohkami::openapi::Schema>::schema()
                                .into()
                                .into_inline()
                                .unwrap(),
                        ),
                    );
                schema = schema
                    .property(
                        "name",
                        ::ohkami::openapi::schema::Schema::<
                            ::ohkami::openapi::schema::Type::any,
                        >::from(
                            <String as ::ohkami::openapi::Schema>::schema()
                                .into()
                                .into_inline()
                                .unwrap(),
                        ),
                    );
                schema
            },
        )
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

برابر با

impl openapi::Schema for User {
    fn schema() -> impl Into<openapi::schema::SchemaRef> {
        openapi::component(
            "User",
            openapi::object()
                .property("id", openapi::integer())
                .property("name", openapi::string())
        )
    }
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

DSL سازمان یافته امکان پذیر است که به راحتی به صورت دستی مشخص شود.

Schema صفت ساختار را به یک مورد از نوع به نام پیوند می دهد SchemaRefبشر

2 openapi_* قلاب FromParamبا FromRequestبا IntoResponse

آنها ویژگی های اصلی Ohkami در Handler Bound ظاهر شدند:

async fn({FromParam tuple}?, {FromRequest item}*) -> {IntoResponse item}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

کی openapi ویژگی فعال می شود ، آنها علاوه بر این روشهای زیر دارند:

fn openapi_param() -> openapi::Parameter

fn openapi_inbound() -> openapi::Inbound

fn openapi_responses() -> openapi::Responses
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

Ohkami این روش ها را در IntoHandler برای تولید سازگار openapi::Operation، منعکس کننده امضای کنترل کننده واقعی مانند این.

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

3 routes ابرداده روتر

در اوکامی ، آنچه نامیده می شود router::base::Router داشتن routes املاک که تمام مسیرهای متعلق به نمونه Ohkami را ذخیره می کند. این در کنار بازگردانده شده است router::final::Router از finalize مرحله ، و برای جمع آوری ابرداده از تمام نقاط پایانی استفاده می شود.

4 generate

چه Ohkami::generate خودش فقط سریال سازی یک مورد از نوع است openapi::document::Document و آن را برای یک پرونده بنویسید.

در openapi::document::Document مورد توسط ایجاد شده است gen_openapi_doc از router::final::Router، خلاصه شده به شرح زیر:

let mut doc = Document::new(/* ... */);

for route in routes {
    let (openapi_path, openapi_path_param_names) = {
        // "/api/users/:id"
        // ↓
        // ("/api/users/{id}", ["id"])
    };

    let mut operations = Operations::new();
    for (openapi_method, router) in [
        ("get",    &self.GET),
        ("put",    &self.PUT),
        ("post",   &self.POST),
        ("patch",  &self.PATCH),
        ("delete", &self.DELETE),
    ] {
        // if an operation is registerred in a Node
        // at `route` of `router`,
        // perform a preprocess for it and
        // append it to `operations`
    }

    doc = doc.path(openapi_path, operations);
}

doc
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

اینگونه است که Ohkami سند OpenAPI را تولید می کند!

پیوست: کارگران CloudFlare

با این حال ، یک مشکل در rt_worker، کارگران CloudFlare: جایی که Ohkami به عنوان WASM به کارگران Miniflare یا CloudFlare بارگیری می شود ، بنابراین فقط می تواند سند OpenAPI را به عنوان داده تولید کند و نمی تواند آن را برای سیستم فایل محلی کاربر بنویسد.

برای کار در این زمینه ، Ohkami اسکریپت های ابزار CLI/Workers_openapi.js را ارائه می دهد. به عنوان مثال ، این در استفاده می شود package.json از کارگران CloudFlare + الگوی OpenAPI:

{
    ...
    "scripts": {
     "deploy": "export OHKAMI_WORKER_DEV='' && wrangler deploy",
     "dev": "export OHKAMI_WORKER_DEV=1 && wrangler dev",
     "openapi": "node -e \"$(curl -s https://raw.githubusercontent.com/ohkami-rs/ohkami/refs/heads/main/scripts/workers_openapi.js)\" -- --features openapi"
    },
    ...
}
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

در این حالت ، فقط

npm run openapi
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

سند OpenAPI تولید می کند!


از خواندن شما متشکرم اگر به Ohkami علاقه دارید ، Repo GitHub را بررسی کنید و برنامه نویسی را شروع کنید!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا