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

این یک پست متقاطع از رسانه است.
در Rust Web Dev ، Utoipa محبوب ترین جعبه برای تولید سند OpenAPI از کد سرور است. در حالی که این یک ابزار عالی است ، به دلیل استفاده بیش از حد ماکرو می تواند ناامید کننده باشد.
یک چارچوب وب جدید OHKAMI ارائه می دهد کلان کمتر ، بسیار یکپارچه راهی برای تولید سند OpenAPI با آن openapi
ویژگی.
اوکامی جدید [狼] گرگ به زبان ژاپنی – چارچوب وب بصری و اعلامی است
- از نظر کلان کمتر و از نوع ایمن API برای کد بصری و اعلامی
-
زمان های مختلف پشتیبانی می شوند
tokio
باasync-std
باsmol
باnio
باglommio
وتworker
(کارگران CloudFlare) ،lambda
(AWS Lambda) - تست بسیار سریع ، بدون شبکه ، میانه های خوب ساختار یافته ، رویدادهای سرور-سرور ، WebSocket ، تولید اسناد OpenAPI بسیار یکپارچه ، …
شروع سریع
- اضافه کردن
dependencies
:
[dependencies]
ohkami = { version = "0.23", features = ["rt_tokio"] }
tokio = { version = "1", features = ["full"] }
- اولین کد خود را با 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 را بررسی کنید و برنامه نویسی را شروع کنید!