1Mojolicious::Plugin::OpUesneArPIC:oM:noGtjuroiildbieucsti:eo:duOspP:ee:nrPAllPuIDgvoi3cn(u:3m:)eOnpteantAiPoIn::Guides::OpenAPIv3(3)
2
3
4
6 Mojolicious::Plugin::OpenAPI::Guides::OpenAPIv3 - Mojolicious <3
7 OpenAPI v3
8
10 This guide will give you an introduction on how to use
11 Mojolicious::Plugin::OpenAPI with OpenAPI version v3.x.
12
14 Specification
15 This plugin reads an OpenAPI specification
16 <https://openapis.org/specification> and generates routes and
17 input/output rules from it. See JSON::Validator for supported schema
18 file formats.
19
20 {
21 "openapi": "3.0.2",
22 "info": {
23 "version": "1.0",
24 "title": "Some awesome API"
25 },
26 "paths": {
27 "/pets": {
28 "get": {
29 "operationId": "getPets",
30 "x-mojo-name": "get_pets",
31 "x-mojo-to": "pet#list",
32 "summary": "Finds pets in the system",
33 "parameters": [
34 {
35 "in": "query",
36 "name": "age",
37 "schema": {
38 "type": "integer"
39 }
40 }
41 ],
42 "requestBody": {
43 "content": {
44 "application/json": {
45 "schema": {
46 "type": "object"
47 }
48 }
49 }
50 },
51 "responses": {
52 "200": {
53 "description": "Pet response",
54 "content": {
55 "application/json": {
56 "schema": {
57 "type": "object",
58 "properties": {
59 "pets": {
60 "type": "array",
61 "items": {
62 "type": "object"
63 }
64 }
65 }
66 }
67 }
68 }
69 }
70 }
71 }
72 }
73 },
74 "servers": [
75 {
76 "url": "/api"
77 }
78 ]
79 }
80
81 The complete HTTP request for getting the "pet list" will be "GET
82 /api/pets" The first part of the path ("/api") comes from "servers",
83 the second part comes from the keys under "paths", and the HTTP method
84 comes from the keys under "/pets".
85
86 The different parts of the specification can also be retrieved as JSON
87 using the "OPTIONS" HTTP method. Example:
88
89 OPTIONS /api/pets
90 OPTIONS /api/pets?method=get
91
92 Note that the use of "OPTIONS" is EXPERIMENTAL, and subject to change.
93
94 Here are some more details about the different keys:
95
96 · openapi, info and paths
97
98 These three sections are required to make the specification valid.
99 Check out
100 <https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md>
101 for a complete reference to the specification.
102
103 · parameters, requestBody and responses
104
105 "parameters", "requestBody" and "responses" will be used to define
106 input and output validation rules, which is used by "openapi.input"
107 in Mojolicious::Plugin::OpenAPI and when rendering the response back
108 to the client, using "render(openapi => ...)".
109
110 Here OpenAPIv3 input differs from the v2 spec, where "parameters" is
111 used for input in the path or query of the request. The "requestBody"
112 is used for input passed in the body.
113
114 Have a look at "RENDERER" in Mojolicious::Plugin::OpenAPI for more
115 details about output rendering.
116
117 · operationId and x-mojo-name
118
119 See "Route names".
120
121 · x-mojo-placeholder
122
123 "x-mojo-placeholder" can be used inside a parameter definition to
124 instruct Mojolicious to parse a path part in a certain way. Example:
125
126 "parameters": [
127 {
128 "x-mojo-placeholder": "#",
129 "in": "path",
130 "name": "email",
131 "type": "string"
132 }
133 ]
134
135 See Mojolicious::Guides::Routing for more information about
136 "standard", "relaxed" and "wildcard" placeholders. The default is to
137 use the "standard" ("/:foo") placeholder.
138
139 · x-mojo-to
140
141 The non-standard part in the spec above is "x-mojo-to". The "x-mojo-
142 to" key can be either a plain string, object (hash) or an array. The
143 string and hash will be passed directly to "to" in
144 Mojolicious::Routes::Route, while the array ref will be flattened.
145 Examples:
146
147 "x-mojo-to": "pet#list"
148 $route->to("pet#list");
149
150 "x-mojo-to": {"controller": "pet", "action": "list", "foo": 123}
151 $route->to({controller => "pet", action => "list", foo => 123);
152
153 "x-mojo-to": ["pet#list", {"foo": 123}]
154 $route->to("pet#list", {foo => 123});
155
156 · security and securitySchemes
157
158 The securityScheme is added under components, where one way is to
159 have the client place an apiKey in the header of the request
160
161 {
162 ...
163 "components": {
164 "securitySchemes": {
165 "apiKey": {
166 "name": "X-Api-Key",
167 "in": "header",
168 "type": "apiKey"
169 }
170 }
171 }
172 }
173
174 It is then referenced under the path object as security like this
175
176 {
177 ...
178 "paths": {
179 "/pets": {
180 "get": {
181 "operationId": "getPets",
182 ...
183 "security": [
184 {
185 "apiKey": []
186 }
187 ]
188 }
189 }
190 }
191 }
192
193 You can then utilize security, by adding a security callback when
194 loading the plugin
195
196 $self->plugin(
197 OpenAPI => {
198 spec => $self->static->file("openapi.json")->path,
199 schema => 'v3',
200 security => {
201 apiKey => sub {
202 my ($c, $definition, $scopes, $cb) = @_;
203 if (my $key = $c->tx->req->content->headers->header('X-Api-Key')) {
204 if (got_valid_api_key()) {
205 return $c->$cb();
206 }
207 else {
208 return $c->$cb('Api Key not valid');
209 }
210 }
211 else {
212 return $c->$cb('Api Key header not present');
213 }
214 }
215 }
216 }
217 );
218
219 References with files
220
221 Only a file reference like
222
223 "$ref": "my-other-cool-component.json#/components/schemas/inputSchema"
224
225 Is supported, though a valid path must be used for both the reference
226 and in the referenced file, in order to produce a valid spec output.
227
228 See "File references" in Known Issues for unsupported file references
229
230 Application
231 package Myapp;
232 use Mojo::Base "Mojolicious";
233
234 sub startup {
235 my $app = shift;
236 $app->plugin("OpenAPI" => {url => $app->home->rel_file("myapi.json"), schema => 'v3'});
237 }
238
239 1;
240
241 The first thing in your code that you need to do is to load this plugin
242 and the "Specification". See "register" in Mojolicious::Plugin::OpenAPI
243 for information about what the plugin config can be.
244
245 See also "SYNOPSIS" in Mojolicious::Plugin::OpenAPI for example
246 Mojolicious::Lite application.
247
248 Controller
249 package Myapp::Controller::Pet;
250 use Mojo::Base "Mojolicious::Controller";
251
252 sub list {
253
254 # Do not continue on invalid input and render a default 400
255 # error document.
256 my $c = shift->openapi->valid_input or return;
257
258 # You might want to introspect the specification for the current route
259 my $spec = $c->openapi->spec;
260 unless ($spec->{'x-opening-hour'} == (localtime)[2]) {
261 return $c->render(openapi => [], status => 498);
262 }
263
264 my $age = $c->param("age");
265 my $body = $c->req->json;
266
267 # $output will be validated by the OpenAPI spec before rendered
268 my $output = {pets => [{name => "kit-e-cat"}]};
269 $c->render(openapi => $output);
270 }
271
272 1;
273
274 The input will be validated using "openapi.valid_input" in
275 Mojolicious::Plugin::OpenAPI while the output is validated through then
276 openapi handler.
277
278 Route names
279 Routes will get its name from either "x-mojo-name" or from
280 "operationId" if defined in the specification.
281
282 The route name can also be used the other way around, to find already
283 defined routes. This is especially useful for Mojolicious::Lite apps.
284
285 Note that if spec_route_name is used then all the route names will have
286 that value as prefix:
287
288 spec_route_name = "my_cool_api"
289 operationId or x-mojo-name = "Foo"
290 Route name = "my_cool_api.Foo"
291
292 You can also set "x-mojo-name" in the spec, instead of passing
293 spec_route_name to plugin():
294
295 {
296 "openapi": "3.0.2",
297 "info": { "version": "1.0", "title": "Some awesome API" },
298 "x-mojo-name": "my_cool_api"
299 }
300
301 Default response schema
302 A default response definition will be added to the API spec, unless
303 it's already defined. This schema will at least be used for invalid
304 input (400 - Bad Request) and invalid output (500 - Internal Server
305 Error), but can also be used in other cases.
306
307 See "default_response_codes" in Mojolicious::Plugin::OpenAPI and
308 "default_response_name" in Mojolicious::Plugin::OpenAPI for more
309 details on how to configure these settings.
310
311 The response schema will be added to your spec like this, unless
312 already defined:
313
314 {
315 ...
316 "components": {
317 ...
318 "schemas": {
319 ...
320 "DefaultResponse": {
321 "type": "object",
322 "required": ["errors"],
323 "properties": {
324 "errors": {
325 "type": "array",
326 "items": {
327 "type": "object",
328 "required": ["message"],
329 "properties": {"message": {"type": "string"}, "path": {"type": "string"}}
330 }
331 }
332 }
333 }
334 }
335 }
336 }
337
338 The "errors" key will contain one element for all the invalid data, and
339 not just the first one. The useful part for a client is mostly the
340 "path", while the "message" is just to add some human readable debug
341 information for why this request/response failed.
342
343 Rendering binary data
344 Rendering assets and binary data should be accomplished by using the
345 standard Mojolicious tools:
346
347 sub get_image {
348 my $c = shift->openapi->valid_input or return;
349 my $asset = Mojo::Asset::File->new(path => "image.jpeg");
350
351 $c->res->headers->content_type("image/jpeg");
352 $c->reply->asset($asset);
353 }
354
356 Both online and offline tools are available. One example is of this is
357 <https://github.com/mermade/openapi-webconverter>
358
360 File references
361 Relative file references like the following
362
363 "$ref": "my-cool-component.json#"
364 "$ref": "my-cool-component.json"
365
366 Will also be placed under '#/definitions/...', again producing a spec
367 output which will not pass validation.
368
370 Mojolicious::Plugin::OpenAPI, <https://openapis.org/specification>.
371
372
373
374perl v5.32.0 Mojoli2c0i2o0u-s0:8:-P0l9ugin::OpenAPI::Guides::OpenAPIv3(3)