[{"data":1,"prerenderedAt":3639},["ShallowReactive",2],{"navigation":3,"\u002Fblog\u002Futoipa-integration-with-axum":46,"\u002Fblog\u002Futoipa-integration-with-axum-surround":3634},[4],{"title":5,"path":6,"stem":7,"children":8,"page":45},"Blog","\u002Fblog","blog",[9,13,17,21,25,29,33,37,41],{"title":10,"path":11,"stem":12},"吉布斯自由能、化学势与扩散势","\u002Fblog\u002Fchemical-potential-and-diffusion-potential","blog\u002Fchemical-potential-and-diffusion-potential",{"title":14,"path":15,"stem":16},"Deploying Nuxt Static Sites with Nginx","\u002Fblog\u002Fdeploying-nuxt-static-site-with-nginx","blog\u002Fdeploying-nuxt-static-site-with-nginx",{"title":18,"path":19,"stem":20},"相变驱动力计算","\u002Fblog\u002Fphase-transformation-driving-force","blog\u002Fphase-transformation-driving-force",{"title":22,"path":23,"stem":24},"Python 环境配置完全指南：Conda 与国内镜像源","\u002Fblog\u002Fpython-environment-setup-guide","blog\u002Fpython-environment-setup-guide",{"title":26,"path":27,"stem":28},"Rust 编译与配置指南","\u002Fblog\u002Frust-compilation-and-configuration","blog\u002Frust-compilation-and-configuration",{"title":30,"path":31,"stem":32},"SSH Key Authentication Setup Guide","\u002Fblog\u002Fssh-key-authentication-setup","blog\u002Fssh-key-authentication-setup",{"title":34,"path":35,"stem":36},"Tauri + Rust 技术栈与编译说明","\u002Fblog\u002Ftauri-rust-tech-stack-and-build","blog\u002Ftauri-rust-tech-stack-and-build",{"title":38,"path":39,"stem":40},"Complete Guide to Utoipa Integration with Axum","\u002Fblog\u002Futoipa-integration-with-axum","blog\u002Futoipa-integration-with-axum",{"title":42,"path":43,"stem":44},"Rust: Code Once, Build Anywhere","\u002Fblog\u002Fwhy-rust-priority","blog\u002Fwhy-rust-priority",false,{"id":47,"title":38,"author":48,"body":52,"date":3627,"description":3628,"extension":3629,"image":3630,"meta":3631,"minRead":198,"navigation":165,"path":39,"seo":3632,"stem":40,"__hash__":3633},"blog\u002Fblog\u002Futoipa-integration-with-axum.md",{"name":49,"avatar":50},"Jing Zhong",{"src":51,"alt":49},"https:\u002F\u002Fppm-sources.oss-cn-hongkong.aliyuncs.com\u002Fzj\u002Fme.jpg",{"type":53,"value":54,"toc":3568},"minimark",[55,59,64,67,102,106,111,119,231,236,262,266,269,277,281,284,290,294,298,305,352,356,363,556,562,602,606,611,625,630,644,649,666,670,674,821,825,913,917,1013,1017,1113,1117,1196,1200,1204,1275,1279,1367,1371,1430,1434,1497,1500,1560,1564,1568,1973,1977,1981,2158,2162,2226,2230,2233,2256,2260,2264,2369,2373,2444,2448,2539,2543,2742,2746,2770,2774,2778,2783,2809,2814,2871,2875,2879,2899,2903,2938,2942,2947,2971,2975,3001,3008,3013,3019,3028,3036,3094,3098,3155,3158,3162,3166,3196,3200,3234,3238,3277,3281,3321,3325,3402,3406,3460,3464,3467,3528,3531,3535,3564],[56,57,58],"p",{},"Building RESTful APIs in Rust with Axum is fantastic, but comprehensive API documentation is equally important. That's where utoipa comes in - a powerful OpenAPI documentation generator that integrates seamlessly with Axum and Swagger UI. Let's dive into how to set up a complete documentation system with multiple authentication methods.",[60,61,63],"h2",{"id":62},"why-utoipa","Why Utoipa?",[56,65,66],{},"utoipa provides compile-time OpenAPI documentation generation using Rust macros:",[68,69,70,78,84,90,96],"ul",{},[71,72,73,77],"li",{},[74,75,76],"strong",{},"Type Safety",": Leverages Rust's type system for accurate API docs",[71,79,80,83],{},[74,81,82],{},"No Code Duplication",": Documentation lives alongside your code",[71,85,86,89],{},[74,87,88],{},"Multiple Auth Methods",": Support for JWT, API Keys, Cookies",[71,91,92,95],{},[74,93,94],{},"Swagger UI Integration",": Beautiful, interactive API documentation",[71,97,98,101],{},[74,99,100],{},"OpenAPI 3.0 Compatible",": Standard-compliant specification",[60,103,105],{"id":104},"project-setup","Project Setup",[107,108,110],"h3",{"id":109},"dependency-configuration","Dependency Configuration",[56,112,113,114,118],{},"Start by adding the necessary dependencies to your ",[115,116,117],"code",{},"Cargo.toml",":",[120,121,126],"pre",{"className":122,"code":123,"language":124,"meta":125,"style":125},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[dependencies]\n# Core dependencies\naxum = \"0.7\"\ntokio = { version = \"1\", features = [\"full\"] }\nsea-orm = \"0.12\"\n\n# OpenAPI documentation\nutoipa = { version = \"4\", features = [\"uuid\", \"chrono\"] }\nutoipa-swagger-ui = { version = \"6\", features = [\"axum\"] }\n\n# Serialization\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0\"\n\n# Time and UUID\nchrono = { version = \"0.4\", features = [\"serde\"] }\nuuid = { version = \"1\", features = [\"v4\", \"serde\"] }\n","toml","",[115,127,128,136,142,148,154,160,167,173,179,185,190,196,202,208,213,219,225],{"__ignoreMap":125},[129,130,133],"span",{"class":131,"line":132},"line",1,[129,134,135],{},"[dependencies]\n",[129,137,139],{"class":131,"line":138},2,[129,140,141],{},"# Core dependencies\n",[129,143,145],{"class":131,"line":144},3,[129,146,147],{},"axum = \"0.7\"\n",[129,149,151],{"class":131,"line":150},4,[129,152,153],{},"tokio = { version = \"1\", features = [\"full\"] }\n",[129,155,157],{"class":131,"line":156},5,[129,158,159],{},"sea-orm = \"0.12\"\n",[129,161,163],{"class":131,"line":162},6,[129,164,166],{"emptyLinePlaceholder":165},true,"\n",[129,168,170],{"class":131,"line":169},7,[129,171,172],{},"# OpenAPI documentation\n",[129,174,176],{"class":131,"line":175},8,[129,177,178],{},"utoipa = { version = \"4\", features = [\"uuid\", \"chrono\"] }\n",[129,180,182],{"class":131,"line":181},9,[129,183,184],{},"utoipa-swagger-ui = { version = \"6\", features = [\"axum\"] }\n",[129,186,188],{"class":131,"line":187},10,[129,189,166],{"emptyLinePlaceholder":165},[129,191,193],{"class":131,"line":192},11,[129,194,195],{},"# Serialization\n",[129,197,199],{"class":131,"line":198},12,[129,200,201],{},"serde = { version = \"1.0\", features = [\"derive\"] }\n",[129,203,205],{"class":131,"line":204},13,[129,206,207],{},"serde_json = \"1.0\"\n",[129,209,211],{"class":131,"line":210},14,[129,212,166],{"emptyLinePlaceholder":165},[129,214,216],{"class":131,"line":215},15,[129,217,218],{},"# Time and UUID\n",[129,220,222],{"class":131,"line":221},16,[129,223,224],{},"chrono = { version = \"0.4\", features = [\"serde\"] }\n",[129,226,228],{"class":131,"line":227},17,[129,229,230],{},"uuid = { version = \"1\", features = [\"v4\", \"serde\"] }\n",[56,232,233,118],{},[74,234,235],{},"Important Notes",[68,237,238,241,252],{},[71,239,240],{},"Use utoipa version 4.x (avoid 5.x for better stability)",[71,242,243,244,247,248,251],{},"Enable ",[115,245,246],{},"uuid"," and ",[115,249,250],{},"chrono"," features for proper type support",[71,253,254,255,258,259],{},"Ensure ",[115,256,257],{},"utoipa-swagger-ui"," version is compatible with ",[115,260,261],{},"utoipa",[107,263,265],{"id":264},"project-structure","Project Structure",[56,267,268],{},"Organize your documentation code for clarity:",[120,270,275],{"className":271,"code":273,"language":274},[272],"language-text","src\u002F\n├── docs\u002F\n│   ├── mod.rs              # Documentation module entry\n│   ├── security.rs         # Security schemes (Modify trait)\n│   ├── openapi.rs          # OpenAPI helpers\n│   └── schemas.rs          # Data model definitions\n├── handler.rs              # API handlers\n├── main.rs                 # App entry + ApiDoc definition\n└── route.rs                # Route definitions\n","text",[115,276,273],{"__ignoreMap":125},[60,278,280],{"id":279},"understanding-utoipas-architecture","Understanding Utoipa's Architecture",[56,282,283],{},"utoipa generates OpenAPI documentation through three key components:",[120,285,288],{"className":286,"code":287,"language":274},[272],"┌─────────────────────────────────────────────────┐\n│  utoipa::path (marks handler functions)         │\n│  ↓                                              │\n│  Collects API info (path, method, params, resp) │\n│                                                 │\n├─────────────────────────────────────────────────┤\n│  ToSchema (marks data structures)               │\n│  ↓                                              │\n│  Generates Schema definitions (request\u002Fresponse)│\n│                                                 │\n├─────────────────────────────────────────────────┤\n│  OpenApi (aggregates configuration)             │\n│  ↓                                              │\n│  Generates complete OpenAPI JSON document       │\n│                                                 │\n├─────────────────────────────────────────────────┤\n│  SwaggerUi (serves documentation interface)     │\n│  ↓                                              │\n│  Provides visual API docs and testing UI        │\n└─────────────────────────────────────────────────┘\n",[115,289,287],{"__ignoreMap":125},[60,291,293],{"id":292},"security-configuration-the-modify-trait","Security Configuration: The Modify Trait",[107,295,297],{"id":296},"the-challenge","The Challenge",[56,299,300,301,304],{},"utoipa 4.x doesn't support defining security schemes directly in the ",[115,302,303],{},"#[openapi]"," macro:",[120,306,310],{"className":307,"code":308,"language":309,"meta":125,"style":125},"language-rust shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F ❌ This doesn't work in utoipa 4.x!\n#[derive(OpenApi)]\n#[openapi(\n    security_schemes(\n        (\"bearerAuth\" = ...)\n    )\n)]\nstruct ApiDoc;\n","rust",[115,311,312,317,322,327,332,337,342,347],{"__ignoreMap":125},[129,313,314],{"class":131,"line":132},[129,315,316],{},"\u002F\u002F ❌ This doesn't work in utoipa 4.x!\n",[129,318,319],{"class":131,"line":138},[129,320,321],{},"#[derive(OpenApi)]\n",[129,323,324],{"class":131,"line":144},[129,325,326],{},"#[openapi(\n",[129,328,329],{"class":131,"line":150},[129,330,331],{},"    security_schemes(\n",[129,333,334],{"class":131,"line":156},[129,335,336],{},"        (\"bearerAuth\" = ...)\n",[129,338,339],{"class":131,"line":162},[129,340,341],{},"    )\n",[129,343,344],{"class":131,"line":169},[129,345,346],{},")]\n",[129,348,349],{"class":131,"line":175},[129,350,351],{},"struct ApiDoc;\n",[107,353,355],{"id":354},"the-solution-modify-trait","The Solution: Modify Trait",[56,357,358,359,362],{},"Create a custom type that implements the ",[115,360,361],{},"Modify"," trait:",[120,364,366],{"className":307,"code":365,"language":309,"meta":125,"style":125},"\u002F\u002F src\u002Fdocs\u002Fsecurity.rs\nuse utoipa::{Modify, OpenApi};\nuse utoipa::openapi::security::{SecurityScheme, HttpAuthScheme, Http, ApiKey, ApiKeyValue};\n\n\u002F\u002F\u002F JWT and API Key security configuration\npub struct JwtSecurityAddon;\n\nimpl Modify for JwtSecurityAddon {\n    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {\n        if let Some(components) = openapi.components.as_mut() {\n            \u002F\u002F 1. JWT Bearer Token authentication\n            components.add_security_scheme(\n                \"bearerAuth\",\n                SecurityScheme::Http(\n                    Http::new(HttpAuthScheme::Bearer)\n                ),\n            );\n\n            \u002F\u002F 2. Cookie authentication\n            components.add_security_scheme(\n                \"cookieAuth\",\n                SecurityScheme::ApiKey(\n                    ApiKey::Cookie(ApiKeyValue::new(\"token\"))\n                ),\n            );\n\n            \u002F\u002F 3. API Key authentication (Header-based)\n            components.add_security_scheme(\n                \"apiKeyAuth\",\n                SecurityScheme::ApiKey(\n                    ApiKey::Header(ApiKeyValue::new(\"X-API-Key\"))\n                ),\n            );\n        }\n    }\n}\n",[115,367,368,373,378,383,387,392,397,401,406,411,416,421,426,431,436,441,446,451,456,462,467,473,479,485,490,495,500,506,511,517,522,528,533,538,544,550],{"__ignoreMap":125},[129,369,370],{"class":131,"line":132},[129,371,372],{},"\u002F\u002F src\u002Fdocs\u002Fsecurity.rs\n",[129,374,375],{"class":131,"line":138},[129,376,377],{},"use utoipa::{Modify, OpenApi};\n",[129,379,380],{"class":131,"line":144},[129,381,382],{},"use utoipa::openapi::security::{SecurityScheme, HttpAuthScheme, Http, ApiKey, ApiKeyValue};\n",[129,384,385],{"class":131,"line":150},[129,386,166],{"emptyLinePlaceholder":165},[129,388,389],{"class":131,"line":156},[129,390,391],{},"\u002F\u002F\u002F JWT and API Key security configuration\n",[129,393,394],{"class":131,"line":162},[129,395,396],{},"pub struct JwtSecurityAddon;\n",[129,398,399],{"class":131,"line":169},[129,400,166],{"emptyLinePlaceholder":165},[129,402,403],{"class":131,"line":175},[129,404,405],{},"impl Modify for JwtSecurityAddon {\n",[129,407,408],{"class":131,"line":181},[129,409,410],{},"    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {\n",[129,412,413],{"class":131,"line":187},[129,414,415],{},"        if let Some(components) = openapi.components.as_mut() {\n",[129,417,418],{"class":131,"line":192},[129,419,420],{},"            \u002F\u002F 1. JWT Bearer Token authentication\n",[129,422,423],{"class":131,"line":198},[129,424,425],{},"            components.add_security_scheme(\n",[129,427,428],{"class":131,"line":204},[129,429,430],{},"                \"bearerAuth\",\n",[129,432,433],{"class":131,"line":210},[129,434,435],{},"                SecurityScheme::Http(\n",[129,437,438],{"class":131,"line":215},[129,439,440],{},"                    Http::new(HttpAuthScheme::Bearer)\n",[129,442,443],{"class":131,"line":221},[129,444,445],{},"                ),\n",[129,447,448],{"class":131,"line":227},[129,449,450],{},"            );\n",[129,452,454],{"class":131,"line":453},18,[129,455,166],{"emptyLinePlaceholder":165},[129,457,459],{"class":131,"line":458},19,[129,460,461],{},"            \u002F\u002F 2. Cookie authentication\n",[129,463,465],{"class":131,"line":464},20,[129,466,425],{},[129,468,470],{"class":131,"line":469},21,[129,471,472],{},"                \"cookieAuth\",\n",[129,474,476],{"class":131,"line":475},22,[129,477,478],{},"                SecurityScheme::ApiKey(\n",[129,480,482],{"class":131,"line":481},23,[129,483,484],{},"                    ApiKey::Cookie(ApiKeyValue::new(\"token\"))\n",[129,486,488],{"class":131,"line":487},24,[129,489,445],{},[129,491,493],{"class":131,"line":492},25,[129,494,450],{},[129,496,498],{"class":131,"line":497},26,[129,499,166],{"emptyLinePlaceholder":165},[129,501,503],{"class":131,"line":502},27,[129,504,505],{},"            \u002F\u002F 3. API Key authentication (Header-based)\n",[129,507,509],{"class":131,"line":508},28,[129,510,425],{},[129,512,514],{"class":131,"line":513},29,[129,515,516],{},"                \"apiKeyAuth\",\n",[129,518,520],{"class":131,"line":519},30,[129,521,478],{},[129,523,525],{"class":131,"line":524},31,[129,526,527],{},"                    ApiKey::Header(ApiKeyValue::new(\"X-API-Key\"))\n",[129,529,531],{"class":131,"line":530},32,[129,532,445],{},[129,534,536],{"class":131,"line":535},33,[129,537,450],{},[129,539,541],{"class":131,"line":540},34,[129,542,543],{},"        }\n",[129,545,547],{"class":131,"line":546},35,[129,548,549],{},"    }\n",[129,551,553],{"class":131,"line":552},36,[129,554,555],{},"}\n",[56,557,558,559,118],{},"Then use it in your ",[115,560,561],{},"ApiDoc",[120,563,565],{"className":307,"code":564,"language":309,"meta":125,"style":125},"\u002F\u002F src\u002Fmain.rs\nuse acrospire_auth::docs::JwtSecurityAddon;\n\n#[derive(OpenApi)]\n#[openapi(\n    modifiers(&JwtSecurityAddon)  \u002F\u002F ← Key! Use modifiers to add security schemes\n)]\nstruct ApiDoc;\n",[115,566,567,572,577,581,585,589,594,598],{"__ignoreMap":125},[129,568,569],{"class":131,"line":132},[129,570,571],{},"\u002F\u002F src\u002Fmain.rs\n",[129,573,574],{"class":131,"line":138},[129,575,576],{},"use acrospire_auth::docs::JwtSecurityAddon;\n",[129,578,579],{"class":131,"line":144},[129,580,166],{"emptyLinePlaceholder":165},[129,582,583],{"class":131,"line":150},[129,584,321],{},[129,586,587],{"class":131,"line":156},[129,588,326],{},[129,590,591],{"class":131,"line":162},[129,592,593],{},"    modifiers(&JwtSecurityAddon)  \u002F\u002F ← Key! Use modifiers to add security schemes\n",[129,595,596],{"class":131,"line":169},[129,597,346],{},[129,599,600],{"class":131,"line":175},[129,601,351],{},[107,603,605],{"id":604},"authentication-methods-explained","Authentication Methods Explained",[56,607,608],{},[74,609,610],{},"1. Bearer Token (JWT)",[68,612,613,616,619,622],{},[71,614,615],{},"Type: HTTP Authentication",[71,617,618],{},"Scheme: Bearer",[71,620,621],{},"Usage: JWT Token authentication",[71,623,624],{},"Client: Enter JWT Token directly in Swagger UI (no \"Bearer \" prefix needed)",[56,626,627],{},[74,628,629],{},"2. Cookie Authentication",[68,631,632,635,638,641],{},[71,633,634],{},"Type: API Key",[71,636,637],{},"Location: Cookie",[71,639,640],{},"Name: token",[71,642,643],{},"Usage: Cookie-based authentication, browser auto-carries cookie",[56,645,646],{},[74,647,648],{},"3. API Key (Header)",[68,650,651,653,656,659],{},[71,652,634],{},[71,654,655],{},"Location: Header",[71,657,658],{},"Name: X-API-Key",[71,660,661,662,665],{},"Usage: API Key authentication (e.g., ",[115,663,664],{},"ak_live_xxxxxx",")",[60,667,669],{"id":668},"defining-api-paths","Defining API Paths",[107,671,673],{"id":672},"basic-syntax","Basic Syntax",[120,675,677],{"className":307,"code":676,"language":309,"meta":125,"style":125},"use utoipa::path;\n\n#[utoipa::path(\n    \u002F\u002F HTTP method and path\n    post,\n    path = \"\u002Fapi\u002Fauth\u002Flogin\",\n\n    \u002F\u002F Grouping tag\n    tag = \"Authentication\",\n\n    \u002F\u002F Request body\n    request_body = LoginRequest,\n\n    \u002F\u002F Authentication requirements\n    security(\n        (\"bearerAuth\" = []),\n        (\"cookieAuth\" = [])\n    ),\n\n    \u002F\u002F Response definitions\n    responses(\n        (status = 200, description = \"Login successful\", body = LoginResponse),\n        (status = 401, description = \"Authentication failed\", body = ErrorResponse)\n    )\n)]\npub async fn login_handler(\n    Json(payload): Json\u003CLoginRequest>,\n) -> Result\u003CJson\u003CLoginResponse>, StatusCode> {\n    \u002F\u002F Handler implementation\n}\n",[115,678,679,684,688,693,698,703,708,712,717,722,726,731,736,740,745,750,755,760,765,769,774,779,784,789,793,797,802,807,812,817],{"__ignoreMap":125},[129,680,681],{"class":131,"line":132},[129,682,683],{},"use utoipa::path;\n",[129,685,686],{"class":131,"line":138},[129,687,166],{"emptyLinePlaceholder":165},[129,689,690],{"class":131,"line":144},[129,691,692],{},"#[utoipa::path(\n",[129,694,695],{"class":131,"line":150},[129,696,697],{},"    \u002F\u002F HTTP method and path\n",[129,699,700],{"class":131,"line":156},[129,701,702],{},"    post,\n",[129,704,705],{"class":131,"line":162},[129,706,707],{},"    path = \"\u002Fapi\u002Fauth\u002Flogin\",\n",[129,709,710],{"class":131,"line":169},[129,711,166],{"emptyLinePlaceholder":165},[129,713,714],{"class":131,"line":175},[129,715,716],{},"    \u002F\u002F Grouping tag\n",[129,718,719],{"class":131,"line":181},[129,720,721],{},"    tag = \"Authentication\",\n",[129,723,724],{"class":131,"line":187},[129,725,166],{"emptyLinePlaceholder":165},[129,727,728],{"class":131,"line":192},[129,729,730],{},"    \u002F\u002F Request body\n",[129,732,733],{"class":131,"line":198},[129,734,735],{},"    request_body = LoginRequest,\n",[129,737,738],{"class":131,"line":204},[129,739,166],{"emptyLinePlaceholder":165},[129,741,742],{"class":131,"line":210},[129,743,744],{},"    \u002F\u002F Authentication requirements\n",[129,746,747],{"class":131,"line":215},[129,748,749],{},"    security(\n",[129,751,752],{"class":131,"line":221},[129,753,754],{},"        (\"bearerAuth\" = []),\n",[129,756,757],{"class":131,"line":227},[129,758,759],{},"        (\"cookieAuth\" = [])\n",[129,761,762],{"class":131,"line":453},[129,763,764],{},"    ),\n",[129,766,767],{"class":131,"line":458},[129,768,166],{"emptyLinePlaceholder":165},[129,770,771],{"class":131,"line":464},[129,772,773],{},"    \u002F\u002F Response definitions\n",[129,775,776],{"class":131,"line":469},[129,777,778],{},"    responses(\n",[129,780,781],{"class":131,"line":475},[129,782,783],{},"        (status = 200, description = \"Login successful\", body = LoginResponse),\n",[129,785,786],{"class":131,"line":481},[129,787,788],{},"        (status = 401, description = \"Authentication failed\", body = ErrorResponse)\n",[129,790,791],{"class":131,"line":487},[129,792,341],{},[129,794,795],{"class":131,"line":492},[129,796,346],{},[129,798,799],{"class":131,"line":497},[129,800,801],{},"pub async fn login_handler(\n",[129,803,804],{"class":131,"line":502},[129,805,806],{},"    Json(payload): Json\u003CLoginRequest>,\n",[129,808,809],{"class":131,"line":508},[129,810,811],{},") -> Result\u003CJson\u003CLoginResponse>, StatusCode> {\n",[129,813,814],{"class":131,"line":513},[129,815,816],{},"    \u002F\u002F Handler implementation\n",[129,818,819],{"class":131,"line":519},[129,820,555],{},[107,822,824],{"id":823},"public-endpoint-no-authentication","Public Endpoint (No Authentication)",[120,826,828],{"className":307,"code":827,"language":309,"meta":125,"style":125},"\u002F\u002F\u002F User registration\n#[utoipa::path(\n    post,\n    path = \"\u002Fapi\u002Fauth\u002Fregister\",\n    tag = \"Authentication\",\n    request_body = RegisterUserSchema,\n    responses(\n        (status = 201, description = \"Registration successful\", body = UserResponse),\n        (status = 409, description = \"Email already exists\", body = ErrorResponse),\n        (status = 500, description = \"Server error\", body = ErrorResponse)\n    )\n)]\npub async fn register_user_handler(\n    State(data): State\u003CAppState>,\n    Json(body): Json\u003CRegisterUserSchema>,\n) -> Result\u003Cimpl IntoResponse, (StatusCode, Json\u003Cserde_json::Value>)> {\n    \u002F\u002F Implementation...\n}\n",[115,829,830,835,839,843,848,852,857,861,866,871,876,880,884,889,894,899,904,909],{"__ignoreMap":125},[129,831,832],{"class":131,"line":132},[129,833,834],{},"\u002F\u002F\u002F User registration\n",[129,836,837],{"class":131,"line":138},[129,838,692],{},[129,840,841],{"class":131,"line":144},[129,842,702],{},[129,844,845],{"class":131,"line":150},[129,846,847],{},"    path = \"\u002Fapi\u002Fauth\u002Fregister\",\n",[129,849,850],{"class":131,"line":156},[129,851,721],{},[129,853,854],{"class":131,"line":162},[129,855,856],{},"    request_body = RegisterUserSchema,\n",[129,858,859],{"class":131,"line":169},[129,860,778],{},[129,862,863],{"class":131,"line":175},[129,864,865],{},"        (status = 201, description = \"Registration successful\", body = UserResponse),\n",[129,867,868],{"class":131,"line":181},[129,869,870],{},"        (status = 409, description = \"Email already exists\", body = ErrorResponse),\n",[129,872,873],{"class":131,"line":187},[129,874,875],{},"        (status = 500, description = \"Server error\", body = ErrorResponse)\n",[129,877,878],{"class":131,"line":192},[129,879,341],{},[129,881,882],{"class":131,"line":198},[129,883,346],{},[129,885,886],{"class":131,"line":204},[129,887,888],{},"pub async fn register_user_handler(\n",[129,890,891],{"class":131,"line":210},[129,892,893],{},"    State(data): State\u003CAppState>,\n",[129,895,896],{"class":131,"line":215},[129,897,898],{},"    Json(body): Json\u003CRegisterUserSchema>,\n",[129,900,901],{"class":131,"line":221},[129,902,903],{},") -> Result\u003Cimpl IntoResponse, (StatusCode, Json\u003Cserde_json::Value>)> {\n",[129,905,906],{"class":131,"line":227},[129,907,908],{},"    \u002F\u002F Implementation...\n",[129,910,911],{"class":131,"line":453},[129,912,555],{},[107,914,916],{"id":915},"protected-endpoint-requires-authentication","Protected Endpoint (Requires Authentication)",[120,918,920],{"className":307,"code":919,"language":309,"meta":125,"style":125},"\u002F\u002F\u002F Get current user information\n\u002F\u002F\u002F Requires JWT Bearer Token or Cookie authentication\n#[utoipa::path(\n    get,\n    path = \"\u002Fapi\u002Fusers\u002Fme\",\n    tag = \"Users\",\n    security(\n        (\"bearerAuth\" = []),   \u002F\u002F Method 1: JWT Token\n        (\"cookieAuth\" = [])     \u002F\u002F Method 2: Cookie\n    ),\n    responses(\n        (status = 200, description = \"Success\", body = UserResponse),\n        (status = 401, description = \"Unauthorized\", body = ErrorResponse)\n    )\n)]\npub async fn get_me_handler(\n    Extension(user): Extension\u003CUserModel>,\n) -> Result\u003Cimpl IntoResponse, (StatusCode, Json\u003Cserde_json::Value>)> {\n    \u002F\u002F Implementation...\n}\n",[115,921,922,927,932,936,941,946,951,955,960,965,969,973,978,983,987,991,996,1001,1005,1009],{"__ignoreMap":125},[129,923,924],{"class":131,"line":132},[129,925,926],{},"\u002F\u002F\u002F Get current user information\n",[129,928,929],{"class":131,"line":138},[129,930,931],{},"\u002F\u002F\u002F Requires JWT Bearer Token or Cookie authentication\n",[129,933,934],{"class":131,"line":144},[129,935,692],{},[129,937,938],{"class":131,"line":150},[129,939,940],{},"    get,\n",[129,942,943],{"class":131,"line":156},[129,944,945],{},"    path = \"\u002Fapi\u002Fusers\u002Fme\",\n",[129,947,948],{"class":131,"line":162},[129,949,950],{},"    tag = \"Users\",\n",[129,952,953],{"class":131,"line":169},[129,954,749],{},[129,956,957],{"class":131,"line":175},[129,958,959],{},"        (\"bearerAuth\" = []),   \u002F\u002F Method 1: JWT Token\n",[129,961,962],{"class":131,"line":181},[129,963,964],{},"        (\"cookieAuth\" = [])     \u002F\u002F Method 2: Cookie\n",[129,966,967],{"class":131,"line":187},[129,968,764],{},[129,970,971],{"class":131,"line":192},[129,972,778],{},[129,974,975],{"class":131,"line":198},[129,976,977],{},"        (status = 200, description = \"Success\", body = UserResponse),\n",[129,979,980],{"class":131,"line":204},[129,981,982],{},"        (status = 401, description = \"Unauthorized\", body = ErrorResponse)\n",[129,984,985],{"class":131,"line":210},[129,986,341],{},[129,988,989],{"class":131,"line":215},[129,990,346],{},[129,992,993],{"class":131,"line":221},[129,994,995],{},"pub async fn get_me_handler(\n",[129,997,998],{"class":131,"line":227},[129,999,1000],{},"    Extension(user): Extension\u003CUserModel>,\n",[129,1002,1003],{"class":131,"line":453},[129,1004,903],{},[129,1006,1007],{"class":131,"line":458},[129,1008,908],{},[129,1010,1011],{"class":131,"line":464},[129,1012,555],{},[107,1014,1016],{"id":1015},"path-parameters","Path Parameters",[120,1018,1020],{"className":307,"code":1019,"language":309,"meta":125,"style":125},"#[utoipa::path(\n    delete,\n    path = \"\u002Fapi\u002Fkeys\u002F{key_id}\",\n    tag = \"API Keys\",\n    params(\n        (\"key_id\" = String, Path, description = \"ID of the API Key\")\n    ),\n    security(\n        (\"bearerAuth\" = [])\n    ),\n    responses(\n        (status = 200, description = \"Deletion successful\"),\n        (status = 404, description = \"Key not found\")\n    )\n)]\npub async fn delete_api_key_handler(\n    Path(key_id): Path\u003CString>,\n) -> impl IntoResponse {\n    \u002F\u002F Implementation...\n}\n",[115,1021,1022,1026,1031,1036,1041,1046,1051,1055,1059,1064,1068,1072,1077,1082,1086,1090,1095,1100,1105,1109],{"__ignoreMap":125},[129,1023,1024],{"class":131,"line":132},[129,1025,692],{},[129,1027,1028],{"class":131,"line":138},[129,1029,1030],{},"    delete,\n",[129,1032,1033],{"class":131,"line":144},[129,1034,1035],{},"    path = \"\u002Fapi\u002Fkeys\u002F{key_id}\",\n",[129,1037,1038],{"class":131,"line":150},[129,1039,1040],{},"    tag = \"API Keys\",\n",[129,1042,1043],{"class":131,"line":156},[129,1044,1045],{},"    params(\n",[129,1047,1048],{"class":131,"line":162},[129,1049,1050],{},"        (\"key_id\" = String, Path, description = \"ID of the API Key\")\n",[129,1052,1053],{"class":131,"line":169},[129,1054,764],{},[129,1056,1057],{"class":131,"line":175},[129,1058,749],{},[129,1060,1061],{"class":131,"line":181},[129,1062,1063],{},"        (\"bearerAuth\" = [])\n",[129,1065,1066],{"class":131,"line":187},[129,1067,764],{},[129,1069,1070],{"class":131,"line":192},[129,1071,778],{},[129,1073,1074],{"class":131,"line":198},[129,1075,1076],{},"        (status = 200, description = \"Deletion successful\"),\n",[129,1078,1079],{"class":131,"line":204},[129,1080,1081],{},"        (status = 404, description = \"Key not found\")\n",[129,1083,1084],{"class":131,"line":210},[129,1085,341],{},[129,1087,1088],{"class":131,"line":215},[129,1089,346],{},[129,1091,1092],{"class":131,"line":221},[129,1093,1094],{},"pub async fn delete_api_key_handler(\n",[129,1096,1097],{"class":131,"line":227},[129,1098,1099],{},"    Path(key_id): Path\u003CString>,\n",[129,1101,1102],{"class":131,"line":453},[129,1103,1104],{},") -> impl IntoResponse {\n",[129,1106,1107],{"class":131,"line":458},[129,1108,908],{},[129,1110,1111],{"class":131,"line":464},[129,1112,555],{},[107,1114,1116],{"id":1115},"query-parameters","Query Parameters",[120,1118,1120],{"className":307,"code":1119,"language":309,"meta":125,"style":125},"#[utoipa::path(\n    get,\n    path = \"\u002Fapi\u002Fusers\",\n    tag = \"Users\",\n    params(\n        (\"page\" = Option\u003Ci32>, Query, description = \"Page number\"),\n        (\"limit\" = Option\u003Ci32>, Query, description = \"Items per page\")\n    ),\n    responses(\n        (status = 200, description = \"Success\")\n    )\n)]\npub async fn list_users(\n    Query(params): Query\u003CListParams>,\n) -> impl IntoResponse {\n    \u002F\u002F Implementation...\n}\n",[115,1121,1122,1126,1130,1135,1139,1143,1148,1153,1157,1161,1166,1170,1174,1179,1184,1188,1192],{"__ignoreMap":125},[129,1123,1124],{"class":131,"line":132},[129,1125,692],{},[129,1127,1128],{"class":131,"line":138},[129,1129,940],{},[129,1131,1132],{"class":131,"line":144},[129,1133,1134],{},"    path = \"\u002Fapi\u002Fusers\",\n",[129,1136,1137],{"class":131,"line":150},[129,1138,950],{},[129,1140,1141],{"class":131,"line":156},[129,1142,1045],{},[129,1144,1145],{"class":131,"line":162},[129,1146,1147],{},"        (\"page\" = Option\u003Ci32>, Query, description = \"Page number\"),\n",[129,1149,1150],{"class":131,"line":169},[129,1151,1152],{},"        (\"limit\" = Option\u003Ci32>, Query, description = \"Items per page\")\n",[129,1154,1155],{"class":131,"line":175},[129,1156,764],{},[129,1158,1159],{"class":131,"line":181},[129,1160,778],{},[129,1162,1163],{"class":131,"line":187},[129,1164,1165],{},"        (status = 200, description = \"Success\")\n",[129,1167,1168],{"class":131,"line":192},[129,1169,341],{},[129,1171,1172],{"class":131,"line":198},[129,1173,346],{},[129,1175,1176],{"class":131,"line":204},[129,1177,1178],{},"pub async fn list_users(\n",[129,1180,1181],{"class":131,"line":210},[129,1182,1183],{},"    Query(params): Query\u003CListParams>,\n",[129,1185,1186],{"class":131,"line":215},[129,1187,1104],{},[129,1189,1190],{"class":131,"line":221},[129,1191,908],{},[129,1193,1194],{"class":131,"line":227},[129,1195,555],{},[60,1197,1199],{"id":1198},"defining-data-models","Defining Data Models",[107,1201,1203],{"id":1202},"basic-usage","Basic Usage",[120,1205,1207],{"className":307,"code":1206,"language":309,"meta":125,"style":125},"use serde::{Deserialize, Serialize};\nuse utoipa::ToSchema;\n\n#[derive(Debug, Serialize, Deserialize, ToSchema)]\npub struct UserResponse {\n    #[schema(example = \"123e4567-e89b-12d3-a456-426614174000\")]\n    pub id: String,\n\n    #[schema(example = \"john@example.com\")]\n    pub email: String,\n\n    #[schema(example = \"John Doe\")]\n    pub name: String,\n}\n",[115,1208,1209,1214,1219,1223,1228,1233,1238,1243,1247,1252,1257,1261,1266,1271],{"__ignoreMap":125},[129,1210,1211],{"class":131,"line":132},[129,1212,1213],{},"use serde::{Deserialize, Serialize};\n",[129,1215,1216],{"class":131,"line":138},[129,1217,1218],{},"use utoipa::ToSchema;\n",[129,1220,1221],{"class":131,"line":144},[129,1222,166],{"emptyLinePlaceholder":165},[129,1224,1225],{"class":131,"line":150},[129,1226,1227],{},"#[derive(Debug, Serialize, Deserialize, ToSchema)]\n",[129,1229,1230],{"class":131,"line":156},[129,1231,1232],{},"pub struct UserResponse {\n",[129,1234,1235],{"class":131,"line":162},[129,1236,1237],{},"    #[schema(example = \"123e4567-e89b-12d3-a456-426614174000\")]\n",[129,1239,1240],{"class":131,"line":169},[129,1241,1242],{},"    pub id: String,\n",[129,1244,1245],{"class":131,"line":175},[129,1246,166],{"emptyLinePlaceholder":165},[129,1248,1249],{"class":131,"line":181},[129,1250,1251],{},"    #[schema(example = \"john@example.com\")]\n",[129,1253,1254],{"class":131,"line":187},[129,1255,1256],{},"    pub email: String,\n",[129,1258,1259],{"class":131,"line":192},[129,1260,166],{"emptyLinePlaceholder":165},[129,1262,1263],{"class":131,"line":198},[129,1264,1265],{},"    #[schema(example = \"John Doe\")]\n",[129,1267,1268],{"class":131,"line":204},[129,1269,1270],{},"    pub name: String,\n",[129,1272,1273],{"class":131,"line":210},[129,1274,555],{},[107,1276,1278],{"id":1277},"advanced-field-level-annotations","Advanced Field-Level Annotations",[120,1280,1282],{"className":307,"code":1281,"language":309,"meta":125,"style":125},"#[derive(Debug, Serialize, Deserialize, ToSchema)]\npub struct RegisterUserSchema {\n    \u002F\u002F\u002F Email address (must be unique)\n    #[schema(example = \"user@example.com\", max_length = 100)]\n    pub email: String,\n\n    \u002F\u002F\u002F Password (minimum 8 characters)\n    #[schema(example = \"password123\", min_length = 8)]\n    pub password: String,\n\n    \u002F\u002F\u002F User's full name\n    #[schema(example = \"张三\")]\n    pub name: String,\n\n    \u002F\u002F\u002F Whether user is an admin\n    #[schema(example = false)]\n    pub is_admin: bool,\n}\n",[115,1283,1284,1288,1293,1298,1303,1307,1311,1316,1321,1326,1330,1335,1340,1344,1348,1353,1358,1363],{"__ignoreMap":125},[129,1285,1286],{"class":131,"line":132},[129,1287,1227],{},[129,1289,1290],{"class":131,"line":138},[129,1291,1292],{},"pub struct RegisterUserSchema {\n",[129,1294,1295],{"class":131,"line":144},[129,1296,1297],{},"    \u002F\u002F\u002F Email address (must be unique)\n",[129,1299,1300],{"class":131,"line":150},[129,1301,1302],{},"    #[schema(example = \"user@example.com\", max_length = 100)]\n",[129,1304,1305],{"class":131,"line":156},[129,1306,1256],{},[129,1308,1309],{"class":131,"line":162},[129,1310,166],{"emptyLinePlaceholder":165},[129,1312,1313],{"class":131,"line":169},[129,1314,1315],{},"    \u002F\u002F\u002F Password (minimum 8 characters)\n",[129,1317,1318],{"class":131,"line":175},[129,1319,1320],{},"    #[schema(example = \"password123\", min_length = 8)]\n",[129,1322,1323],{"class":131,"line":181},[129,1324,1325],{},"    pub password: String,\n",[129,1327,1328],{"class":131,"line":187},[129,1329,166],{"emptyLinePlaceholder":165},[129,1331,1332],{"class":131,"line":192},[129,1333,1334],{},"    \u002F\u002F\u002F User's full name\n",[129,1336,1337],{"class":131,"line":198},[129,1338,1339],{},"    #[schema(example = \"张三\")]\n",[129,1341,1342],{"class":131,"line":204},[129,1343,1270],{},[129,1345,1346],{"class":131,"line":210},[129,1347,166],{"emptyLinePlaceholder":165},[129,1349,1350],{"class":131,"line":215},[129,1351,1352],{},"    \u002F\u002F\u002F Whether user is an admin\n",[129,1354,1355],{"class":131,"line":221},[129,1356,1357],{},"    #[schema(example = false)]\n",[129,1359,1360],{"class":131,"line":227},[129,1361,1362],{},"    pub is_admin: bool,\n",[129,1364,1365],{"class":131,"line":453},[129,1366,555],{},[107,1368,1370],{"id":1369},"nested-structures","Nested Structures",[120,1372,1374],{"className":307,"code":1373,"language":309,"meta":125,"style":125},"#[derive(Debug, Serialize, Deserialize, ToSchema)]\npub struct LoginResponse {\n    pub data: LoginData,\n}\n\n#[derive(Debug, Serialize, Deserialize, ToSchema)]\npub struct LoginData {\n    #[schema(example = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\")]\n    pub token: String,\n\n    pub user: FilteredUser,\n}\n",[115,1375,1376,1380,1385,1390,1394,1398,1402,1407,1412,1417,1421,1426],{"__ignoreMap":125},[129,1377,1378],{"class":131,"line":132},[129,1379,1227],{},[129,1381,1382],{"class":131,"line":138},[129,1383,1384],{},"pub struct LoginResponse {\n",[129,1386,1387],{"class":131,"line":144},[129,1388,1389],{},"    pub data: LoginData,\n",[129,1391,1392],{"class":131,"line":150},[129,1393,555],{},[129,1395,1396],{"class":131,"line":156},[129,1397,166],{"emptyLinePlaceholder":165},[129,1399,1400],{"class":131,"line":162},[129,1401,1227],{},[129,1403,1404],{"class":131,"line":169},[129,1405,1406],{},"pub struct LoginData {\n",[129,1408,1409],{"class":131,"line":175},[129,1410,1411],{},"    #[schema(example = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\")]\n",[129,1413,1414],{"class":131,"line":181},[129,1415,1416],{},"    pub token: String,\n",[129,1418,1419],{"class":131,"line":187},[129,1420,166],{"emptyLinePlaceholder":165},[129,1422,1423],{"class":131,"line":192},[129,1424,1425],{},"    pub user: FilteredUser,\n",[129,1427,1428],{"class":131,"line":198},[129,1429,555],{},[107,1431,1433],{"id":1432},"datetime-handling-important","DateTime Handling (Important!)",[120,1435,1437],{"className":307,"code":1436,"language":309,"meta":125,"style":125},"use chrono::{DateTime, Utc};\n\n#[derive(Debug, Serialize, Deserialize, ToSchema)]\npub struct UserResponse {\n    pub id: String,\n\n    \u002F\u002F ❌ This might not work in utoipa 4.x\n    \u002F\u002F pub created_at: DateTime\u003CUtc>,\n\n    \u002F\u002F ✅ Correct: Use value_type override\n    #[schema(value_type = String, format = \"date-time\")]\n    pub created_at: DateTime\u003CUtc>,\n}\n",[115,1438,1439,1444,1448,1452,1456,1460,1464,1469,1474,1478,1483,1488,1493],{"__ignoreMap":125},[129,1440,1441],{"class":131,"line":132},[129,1442,1443],{},"use chrono::{DateTime, Utc};\n",[129,1445,1446],{"class":131,"line":138},[129,1447,166],{"emptyLinePlaceholder":165},[129,1449,1450],{"class":131,"line":144},[129,1451,1227],{},[129,1453,1454],{"class":131,"line":150},[129,1455,1232],{},[129,1457,1458],{"class":131,"line":156},[129,1459,1242],{},[129,1461,1462],{"class":131,"line":162},[129,1463,166],{"emptyLinePlaceholder":165},[129,1465,1466],{"class":131,"line":169},[129,1467,1468],{},"    \u002F\u002F ❌ This might not work in utoipa 4.x\n",[129,1470,1471],{"class":131,"line":175},[129,1472,1473],{},"    \u002F\u002F pub created_at: DateTime\u003CUtc>,\n",[129,1475,1476],{"class":131,"line":181},[129,1477,166],{"emptyLinePlaceholder":165},[129,1479,1480],{"class":131,"line":187},[129,1481,1482],{},"    \u002F\u002F ✅ Correct: Use value_type override\n",[129,1484,1485],{"class":131,"line":192},[129,1486,1487],{},"    #[schema(value_type = String, format = \"date-time\")]\n",[129,1489,1490],{"class":131,"line":198},[129,1491,1492],{},"    pub created_at: DateTime\u003CUtc>,\n",[129,1494,1495],{"class":131,"line":204},[129,1496,555],{},[56,1498,1499],{},"Or use custom formatting:",[120,1501,1503],{"className":307,"code":1502,"language":309,"meta":125,"style":125},"#[derive(Debug, Serialize, Deserialize, ToSchema)]\npub struct ApiKeyInfo {\n    pub id: String,\n    pub name: String,\n\n    \u002F\u002F\u002F Creation time (ISO 8601 format)\n    #[schema(\n        value_type = String,\n        example = \"2024-01-15T10:30:00Z\"\n    )]\n    pub created_at: DateTimeWithTimeZone,\n}\n",[115,1504,1505,1509,1514,1518,1522,1526,1531,1536,1541,1546,1551,1556],{"__ignoreMap":125},[129,1506,1507],{"class":131,"line":132},[129,1508,1227],{},[129,1510,1511],{"class":131,"line":138},[129,1512,1513],{},"pub struct ApiKeyInfo {\n",[129,1515,1516],{"class":131,"line":144},[129,1517,1242],{},[129,1519,1520],{"class":131,"line":150},[129,1521,1270],{},[129,1523,1524],{"class":131,"line":156},[129,1525,166],{"emptyLinePlaceholder":165},[129,1527,1528],{"class":131,"line":162},[129,1529,1530],{},"    \u002F\u002F\u002F Creation time (ISO 8601 format)\n",[129,1532,1533],{"class":131,"line":169},[129,1534,1535],{},"    #[schema(\n",[129,1537,1538],{"class":131,"line":175},[129,1539,1540],{},"        value_type = String,\n",[129,1542,1543],{"class":131,"line":181},[129,1544,1545],{},"        example = \"2024-01-15T10:30:00Z\"\n",[129,1547,1548],{"class":131,"line":187},[129,1549,1550],{},"    )]\n",[129,1552,1553],{"class":131,"line":192},[129,1554,1555],{},"    pub created_at: DateTimeWithTimeZone,\n",[129,1557,1558],{"class":131,"line":198},[129,1559,555],{},[60,1561,1563],{"id":1562},"generating-openapi-documentation","Generating OpenAPI Documentation",[107,1565,1567],{"id":1566},"complete-apidoc-definition","Complete ApiDoc Definition",[120,1569,1571],{"className":307,"code":1570,"language":309,"meta":125,"style":125},"use acrospire_auth::{\n    docs::JwtSecurityAddon,\n    docs::schemas::*,\n    handler::*,\n};\nuse utoipa::OpenApi;\n\n#[derive(OpenApi)]\n#[openapi(\n    \u002F\u002F 1. Register all API paths\n    paths(\n        health_checker_handler,\n        register_user_handler,\n        login_user_handler,\n        logout_handler,\n        get_me_handler,\n        refresh_token_handler,\n        create_api_key_handler,\n        list_api_keys_handler,\n        validate_api_key_handler,\n        delete_api_key_handler,\n        protected_data_handler\n    ),\n\n    \u002F\u002F 2. Register all data models\n    components(\n        schemas(\n            HealthCheckResponse,\n            RegisterUserSchema,\n            LoginUserSchema,\n            UserResponse,\n            FilteredUser,\n            LoginResponse,\n            LoginData,\n            LogoutResponse,\n            RefreshTokenRequest,\n            TokenPairResponse,\n            TokenPairData,\n            CreateApiKeySchema,\n            CreateApiKeyResponse,\n            CreateApiKeyData,\n            ApiKeyListResponse,\n            ApiKeyListData,\n            ApiKeyInfo,\n            ValidateApiKeySchema,\n            ValidateApiKeyResponse,\n            ValidateApiKeyData,\n            DeleteApiKeyResponse,\n            ErrorResponse,\n        )\n    ),\n\n    \u002F\u002F 3. Define tag groups\n    tags(\n        (name = \"System\", description = \"System health check and similar endpoints\"),\n        (name = \"Authentication\", description = \"User registration, login, logout endpoints\"),\n        (name = \"Users\", description = \"User information management endpoints\"),\n        (name = \"API Keys\", description = \"API Key management endpoints\"),\n        (name = \"Protected\", description = \"Protected endpoints requiring API Key authentication\")\n    ),\n\n    \u002F\u002F 4. API basic information\n    info(\n        title = \"API Key Store Auth API\",\n        version = \"1.0.0\",\n        description = \"Authentication service API documentation based on JWT and API Key\"\n    ),\n\n    \u002F\u002F 5. Add security schemes (critical!)\n    modifiers(&JwtSecurityAddon)\n)]\nstruct ApiDoc;\n\n\u002F\u002F Generate OpenAPI documentation\nlet openapi = ApiDoc::openapi();\n",[115,1572,1573,1578,1583,1588,1593,1598,1603,1607,1611,1615,1620,1625,1630,1635,1640,1645,1650,1655,1660,1665,1670,1675,1680,1684,1688,1693,1698,1703,1708,1713,1718,1723,1728,1733,1738,1743,1748,1754,1760,1766,1772,1778,1784,1790,1796,1802,1808,1814,1820,1826,1832,1837,1842,1848,1854,1860,1866,1872,1878,1884,1889,1894,1900,1906,1912,1918,1924,1929,1934,1940,1946,1951,1956,1961,1967],{"__ignoreMap":125},[129,1574,1575],{"class":131,"line":132},[129,1576,1577],{},"use acrospire_auth::{\n",[129,1579,1580],{"class":131,"line":138},[129,1581,1582],{},"    docs::JwtSecurityAddon,\n",[129,1584,1585],{"class":131,"line":144},[129,1586,1587],{},"    docs::schemas::*,\n",[129,1589,1590],{"class":131,"line":150},[129,1591,1592],{},"    handler::*,\n",[129,1594,1595],{"class":131,"line":156},[129,1596,1597],{},"};\n",[129,1599,1600],{"class":131,"line":162},[129,1601,1602],{},"use utoipa::OpenApi;\n",[129,1604,1605],{"class":131,"line":169},[129,1606,166],{"emptyLinePlaceholder":165},[129,1608,1609],{"class":131,"line":175},[129,1610,321],{},[129,1612,1613],{"class":131,"line":181},[129,1614,326],{},[129,1616,1617],{"class":131,"line":187},[129,1618,1619],{},"    \u002F\u002F 1. Register all API paths\n",[129,1621,1622],{"class":131,"line":192},[129,1623,1624],{},"    paths(\n",[129,1626,1627],{"class":131,"line":198},[129,1628,1629],{},"        health_checker_handler,\n",[129,1631,1632],{"class":131,"line":204},[129,1633,1634],{},"        register_user_handler,\n",[129,1636,1637],{"class":131,"line":210},[129,1638,1639],{},"        login_user_handler,\n",[129,1641,1642],{"class":131,"line":215},[129,1643,1644],{},"        logout_handler,\n",[129,1646,1647],{"class":131,"line":221},[129,1648,1649],{},"        get_me_handler,\n",[129,1651,1652],{"class":131,"line":227},[129,1653,1654],{},"        refresh_token_handler,\n",[129,1656,1657],{"class":131,"line":453},[129,1658,1659],{},"        create_api_key_handler,\n",[129,1661,1662],{"class":131,"line":458},[129,1663,1664],{},"        list_api_keys_handler,\n",[129,1666,1667],{"class":131,"line":464},[129,1668,1669],{},"        validate_api_key_handler,\n",[129,1671,1672],{"class":131,"line":469},[129,1673,1674],{},"        delete_api_key_handler,\n",[129,1676,1677],{"class":131,"line":475},[129,1678,1679],{},"        protected_data_handler\n",[129,1681,1682],{"class":131,"line":481},[129,1683,764],{},[129,1685,1686],{"class":131,"line":487},[129,1687,166],{"emptyLinePlaceholder":165},[129,1689,1690],{"class":131,"line":492},[129,1691,1692],{},"    \u002F\u002F 2. Register all data models\n",[129,1694,1695],{"class":131,"line":497},[129,1696,1697],{},"    components(\n",[129,1699,1700],{"class":131,"line":502},[129,1701,1702],{},"        schemas(\n",[129,1704,1705],{"class":131,"line":508},[129,1706,1707],{},"            HealthCheckResponse,\n",[129,1709,1710],{"class":131,"line":513},[129,1711,1712],{},"            RegisterUserSchema,\n",[129,1714,1715],{"class":131,"line":519},[129,1716,1717],{},"            LoginUserSchema,\n",[129,1719,1720],{"class":131,"line":524},[129,1721,1722],{},"            UserResponse,\n",[129,1724,1725],{"class":131,"line":530},[129,1726,1727],{},"            FilteredUser,\n",[129,1729,1730],{"class":131,"line":535},[129,1731,1732],{},"            LoginResponse,\n",[129,1734,1735],{"class":131,"line":540},[129,1736,1737],{},"            LoginData,\n",[129,1739,1740],{"class":131,"line":546},[129,1741,1742],{},"            LogoutResponse,\n",[129,1744,1745],{"class":131,"line":552},[129,1746,1747],{},"            RefreshTokenRequest,\n",[129,1749,1751],{"class":131,"line":1750},37,[129,1752,1753],{},"            TokenPairResponse,\n",[129,1755,1757],{"class":131,"line":1756},38,[129,1758,1759],{},"            TokenPairData,\n",[129,1761,1763],{"class":131,"line":1762},39,[129,1764,1765],{},"            CreateApiKeySchema,\n",[129,1767,1769],{"class":131,"line":1768},40,[129,1770,1771],{},"            CreateApiKeyResponse,\n",[129,1773,1775],{"class":131,"line":1774},41,[129,1776,1777],{},"            CreateApiKeyData,\n",[129,1779,1781],{"class":131,"line":1780},42,[129,1782,1783],{},"            ApiKeyListResponse,\n",[129,1785,1787],{"class":131,"line":1786},43,[129,1788,1789],{},"            ApiKeyListData,\n",[129,1791,1793],{"class":131,"line":1792},44,[129,1794,1795],{},"            ApiKeyInfo,\n",[129,1797,1799],{"class":131,"line":1798},45,[129,1800,1801],{},"            ValidateApiKeySchema,\n",[129,1803,1805],{"class":131,"line":1804},46,[129,1806,1807],{},"            ValidateApiKeyResponse,\n",[129,1809,1811],{"class":131,"line":1810},47,[129,1812,1813],{},"            ValidateApiKeyData,\n",[129,1815,1817],{"class":131,"line":1816},48,[129,1818,1819],{},"            DeleteApiKeyResponse,\n",[129,1821,1823],{"class":131,"line":1822},49,[129,1824,1825],{},"            ErrorResponse,\n",[129,1827,1829],{"class":131,"line":1828},50,[129,1830,1831],{},"        )\n",[129,1833,1835],{"class":131,"line":1834},51,[129,1836,764],{},[129,1838,1840],{"class":131,"line":1839},52,[129,1841,166],{"emptyLinePlaceholder":165},[129,1843,1845],{"class":131,"line":1844},53,[129,1846,1847],{},"    \u002F\u002F 3. Define tag groups\n",[129,1849,1851],{"class":131,"line":1850},54,[129,1852,1853],{},"    tags(\n",[129,1855,1857],{"class":131,"line":1856},55,[129,1858,1859],{},"        (name = \"System\", description = \"System health check and similar endpoints\"),\n",[129,1861,1863],{"class":131,"line":1862},56,[129,1864,1865],{},"        (name = \"Authentication\", description = \"User registration, login, logout endpoints\"),\n",[129,1867,1869],{"class":131,"line":1868},57,[129,1870,1871],{},"        (name = \"Users\", description = \"User information management endpoints\"),\n",[129,1873,1875],{"class":131,"line":1874},58,[129,1876,1877],{},"        (name = \"API Keys\", description = \"API Key management endpoints\"),\n",[129,1879,1881],{"class":131,"line":1880},59,[129,1882,1883],{},"        (name = \"Protected\", description = \"Protected endpoints requiring API Key authentication\")\n",[129,1885,1887],{"class":131,"line":1886},60,[129,1888,764],{},[129,1890,1892],{"class":131,"line":1891},61,[129,1893,166],{"emptyLinePlaceholder":165},[129,1895,1897],{"class":131,"line":1896},62,[129,1898,1899],{},"    \u002F\u002F 4. API basic information\n",[129,1901,1903],{"class":131,"line":1902},63,[129,1904,1905],{},"    info(\n",[129,1907,1909],{"class":131,"line":1908},64,[129,1910,1911],{},"        title = \"API Key Store Auth API\",\n",[129,1913,1915],{"class":131,"line":1914},65,[129,1916,1917],{},"        version = \"1.0.0\",\n",[129,1919,1921],{"class":131,"line":1920},66,[129,1922,1923],{},"        description = \"Authentication service API documentation based on JWT and API Key\"\n",[129,1925,1927],{"class":131,"line":1926},67,[129,1928,764],{},[129,1930,1932],{"class":131,"line":1931},68,[129,1933,166],{"emptyLinePlaceholder":165},[129,1935,1937],{"class":131,"line":1936},69,[129,1938,1939],{},"    \u002F\u002F 5. Add security schemes (critical!)\n",[129,1941,1943],{"class":131,"line":1942},70,[129,1944,1945],{},"    modifiers(&JwtSecurityAddon)\n",[129,1947,1949],{"class":131,"line":1948},71,[129,1950,346],{},[129,1952,1954],{"class":131,"line":1953},72,[129,1955,351],{},[129,1957,1959],{"class":131,"line":1958},73,[129,1960,166],{"emptyLinePlaceholder":165},[129,1962,1964],{"class":131,"line":1963},74,[129,1965,1966],{},"\u002F\u002F Generate OpenAPI documentation\n",[129,1968,1970],{"class":131,"line":1969},75,[129,1971,1972],{},"let openapi = ApiDoc::openapi();\n",[60,1974,1976],{"id":1975},"integrating-swagger-ui","Integrating Swagger UI",[107,1978,1980],{"id":1979},"method-integration-in-mainrs-recommended","Method: Integration in main.rs (Recommended)",[120,1982,1984],{"className":307,"code":1983,"language":309,"meta":125,"style":125},"use utoipa_swagger_ui::SwaggerUi;\n\n#[tokio::main]\nasync fn main() -> Result\u003C(), Box\u003Cdyn std::error::Error>> {\n    let app_state = AppState::new().await?;\n\n    \u002F\u002F Generate OpenAPI documentation\n    #[derive(OpenApi)]\n    #[openapi(\n        paths(...),\n        components(schemas(...)),\n        modifiers(&JwtSecurityAddon)\n    )]\n    struct ApiDoc;\n\n    let openapi = ApiDoc::openapi();\n\n    \u002F\u002F Create router\n    let app = create_router()\n        .merge(\n            SwaggerUi::new(\"\u002Fswagger-ui\")\n                .url(\"\u002Fapi-docs\u002Fopenapi.json\", openapi)\n                .config(\n                    utoipa_swagger_ui::Config::default()\n                        .try_it_out_enabled(true)      \u002F\u002F Enable \"Try it out\"\n                        .deep_linking(true)           \u002F\u002F Enable deep linking\n                        .display_request_duration(true) \u002F\u002F Show request duration\n                )\n        );\n\n    \u002F\u002F Start server\n    let listener = tokio::net::TcpListener::bind(\"0.0.0.0:3081\").await?;\n    axum::serve(listener, app).await?;\n\n    Ok(())\n}\n",[115,1985,1986,1991,1995,2000,2005,2010,2014,2019,2024,2029,2034,2039,2044,2048,2053,2057,2062,2066,2071,2076,2081,2086,2091,2096,2101,2106,2111,2116,2121,2126,2130,2135,2140,2145,2149,2154],{"__ignoreMap":125},[129,1987,1988],{"class":131,"line":132},[129,1989,1990],{},"use utoipa_swagger_ui::SwaggerUi;\n",[129,1992,1993],{"class":131,"line":138},[129,1994,166],{"emptyLinePlaceholder":165},[129,1996,1997],{"class":131,"line":144},[129,1998,1999],{},"#[tokio::main]\n",[129,2001,2002],{"class":131,"line":150},[129,2003,2004],{},"async fn main() -> Result\u003C(), Box\u003Cdyn std::error::Error>> {\n",[129,2006,2007],{"class":131,"line":156},[129,2008,2009],{},"    let app_state = AppState::new().await?;\n",[129,2011,2012],{"class":131,"line":162},[129,2013,166],{"emptyLinePlaceholder":165},[129,2015,2016],{"class":131,"line":169},[129,2017,2018],{},"    \u002F\u002F Generate OpenAPI documentation\n",[129,2020,2021],{"class":131,"line":175},[129,2022,2023],{},"    #[derive(OpenApi)]\n",[129,2025,2026],{"class":131,"line":181},[129,2027,2028],{},"    #[openapi(\n",[129,2030,2031],{"class":131,"line":187},[129,2032,2033],{},"        paths(...),\n",[129,2035,2036],{"class":131,"line":192},[129,2037,2038],{},"        components(schemas(...)),\n",[129,2040,2041],{"class":131,"line":198},[129,2042,2043],{},"        modifiers(&JwtSecurityAddon)\n",[129,2045,2046],{"class":131,"line":204},[129,2047,1550],{},[129,2049,2050],{"class":131,"line":210},[129,2051,2052],{},"    struct ApiDoc;\n",[129,2054,2055],{"class":131,"line":215},[129,2056,166],{"emptyLinePlaceholder":165},[129,2058,2059],{"class":131,"line":221},[129,2060,2061],{},"    let openapi = ApiDoc::openapi();\n",[129,2063,2064],{"class":131,"line":227},[129,2065,166],{"emptyLinePlaceholder":165},[129,2067,2068],{"class":131,"line":453},[129,2069,2070],{},"    \u002F\u002F Create router\n",[129,2072,2073],{"class":131,"line":458},[129,2074,2075],{},"    let app = create_router()\n",[129,2077,2078],{"class":131,"line":464},[129,2079,2080],{},"        .merge(\n",[129,2082,2083],{"class":131,"line":469},[129,2084,2085],{},"            SwaggerUi::new(\"\u002Fswagger-ui\")\n",[129,2087,2088],{"class":131,"line":475},[129,2089,2090],{},"                .url(\"\u002Fapi-docs\u002Fopenapi.json\", openapi)\n",[129,2092,2093],{"class":131,"line":481},[129,2094,2095],{},"                .config(\n",[129,2097,2098],{"class":131,"line":487},[129,2099,2100],{},"                    utoipa_swagger_ui::Config::default()\n",[129,2102,2103],{"class":131,"line":492},[129,2104,2105],{},"                        .try_it_out_enabled(true)      \u002F\u002F Enable \"Try it out\"\n",[129,2107,2108],{"class":131,"line":497},[129,2109,2110],{},"                        .deep_linking(true)           \u002F\u002F Enable deep linking\n",[129,2112,2113],{"class":131,"line":502},[129,2114,2115],{},"                        .display_request_duration(true) \u002F\u002F Show request duration\n",[129,2117,2118],{"class":131,"line":508},[129,2119,2120],{},"                )\n",[129,2122,2123],{"class":131,"line":513},[129,2124,2125],{},"        );\n",[129,2127,2128],{"class":131,"line":519},[129,2129,166],{"emptyLinePlaceholder":165},[129,2131,2132],{"class":131,"line":524},[129,2133,2134],{},"    \u002F\u002F Start server\n",[129,2136,2137],{"class":131,"line":530},[129,2138,2139],{},"    let listener = tokio::net::TcpListener::bind(\"0.0.0.0:3081\").await?;\n",[129,2141,2142],{"class":131,"line":535},[129,2143,2144],{},"    axum::serve(listener, app).await?;\n",[129,2146,2147],{"class":131,"line":540},[129,2148,166],{"emptyLinePlaceholder":165},[129,2150,2151],{"class":131,"line":546},[129,2152,2153],{},"    Ok(())\n",[129,2155,2156],{"class":131,"line":552},[129,2157,555],{},[107,2159,2161],{"id":2160},"swagger-ui-configuration-options","Swagger UI Configuration Options",[120,2163,2165],{"className":307,"code":2164,"language":309,"meta":125,"style":125},"SwaggerUi::new(\"\u002Fswagger-ui\")                    \u002F\u002F UI mount path\n    .url(\"\u002Fapi-docs\u002Fopenapi.json\", openapi)     \u002F\u002F OpenAPI JSON path\n    .config(\n        utoipa_swagger_ui::Config::default()\n            \u002F\u002F Configuration options\n            .try_it_out_enabled(true)           \u002F\u002F Enable testing functionality\n            .deep_linking(true)                \u002F\u002F Deep link to specific API\n            .display_request_duration(true)     \u002F\u002F Show request duration\n            .default_models_expand_depth(1)     \u002F\u002F Default model expansion depth\n            .persist_authorization(true)        \u002F\u002F Persist authentication info\n            .filter(true)                       \u002F\u002F Enable search filtering\n    )\n",[115,2166,2167,2172,2177,2182,2187,2192,2197,2202,2207,2212,2217,2222],{"__ignoreMap":125},[129,2168,2169],{"class":131,"line":132},[129,2170,2171],{},"SwaggerUi::new(\"\u002Fswagger-ui\")                    \u002F\u002F UI mount path\n",[129,2173,2174],{"class":131,"line":138},[129,2175,2176],{},"    .url(\"\u002Fapi-docs\u002Fopenapi.json\", openapi)     \u002F\u002F OpenAPI JSON path\n",[129,2178,2179],{"class":131,"line":144},[129,2180,2181],{},"    .config(\n",[129,2183,2184],{"class":131,"line":150},[129,2185,2186],{},"        utoipa_swagger_ui::Config::default()\n",[129,2188,2189],{"class":131,"line":156},[129,2190,2191],{},"            \u002F\u002F Configuration options\n",[129,2193,2194],{"class":131,"line":162},[129,2195,2196],{},"            .try_it_out_enabled(true)           \u002F\u002F Enable testing functionality\n",[129,2198,2199],{"class":131,"line":169},[129,2200,2201],{},"            .deep_linking(true)                \u002F\u002F Deep link to specific API\n",[129,2203,2204],{"class":131,"line":175},[129,2205,2206],{},"            .display_request_duration(true)     \u002F\u002F Show request duration\n",[129,2208,2209],{"class":131,"line":181},[129,2210,2211],{},"            .default_models_expand_depth(1)     \u002F\u002F Default model expansion depth\n",[129,2213,2214],{"class":131,"line":187},[129,2215,2216],{},"            .persist_authorization(true)        \u002F\u002F Persist authentication info\n",[129,2218,2219],{"class":131,"line":192},[129,2220,2221],{},"            .filter(true)                       \u002F\u002F Enable search filtering\n",[129,2223,2224],{"class":131,"line":198},[129,2225,341],{},[107,2227,2229],{"id":2228},"access-addresses","Access Addresses",[56,2231,2232],{},"After starting the server:",[68,2234,2235,2247],{},[71,2236,2237,2240,2241],{},[74,2238,2239],{},"Swagger UI",": ",[2242,2243,2244],"a",{"href":2244,"rel":2245},"http:\u002F\u002Flocalhost:3081\u002Fswagger-ui",[2246],"nofollow",[71,2248,2249,2240,2252],{},[74,2250,2251],{},"OpenAPI JSON",[2242,2253,2254],{"href":2254,"rel":2255},"http:\u002F\u002Flocalhost:3081\u002Fapi-docs\u002Fopenapi.json",[2246],[60,2257,2259],{"id":2258},"complete-workflow-example","Complete Workflow Example",[107,2261,2263],{"id":2262},"_1-define-data-models","1. Define Data Models",[120,2265,2267],{"className":307,"code":2266,"language":309,"meta":125,"style":125},"\u002F\u002F src\u002Fdocs\u002Fschemas.rs\nuse serde::{Deserialize, Serialize};\nuse utoipa::ToSchema;\n\n#[derive(Debug, Serialize, Deserialize, ToSchema)]\npub struct LoginUserSchema {\n    #[schema(example = \"user@example.com\")]\n    pub email: String,\n\n    #[schema(example = \"password123\")]\n    pub password: String,\n}\n\n#[derive(Debug, Serialize, Deserialize, ToSchema)]\npub struct LoginResponse {\n    pub data: LoginData,\n}\n\n#[derive(Debug, Serialize, Deserialize, ToSchema)]\npub struct LoginData {\n    #[schema(example = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\")]\n    pub token: String,\n    pub user: FilteredUser,\n}\n",[115,2268,2269,2274,2278,2282,2286,2290,2295,2300,2304,2308,2313,2317,2321,2325,2329,2333,2337,2341,2345,2349,2353,2357,2361,2365],{"__ignoreMap":125},[129,2270,2271],{"class":131,"line":132},[129,2272,2273],{},"\u002F\u002F src\u002Fdocs\u002Fschemas.rs\n",[129,2275,2276],{"class":131,"line":138},[129,2277,1213],{},[129,2279,2280],{"class":131,"line":144},[129,2281,1218],{},[129,2283,2284],{"class":131,"line":150},[129,2285,166],{"emptyLinePlaceholder":165},[129,2287,2288],{"class":131,"line":156},[129,2289,1227],{},[129,2291,2292],{"class":131,"line":162},[129,2293,2294],{},"pub struct LoginUserSchema {\n",[129,2296,2297],{"class":131,"line":169},[129,2298,2299],{},"    #[schema(example = \"user@example.com\")]\n",[129,2301,2302],{"class":131,"line":175},[129,2303,1256],{},[129,2305,2306],{"class":131,"line":181},[129,2307,166],{"emptyLinePlaceholder":165},[129,2309,2310],{"class":131,"line":187},[129,2311,2312],{},"    #[schema(example = \"password123\")]\n",[129,2314,2315],{"class":131,"line":192},[129,2316,1325],{},[129,2318,2319],{"class":131,"line":198},[129,2320,555],{},[129,2322,2323],{"class":131,"line":204},[129,2324,166],{"emptyLinePlaceholder":165},[129,2326,2327],{"class":131,"line":210},[129,2328,1227],{},[129,2330,2331],{"class":131,"line":215},[129,2332,1384],{},[129,2334,2335],{"class":131,"line":221},[129,2336,1389],{},[129,2338,2339],{"class":131,"line":227},[129,2340,555],{},[129,2342,2343],{"class":131,"line":453},[129,2344,166],{"emptyLinePlaceholder":165},[129,2346,2347],{"class":131,"line":458},[129,2348,1227],{},[129,2350,2351],{"class":131,"line":464},[129,2352,1406],{},[129,2354,2355],{"class":131,"line":469},[129,2356,1411],{},[129,2358,2359],{"class":131,"line":475},[129,2360,1416],{},[129,2362,2363],{"class":131,"line":481},[129,2364,1425],{},[129,2366,2367],{"class":131,"line":487},[129,2368,555],{},[107,2370,2372],{"id":2371},"_2-define-security-schemes","2. Define Security Schemes",[120,2374,2376],{"className":307,"code":2375,"language":309,"meta":125,"style":125},"\u002F\u002F src\u002Fdocs\u002Fsecurity.rs\nuse utoipa::{Modify, OpenApi};\nuse utoipa::openapi::security::{SecurityScheme, HttpAuthScheme, Http};\n\npub struct JwtSecurityAddon;\n\nimpl Modify for JwtSecurityAddon {\n    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {\n        if let Some(components) = openapi.components.as_mut() {\n            components.add_security_scheme(\n                \"bearerAuth\",\n                SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer))\n            );\n        }\n    }\n}\n",[115,2377,2378,2382,2386,2391,2395,2399,2403,2407,2411,2415,2419,2423,2428,2432,2436,2440],{"__ignoreMap":125},[129,2379,2380],{"class":131,"line":132},[129,2381,372],{},[129,2383,2384],{"class":131,"line":138},[129,2385,377],{},[129,2387,2388],{"class":131,"line":144},[129,2389,2390],{},"use utoipa::openapi::security::{SecurityScheme, HttpAuthScheme, Http};\n",[129,2392,2393],{"class":131,"line":150},[129,2394,166],{"emptyLinePlaceholder":165},[129,2396,2397],{"class":131,"line":156},[129,2398,396],{},[129,2400,2401],{"class":131,"line":162},[129,2402,166],{"emptyLinePlaceholder":165},[129,2404,2405],{"class":131,"line":169},[129,2406,405],{},[129,2408,2409],{"class":131,"line":175},[129,2410,410],{},[129,2412,2413],{"class":131,"line":181},[129,2414,415],{},[129,2416,2417],{"class":131,"line":187},[129,2418,425],{},[129,2420,2421],{"class":131,"line":192},[129,2422,430],{},[129,2424,2425],{"class":131,"line":198},[129,2426,2427],{},"                SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer))\n",[129,2429,2430],{"class":131,"line":204},[129,2431,450],{},[129,2433,2434],{"class":131,"line":210},[129,2435,543],{},[129,2437,2438],{"class":131,"line":215},[129,2439,549],{},[129,2441,2442],{"class":131,"line":221},[129,2443,555],{},[107,2445,2447],{"id":2446},"_3-implement-api-handlers","3. Implement API Handlers",[120,2449,2451],{"className":307,"code":2450,"language":309,"meta":125,"style":125},"\u002F\u002F src\u002Fhandler.rs\nuse crate::docs::schemas::*;\nuse utoipa::path;\n\n#[utoipa::path(\n    post,\n    path = \"\u002Fapi\u002Fauth\u002Flogin\",\n    tag = \"Authentication\",\n    request_body = LoginUserSchema,\n    responses(\n        (status = 200, description = \"Login successful\", body = LoginResponse),\n        (status = 401, description = \"Authentication failed\", body = ErrorResponse)\n    )\n)]\npub async fn login_user_handler(\n    State(data): State\u003CAppState>,\n    Json(body): Json\u003CLoginUserSchema>,\n) -> Result\u003Cimpl IntoResponse, (StatusCode, Json\u003Cserde_json::Value>)> {\n    \u002F\u002F Implement login logic\n}\n",[115,2452,2453,2458,2463,2467,2471,2475,2479,2483,2487,2492,2496,2500,2504,2508,2512,2517,2521,2526,2530,2535],{"__ignoreMap":125},[129,2454,2455],{"class":131,"line":132},[129,2456,2457],{},"\u002F\u002F src\u002Fhandler.rs\n",[129,2459,2460],{"class":131,"line":138},[129,2461,2462],{},"use crate::docs::schemas::*;\n",[129,2464,2465],{"class":131,"line":144},[129,2466,683],{},[129,2468,2469],{"class":131,"line":150},[129,2470,166],{"emptyLinePlaceholder":165},[129,2472,2473],{"class":131,"line":156},[129,2474,692],{},[129,2476,2477],{"class":131,"line":162},[129,2478,702],{},[129,2480,2481],{"class":131,"line":169},[129,2482,707],{},[129,2484,2485],{"class":131,"line":175},[129,2486,721],{},[129,2488,2489],{"class":131,"line":181},[129,2490,2491],{},"    request_body = LoginUserSchema,\n",[129,2493,2494],{"class":131,"line":187},[129,2495,778],{},[129,2497,2498],{"class":131,"line":192},[129,2499,783],{},[129,2501,2502],{"class":131,"line":198},[129,2503,788],{},[129,2505,2506],{"class":131,"line":204},[129,2507,341],{},[129,2509,2510],{"class":131,"line":210},[129,2511,346],{},[129,2513,2514],{"class":131,"line":215},[129,2515,2516],{},"pub async fn login_user_handler(\n",[129,2518,2519],{"class":131,"line":221},[129,2520,893],{},[129,2522,2523],{"class":131,"line":227},[129,2524,2525],{},"    Json(body): Json\u003CLoginUserSchema>,\n",[129,2527,2528],{"class":131,"line":453},[129,2529,903],{},[129,2531,2532],{"class":131,"line":458},[129,2533,2534],{},"    \u002F\u002F Implement login logic\n",[129,2536,2537],{"class":131,"line":464},[129,2538,555],{},[107,2540,2542],{"id":2541},"_4-generate-openapi-documentation","4. Generate OpenAPI Documentation",[120,2544,2546],{"className":307,"code":2545,"language":309,"meta":125,"style":125},"\u002F\u002F src\u002Fmain.rs\nuse acrospire_auth::{\n    docs::JwtSecurityAddon,\n    docs::schemas::*,\n    handler::*,\n};\nuse utoipa::OpenApi;\nuse utoipa_swagger_ui::SwaggerUi;\n\n#[derive(OpenApi)]\n#[openapi(\n    paths(\n        health_checker_handler,\n        register_user_handler,\n        login_user_handler,\n    ),\n    components(\n        schemas(\n            HealthCheckResponse,\n            LoginUserSchema,\n            LoginResponse,\n        )\n    ),\n    tags(\n        (name = \"System\", description = \"System health check endpoints\"),\n        (name = \"Authentication\", description = \"User registration and login endpoints\"),\n    ),\n    modifiers(&JwtSecurityAddon),\n    info(\n        title = \"My API\",\n        version = \"1.0.0\"\n    )\n)]\nstruct ApiDoc;\n\n#[tokio::main]\nasync fn main() -> Result\u003C(), Box\u003Cdyn std::error::Error>> {\n    let openapi = ApiDoc::openapi();\n\n    let app = Router::new()\n        \u002F\u002F ... add routes\n        .merge(SwaggerUi::new(\"\u002Fswagger-ui\")\n            .url(\"\u002Fapi-docs\u002Fopenapi.json\", openapi));\n\n    \u002F\u002F Start server...\n}\n",[115,2547,2548,2552,2556,2560,2564,2568,2572,2576,2580,2584,2588,2592,2596,2600,2604,2608,2612,2616,2620,2624,2628,2632,2636,2640,2644,2649,2654,2658,2663,2667,2672,2677,2681,2685,2689,2693,2697,2701,2705,2709,2714,2719,2724,2729,2733,2738],{"__ignoreMap":125},[129,2549,2550],{"class":131,"line":132},[129,2551,571],{},[129,2553,2554],{"class":131,"line":138},[129,2555,1577],{},[129,2557,2558],{"class":131,"line":144},[129,2559,1582],{},[129,2561,2562],{"class":131,"line":150},[129,2563,1587],{},[129,2565,2566],{"class":131,"line":156},[129,2567,1592],{},[129,2569,2570],{"class":131,"line":162},[129,2571,1597],{},[129,2573,2574],{"class":131,"line":169},[129,2575,1602],{},[129,2577,2578],{"class":131,"line":175},[129,2579,1990],{},[129,2581,2582],{"class":131,"line":181},[129,2583,166],{"emptyLinePlaceholder":165},[129,2585,2586],{"class":131,"line":187},[129,2587,321],{},[129,2589,2590],{"class":131,"line":192},[129,2591,326],{},[129,2593,2594],{"class":131,"line":198},[129,2595,1624],{},[129,2597,2598],{"class":131,"line":204},[129,2599,1629],{},[129,2601,2602],{"class":131,"line":210},[129,2603,1634],{},[129,2605,2606],{"class":131,"line":215},[129,2607,1639],{},[129,2609,2610],{"class":131,"line":221},[129,2611,764],{},[129,2613,2614],{"class":131,"line":227},[129,2615,1697],{},[129,2617,2618],{"class":131,"line":453},[129,2619,1702],{},[129,2621,2622],{"class":131,"line":458},[129,2623,1707],{},[129,2625,2626],{"class":131,"line":464},[129,2627,1717],{},[129,2629,2630],{"class":131,"line":469},[129,2631,1732],{},[129,2633,2634],{"class":131,"line":475},[129,2635,1831],{},[129,2637,2638],{"class":131,"line":481},[129,2639,764],{},[129,2641,2642],{"class":131,"line":487},[129,2643,1853],{},[129,2645,2646],{"class":131,"line":492},[129,2647,2648],{},"        (name = \"System\", description = \"System health check endpoints\"),\n",[129,2650,2651],{"class":131,"line":497},[129,2652,2653],{},"        (name = \"Authentication\", description = \"User registration and login endpoints\"),\n",[129,2655,2656],{"class":131,"line":502},[129,2657,764],{},[129,2659,2660],{"class":131,"line":508},[129,2661,2662],{},"    modifiers(&JwtSecurityAddon),\n",[129,2664,2665],{"class":131,"line":513},[129,2666,1905],{},[129,2668,2669],{"class":131,"line":519},[129,2670,2671],{},"        title = \"My API\",\n",[129,2673,2674],{"class":131,"line":524},[129,2675,2676],{},"        version = \"1.0.0\"\n",[129,2678,2679],{"class":131,"line":530},[129,2680,341],{},[129,2682,2683],{"class":131,"line":535},[129,2684,346],{},[129,2686,2687],{"class":131,"line":540},[129,2688,351],{},[129,2690,2691],{"class":131,"line":546},[129,2692,166],{"emptyLinePlaceholder":165},[129,2694,2695],{"class":131,"line":552},[129,2696,1999],{},[129,2698,2699],{"class":131,"line":1750},[129,2700,2004],{},[129,2702,2703],{"class":131,"line":1756},[129,2704,2061],{},[129,2706,2707],{"class":131,"line":1762},[129,2708,166],{"emptyLinePlaceholder":165},[129,2710,2711],{"class":131,"line":1768},[129,2712,2713],{},"    let app = Router::new()\n",[129,2715,2716],{"class":131,"line":1774},[129,2717,2718],{},"        \u002F\u002F ... add routes\n",[129,2720,2721],{"class":131,"line":1780},[129,2722,2723],{},"        .merge(SwaggerUi::new(\"\u002Fswagger-ui\")\n",[129,2725,2726],{"class":131,"line":1786},[129,2727,2728],{},"            .url(\"\u002Fapi-docs\u002Fopenapi.json\", openapi));\n",[129,2730,2731],{"class":131,"line":1792},[129,2732,166],{"emptyLinePlaceholder":165},[129,2734,2735],{"class":131,"line":1798},[129,2736,2737],{},"    \u002F\u002F Start server...\n",[129,2739,2740],{"class":131,"line":1804},[129,2741,555],{},[107,2743,2745],{"id":2744},"_5-using-swagger-ui","5. Using Swagger UI",[2747,2748,2749,2755,2758,2761,2764,2767],"ol",{},[71,2750,2751,2752],{},"Visit ",[2242,2753,2244],{"href":2244,"rel":2754},[2246],[71,2756,2757],{},"Click 🔓 \"Authorize\" button",[71,2759,2760],{},"Select authentication method (bearerAuth\u002FcookieAuth\u002FapiKeyAuth)",[71,2762,2763],{},"Enter authentication credentials",[71,2765,2766],{},"Click \"Authorize\" → \"Close\"",[71,2768,2769],{},"Use \"Try it out\" to test APIs",[60,2771,2773],{"id":2772},"common-issues-and-solutions","Common Issues and Solutions",[107,2775,2777],{"id":2776},"issue-1-authorize-button-not-showing","Issue 1: Authorize Button Not Showing",[56,2779,2780,118],{},[74,2781,2782],{},"Cause",[2747,2784,2785,2791,2802],{},[71,2786,2787,2788],{},"No security schemes added via ",[115,2789,2790],{},"modifiers(&SecurityAddon)",[71,2792,2793,2794,2797,2798,2801],{},"Incorrect ",[115,2795,2796],{},"SecurityAddon"," ",[115,2799,2800],{},"modify"," method implementation",[71,2803,2804,2806,2807],{},[115,2805,2796],{}," not referenced in ",[115,2808,561],{},[56,2810,2811,118],{},[74,2812,2813],{},"Solution",[120,2815,2817],{"className":307,"code":2816,"language":309,"meta":125,"style":125},"\u002F\u002F Ensure Modify trait is correctly implemented\nimpl Modify for JwtSecurityAddon {\n    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {\n        if let Some(components) = openapi.components.as_mut() {\n            components.add_security_scheme(...);\n        }\n    }\n}\n\n\u002F\u002F Use in ApiDoc\n#[openapi(modifiers(&JwtSecurityAddon))]\nstruct ApiDoc;\n",[115,2818,2819,2824,2828,2832,2836,2841,2845,2849,2853,2857,2862,2867],{"__ignoreMap":125},[129,2820,2821],{"class":131,"line":132},[129,2822,2823],{},"\u002F\u002F Ensure Modify trait is correctly implemented\n",[129,2825,2826],{"class":131,"line":138},[129,2827,405],{},[129,2829,2830],{"class":131,"line":144},[129,2831,410],{},[129,2833,2834],{"class":131,"line":150},[129,2835,415],{},[129,2837,2838],{"class":131,"line":156},[129,2839,2840],{},"            components.add_security_scheme(...);\n",[129,2842,2843],{"class":131,"line":162},[129,2844,543],{},[129,2846,2847],{"class":131,"line":169},[129,2848,549],{},[129,2850,2851],{"class":131,"line":175},[129,2852,555],{},[129,2854,2855],{"class":131,"line":181},[129,2856,166],{"emptyLinePlaceholder":165},[129,2858,2859],{"class":131,"line":187},[129,2860,2861],{},"\u002F\u002F Use in ApiDoc\n",[129,2863,2864],{"class":131,"line":192},[129,2865,2866],{},"#[openapi(modifiers(&JwtSecurityAddon))]\n",[129,2868,2869],{"class":131,"line":198},[129,2870,351],{},[107,2872,2874],{"id":2873},"issue-2-empty-paths-field","Issue 2: Empty Paths Field",[56,2876,2877,118],{},[74,2878,2782],{},[2747,2880,2881,2890,2896],{},[71,2882,2883,2884,2886,2887],{},"Handlers not listed in ",[115,2885,561],{},"'s ",[115,2888,2889],{},"paths(...)",[71,2891,2892,2893],{},"Handler functions not marked with ",[115,2894,2895],{},"#[utoipa::path]",[71,2897,2898],{},"Incorrect path references (function name mismatch)",[56,2900,2901,118],{},[74,2902,2813],{},[120,2904,2906],{"className":307,"code":2905,"language":309,"meta":125,"style":125},"#[openapi(\n    paths(\n        handler::function1,   \u002F\u002F ✅ Correct: List all handlers\n        handler::function2,\n    )\n)]\nstruct ApiDoc;\n",[115,2907,2908,2912,2916,2921,2926,2930,2934],{"__ignoreMap":125},[129,2909,2910],{"class":131,"line":132},[129,2911,326],{},[129,2913,2914],{"class":131,"line":138},[129,2915,1624],{},[129,2917,2918],{"class":131,"line":144},[129,2919,2920],{},"        handler::function1,   \u002F\u002F ✅ Correct: List all handlers\n",[129,2922,2923],{"class":131,"line":150},[129,2924,2925],{},"        handler::function2,\n",[129,2927,2928],{"class":131,"line":156},[129,2929,341],{},[129,2931,2932],{"class":131,"line":162},[129,2933,346],{},[129,2935,2936],{"class":131,"line":169},[129,2937,351],{},[107,2939,2941],{"id":2940},"issue-3-datetime-type-not-supported","Issue 3: DateTime Type Not Supported",[56,2943,2944,118],{},[74,2945,2946],{},"Problem",[120,2948,2950],{"className":307,"code":2949,"language":309,"meta":125,"style":125},"#[derive(ToSchema)]\nstruct MyStruct {\n    pub created_at: DateTime\u003CUtc>,  \u002F\u002F ❌ Might error\n}\n",[115,2951,2952,2957,2962,2967],{"__ignoreMap":125},[129,2953,2954],{"class":131,"line":132},[129,2955,2956],{},"#[derive(ToSchema)]\n",[129,2958,2959],{"class":131,"line":138},[129,2960,2961],{},"struct MyStruct {\n",[129,2963,2964],{"class":131,"line":144},[129,2965,2966],{},"    pub created_at: DateTime\u003CUtc>,  \u002F\u002F ❌ Might error\n",[129,2968,2969],{"class":131,"line":150},[129,2970,555],{},[56,2972,2973,118],{},[74,2974,2813],{},[120,2976,2978],{"className":307,"code":2977,"language":309,"meta":125,"style":125},"#[derive(ToSchema)]\nstruct MyStruct {\n    #[schema(value_type = String, format = \"date-time\")]\n    pub created_at: DateTime\u003CUtc>,  \u002F\u002F ✅ Use String type\n}\n",[115,2979,2980,2984,2988,2992,2997],{"__ignoreMap":125},[129,2981,2982],{"class":131,"line":132},[129,2983,2956],{},[129,2985,2986],{"class":131,"line":138},[129,2987,2961],{},[129,2989,2990],{"class":131,"line":144},[129,2991,1487],{},[129,2993,2994],{"class":131,"line":150},[129,2995,2996],{},"    pub created_at: DateTime\u003CUtc>,  \u002F\u002F ✅ Use String type\n",[129,2998,2999],{"class":131,"line":156},[129,3000,555],{},[107,3002,3004,3005],{"id":3003},"issue-4-route-conflict-overlapping-method-route","Issue 4: Route Conflict ",[115,3006,3007],{},"Overlapping method route",[56,3009,3010,118],{},[74,3011,3012],{},"Error Message",[120,3014,3017],{"className":3015,"code":3016,"language":274},[272],"Overlapping method route. Handler for GET \u002Fapi-docs\u002Fopenapi.json already exists\n",[115,3018,3016],{"__ignoreMap":125},[56,3020,3021,3023,3024,3027],{},[74,3022,2782],{},":\nAdded ",[115,3025,3026],{},"SwaggerUi"," in multiple places, causing duplicate routes",[56,3029,3030,3032,3033,665],{},[74,3031,2813],{},":\nOnly add Swagger UI in one place (recommend ",[115,3034,3035],{},"main.rs",[120,3037,3039],{"className":307,"code":3038,"language":309,"meta":125,"style":125},"\u002F\u002F ❌ Wrong: Added in both route.rs and main.rs\n\u002F\u002F route.rs\nlet app = Router::new().merge(swagger_ui)\n\n\u002F\u002F main.rs\nlet app = create_router().merge(swagger_ui)  \u002F\u002F Conflict!\n\n\u002F\u002F ✅ Correct: Only in main.rs\n\u002F\u002F route.rs - Don't add swagger_ui\n\u002F\u002F main.rs - Add it here\nlet app = create_router().merge(swagger_ui)\n",[115,3040,3041,3046,3051,3056,3060,3065,3070,3074,3079,3084,3089],{"__ignoreMap":125},[129,3042,3043],{"class":131,"line":132},[129,3044,3045],{},"\u002F\u002F ❌ Wrong: Added in both route.rs and main.rs\n",[129,3047,3048],{"class":131,"line":138},[129,3049,3050],{},"\u002F\u002F route.rs\n",[129,3052,3053],{"class":131,"line":144},[129,3054,3055],{},"let app = Router::new().merge(swagger_ui)\n",[129,3057,3058],{"class":131,"line":150},[129,3059,166],{"emptyLinePlaceholder":165},[129,3061,3062],{"class":131,"line":156},[129,3063,3064],{},"\u002F\u002F main.rs\n",[129,3066,3067],{"class":131,"line":162},[129,3068,3069],{},"let app = create_router().merge(swagger_ui)  \u002F\u002F Conflict!\n",[129,3071,3072],{"class":131,"line":169},[129,3073,166],{"emptyLinePlaceholder":165},[129,3075,3076],{"class":131,"line":175},[129,3077,3078],{},"\u002F\u002F ✅ Correct: Only in main.rs\n",[129,3080,3081],{"class":131,"line":181},[129,3082,3083],{},"\u002F\u002F route.rs - Don't add swagger_ui\n",[129,3085,3086],{"class":131,"line":187},[129,3087,3088],{},"\u002F\u002F main.rs - Add it here\n",[129,3090,3091],{"class":131,"line":192},[129,3092,3093],{},"let app = create_router().merge(swagger_ui)\n",[107,3095,3097],{"id":3096},"issue-5-supporting-multiple-authentication-methods","Issue 5: Supporting Multiple Authentication Methods",[120,3099,3101],{"className":307,"code":3100,"language":309,"meta":125,"style":125},"\u002F\u002F Declare multiple authentication methods in handler\n#[utoipa::path(\n    get,\n    path = \"\u002Fapi\u002Fusers\u002Fme\",\n    security(\n        (\"bearerAuth\" = []),   \u002F\u002F Method 1\n        (\"cookieAuth\" = [])     \u002F\u002F Method 2\n    )\n)]\npub async fn get_me() {\n    \u002F\u002F Implementation...\n}\n",[115,3102,3103,3108,3112,3116,3120,3124,3129,3134,3138,3142,3147,3151],{"__ignoreMap":125},[129,3104,3105],{"class":131,"line":132},[129,3106,3107],{},"\u002F\u002F Declare multiple authentication methods in handler\n",[129,3109,3110],{"class":131,"line":138},[129,3111,692],{},[129,3113,3114],{"class":131,"line":144},[129,3115,940],{},[129,3117,3118],{"class":131,"line":150},[129,3119,945],{},[129,3121,3122],{"class":131,"line":156},[129,3123,749],{},[129,3125,3126],{"class":131,"line":162},[129,3127,3128],{},"        (\"bearerAuth\" = []),   \u002F\u002F Method 1\n",[129,3130,3131],{"class":131,"line":169},[129,3132,3133],{},"        (\"cookieAuth\" = [])     \u002F\u002F Method 2\n",[129,3135,3136],{"class":131,"line":175},[129,3137,341],{},[129,3139,3140],{"class":131,"line":181},[129,3141,346],{},[129,3143,3144],{"class":131,"line":187},[129,3145,3146],{},"pub async fn get_me() {\n",[129,3148,3149],{"class":131,"line":192},[129,3150,908],{},[129,3152,3153],{"class":131,"line":198},[129,3154,555],{},[56,3156,3157],{},"Users can choose any authentication method in Swagger UI.",[60,3159,3161],{"id":3160},"best-practices","Best Practices",[107,3163,3165],{"id":3164},"_1-organize-code-in-layers","1. Organize Code in Layers",[120,3167,3169],{"className":307,"code":3168,"language":309,"meta":125,"style":125},"docs\u002F\n├── mod.rs          \u002F\u002F Export interfaces\n├── security.rs     \u002F\u002F Security schemes\n├── schemas.rs      \u002F\u002F Data models\n└── openapi.rs      \u002F\u002F Helper functions\n",[115,3170,3171,3176,3181,3186,3191],{"__ignoreMap":125},[129,3172,3173],{"class":131,"line":132},[129,3174,3175],{},"docs\u002F\n",[129,3177,3178],{"class":131,"line":138},[129,3179,3180],{},"├── mod.rs          \u002F\u002F Export interfaces\n",[129,3182,3183],{"class":131,"line":144},[129,3184,3185],{},"├── security.rs     \u002F\u002F Security schemes\n",[129,3187,3188],{"class":131,"line":150},[129,3189,3190],{},"├── schemas.rs      \u002F\u002F Data models\n",[129,3192,3193],{"class":131,"line":156},[129,3194,3195],{},"└── openapi.rs      \u002F\u002F Helper functions\n",[107,3197,3199],{"id":3198},"_2-use-full-type-paths","2. Use Full Type Paths",[120,3201,3203],{"className":307,"code":3202,"language":309,"meta":125,"style":125},"#[openapi(\n    paths(\n        acrospire_auth::handler::login_handler,  \u002F\u002F ✅ Full path\n        \u002F\u002F login_handler,  \u002F\u002F ❌ Errors if not in scope\n    )\n)]\n",[115,3204,3205,3209,3213,3218,3226,3230],{"__ignoreMap":125},[129,3206,3207],{"class":131,"line":132},[129,3208,326],{},[129,3210,3211],{"class":131,"line":138},[129,3212,1624],{},[129,3214,3215],{"class":131,"line":144},[129,3216,3217],{},"        acrospire_auth::handler::login_handler,  \u002F\u002F ✅ Full path\n",[129,3219,3220,3223],{"class":131,"line":150},[129,3221,3222],{},"        \u002F\u002F login_handler,",[129,3224,3225],{},"  \u002F\u002F ❌ Errors if not in scope\n",[129,3227,3228],{"class":131,"line":156},[129,3229,341],{},[129,3231,3232],{"class":131,"line":162},[129,3233,346],{},[107,3235,3237],{"id":3236},"_3-add-examples-for-all-schemas","3. Add Examples for All Schemas",[120,3239,3241],{"className":307,"code":3240,"language":309,"meta":125,"style":125},"#[derive(ToSchema)]\npub struct CreateUserSchema {\n    #[schema(example = \"user@example.com\")]\n    pub email: String,\n\n    #[schema(example = \"StrongPass123!\")]\n    pub password: String,\n}\n",[115,3242,3243,3247,3252,3256,3260,3264,3269,3273],{"__ignoreMap":125},[129,3244,3245],{"class":131,"line":132},[129,3246,2956],{},[129,3248,3249],{"class":131,"line":138},[129,3250,3251],{},"pub struct CreateUserSchema {\n",[129,3253,3254],{"class":131,"line":144},[129,3255,2299],{},[129,3257,3258],{"class":131,"line":150},[129,3259,1256],{},[129,3261,3262],{"class":131,"line":156},[129,3263,166],{"emptyLinePlaceholder":165},[129,3265,3266],{"class":131,"line":162},[129,3267,3268],{},"    #[schema(example = \"StrongPass123!\")]\n",[129,3270,3271],{"class":131,"line":169},[129,3272,1325],{},[129,3274,3275],{"class":131,"line":175},[129,3276,555],{},[107,3278,3280],{"id":3279},"_4-use-descriptive-text","4. Use Descriptive Text",[120,3282,3284],{"className":307,"code":3283,"language":309,"meta":125,"style":125},"#[utoipa::path(\n    post,\n    path = \"\u002Fapi\u002Fauth\u002Flogin\",\n    tag = \"Authentication\",\n    summary = \"User login\",           \u002F\u002F ← Add summary\n    description = \"Login to the system using email and password, returns JWT Token on success\",  \u002F\u002F ← Detailed description\n    responses(...)\n)]\n",[115,3285,3286,3290,3294,3298,3302,3307,3312,3317],{"__ignoreMap":125},[129,3287,3288],{"class":131,"line":132},[129,3289,692],{},[129,3291,3292],{"class":131,"line":138},[129,3293,702],{},[129,3295,3296],{"class":131,"line":144},[129,3297,707],{},[129,3299,3300],{"class":131,"line":150},[129,3301,721],{},[129,3303,3304],{"class":131,"line":156},[129,3305,3306],{},"    summary = \"User login\",           \u002F\u002F ← Add summary\n",[129,3308,3309],{"class":131,"line":162},[129,3310,3311],{},"    description = \"Login to the system using email and password, returns JWT Token on success\",  \u002F\u002F ← Detailed description\n",[129,3313,3314],{"class":131,"line":169},[129,3315,3316],{},"    responses(...)\n",[129,3318,3319],{"class":131,"line":175},[129,3320,346],{},[107,3322,3324],{"id":3323},"_5-standardize-error-response-format","5. Standardize Error Response Format",[120,3326,3328],{"className":307,"code":3327,"language":309,"meta":125,"style":125},"#[derive(ToSchema)]\npub struct ErrorResponse {\n    pub status: String,\n\n    #[schema(example = \"Operation failed\")]\n    pub message: String,\n}\n\n\u002F\u002F Reuse in all handlers\n#[utoipa::path(\n    responses(\n        (status = 200, description = \"Success\"),\n        (status = 400, description = \"Parameter error\", body = ErrorResponse),  \u002F\u002F Reuse\n        (status = 500, description = \"Server error\", body = ErrorResponse),  \u002F\u002F Reuse\n    )\n)]\n",[115,3329,3330,3334,3339,3344,3348,3353,3358,3362,3366,3371,3375,3379,3384,3389,3394,3398],{"__ignoreMap":125},[129,3331,3332],{"class":131,"line":132},[129,3333,2956],{},[129,3335,3336],{"class":131,"line":138},[129,3337,3338],{},"pub struct ErrorResponse {\n",[129,3340,3341],{"class":131,"line":144},[129,3342,3343],{},"    pub status: String,\n",[129,3345,3346],{"class":131,"line":150},[129,3347,166],{"emptyLinePlaceholder":165},[129,3349,3350],{"class":131,"line":156},[129,3351,3352],{},"    #[schema(example = \"Operation failed\")]\n",[129,3354,3355],{"class":131,"line":162},[129,3356,3357],{},"    pub message: String,\n",[129,3359,3360],{"class":131,"line":169},[129,3361,555],{},[129,3363,3364],{"class":131,"line":175},[129,3365,166],{"emptyLinePlaceholder":165},[129,3367,3368],{"class":131,"line":181},[129,3369,3370],{},"\u002F\u002F Reuse in all handlers\n",[129,3372,3373],{"class":131,"line":187},[129,3374,692],{},[129,3376,3377],{"class":131,"line":192},[129,3378,778],{},[129,3380,3381],{"class":131,"line":198},[129,3382,3383],{},"        (status = 200, description = \"Success\"),\n",[129,3385,3386],{"class":131,"line":204},[129,3387,3388],{},"        (status = 400, description = \"Parameter error\", body = ErrorResponse),  \u002F\u002F Reuse\n",[129,3390,3391],{"class":131,"line":210},[129,3392,3393],{},"        (status = 500, description = \"Server error\", body = ErrorResponse),  \u002F\u002F Reuse\n",[129,3395,3396],{"class":131,"line":215},[129,3397,341],{},[129,3399,3400],{"class":131,"line":221},[129,3401,346],{},[60,3403,3405],{"id":3404},"testing-apis-with-swagger-ui","Testing APIs with Swagger UI",[2747,3407,3408,3419,3441],{},[71,3409,3410,118,3413],{},[74,3411,3412],{},"Login to Get Token",[120,3414,3417],{"className":3415,"code":3416,"language":274},[272],"POST \u002Fapi\u002Fauth\u002Flogin\nBody: { \"email\": \"user@example.com\", \"password\": \"password123\" }\n",[115,3418,3416],{"__ignoreMap":125},[71,3420,3421,118,3424],{},[74,3422,3423],{},"Configure Authentication",[68,3425,3426,3429,3435,3438],{},[71,3427,3428],{},"Click \"Authorize\" button",[71,3430,3431,3432],{},"Select ",[115,3433,3434],{},"bearerAuth",[71,3436,3437],{},"Enter returned Token",[71,3439,3440],{},"Click \"Authorize\"",[71,3442,3443,118,3446],{},[74,3444,3445],{},"Test Protected Endpoints",[68,3447,3448,3451,3454,3457],{},[71,3449,3450],{},"Click on protected endpoint",[71,3452,3453],{},"Click \"Try it out\"",[71,3455,3456],{},"Click \"Execute\"",[71,3458,3459],{},"Swagger UI automatically adds Authorization header",[60,3461,3463],{"id":3462},"conclusion","Conclusion",[56,3465,3466],{},"Integrating utoipa with Axum gives you a powerful, type-safe API documentation system. The key steps are:",[2747,3468,3469,3480,3493,3505,3513,3519],{},[71,3470,3471,3474,3475,247,3477,3479],{},[74,3472,3473],{},"Configure Dependencies"," - Correct ",[115,3476,261],{},[115,3478,257],{}," versions",[71,3481,3482,3485,3486,3489,3490],{},[74,3483,3484],{},"Create Modify Implementation"," - Implement ",[115,3487,3488],{},"JwtSecurityAddon"," in ",[115,3491,3492],{},"security.rs",[71,3494,3495,3498,3499,247,3502],{},[74,3496,3497],{},"Mark Handlers"," - Use ",[115,3500,3501],{},"#[utoipa::path(...)]",[115,3503,3504],{},"security()",[71,3506,3507,3498,3510],{},[74,3508,3509],{},"Mark Schemas",[115,3511,3512],{},"#[derive(ToSchema)]",[71,3514,3515,3518],{},[74,3516,3517],{},"Define ApiDoc"," - Register paths, schemas, tags, and modifiers",[71,3520,3521,3524,3525],{},[74,3522,3523],{},"Integrate Swagger UI"," - Mount documentation using ",[115,3526,3527],{},"SwaggerUi::new()",[56,3529,3530],{},"Following this guide, you'll have beautiful, interactive API documentation with full authentication support in no time. Happy coding!",[60,3532,3534],{"id":3533},"resources","Resources",[68,3536,3537,3544,3551,3558],{},[71,3538,3539],{},[2242,3540,3543],{"href":3541,"rel":3542},"https:\u002F\u002Fdocs.rs\u002Futoipa\u002Flatest\u002Futoipa\u002F",[2246],"Utoipa Official Documentation",[71,3545,3546],{},[2242,3547,3550],{"href":3548,"rel":3549},"https:\u002F\u002Fgithub.com\u002Fjuhaku\u002Futoipa",[2246],"Utoipa GitHub",[71,3552,3553],{},[2242,3554,3557],{"href":3555,"rel":3556},"https:\u002F\u002Fswagger.io\u002Fspecification\u002F",[2246],"OpenAPI 3.0 Specification",[71,3559,3560],{},[2242,3561,2239],{"href":3562,"rel":3563},"https:\u002F\u002Fswagger.io\u002Ftools\u002Fswagger-ui\u002F",[2246],[3565,3566,3567],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":125,"searchDepth":138,"depth":138,"links":3569},[3570,3571,3575,3576,3581,3588,3594,3597,3602,3609,3617,3624,3625,3626],{"id":62,"depth":138,"text":63},{"id":104,"depth":138,"text":105,"children":3572},[3573,3574],{"id":109,"depth":144,"text":110},{"id":264,"depth":144,"text":265},{"id":279,"depth":138,"text":280},{"id":292,"depth":138,"text":293,"children":3577},[3578,3579,3580],{"id":296,"depth":144,"text":297},{"id":354,"depth":144,"text":355},{"id":604,"depth":144,"text":605},{"id":668,"depth":138,"text":669,"children":3582},[3583,3584,3585,3586,3587],{"id":672,"depth":144,"text":673},{"id":823,"depth":144,"text":824},{"id":915,"depth":144,"text":916},{"id":1015,"depth":144,"text":1016},{"id":1115,"depth":144,"text":1116},{"id":1198,"depth":138,"text":1199,"children":3589},[3590,3591,3592,3593],{"id":1202,"depth":144,"text":1203},{"id":1277,"depth":144,"text":1278},{"id":1369,"depth":144,"text":1370},{"id":1432,"depth":144,"text":1433},{"id":1562,"depth":138,"text":1563,"children":3595},[3596],{"id":1566,"depth":144,"text":1567},{"id":1975,"depth":138,"text":1976,"children":3598},[3599,3600,3601],{"id":1979,"depth":144,"text":1980},{"id":2160,"depth":144,"text":2161},{"id":2228,"depth":144,"text":2229},{"id":2258,"depth":138,"text":2259,"children":3603},[3604,3605,3606,3607,3608],{"id":2262,"depth":144,"text":2263},{"id":2371,"depth":144,"text":2372},{"id":2446,"depth":144,"text":2447},{"id":2541,"depth":144,"text":2542},{"id":2744,"depth":144,"text":2745},{"id":2772,"depth":138,"text":2773,"children":3610},[3611,3612,3613,3614,3616],{"id":2776,"depth":144,"text":2777},{"id":2873,"depth":144,"text":2874},{"id":2940,"depth":144,"text":2941},{"id":3003,"depth":144,"text":3615},"Issue 4: Route Conflict Overlapping method route",{"id":3096,"depth":144,"text":3097},{"id":3160,"depth":138,"text":3161,"children":3618},[3619,3620,3621,3622,3623],{"id":3164,"depth":144,"text":3165},{"id":3198,"depth":144,"text":3199},{"id":3236,"depth":144,"text":3237},{"id":3279,"depth":144,"text":3280},{"id":3323,"depth":144,"text":3324},{"id":3404,"depth":138,"text":3405},{"id":3462,"depth":138,"text":3463},{"id":3533,"depth":138,"text":3534},"2025-01-03","Learn how to integrate utoipa and Swagger UI in Rust Axum projects to enable OpenAPI documentation with JWT, Cookie, and API Key authentication.","md","https:\u002F\u002Fppm-sources.oss-cn-hongkong.aliyuncs.com\u002Fzj\u002Frust-.crab.jpg",{},{"title":38,"description":3628},"89-DdNesAH6hrt9aHnBYQcT7R3xEuZ4jhV0EeD_HP1U",[3635,3637],{"title":34,"path":35,"stem":36,"description":3636,"children":-1},"以 csv_editor_app 为例，说明基于 Tauri 2 + Rust + React + Vite + TypeScript 的桌面应用开发环境、编译流程与技术栈。",{"title":42,"path":43,"stem":44,"description":3638,"children":-1},"Explore how Rust's powerful cross-platform compilation capabilities enable developers to build once and deploy everywhere—from embedded systems to webAssembly.",1792686186845]