Mojolicious::Plugin::OpenAPI::Cors(3pm) | User Contributed Perl Documentation | Mojolicious::Plugin::OpenAPI::Cors(3pm) |
Mojolicious::Plugin::OpenAPI::Cors - OpenAPI plugin for Cross-Origin Resource Sharing
Set "add_preflighted_routes" to 1, if you want "Preflighted" CORS requests to be sent to your already existing actions.
$app->plugin(OpenAPI => {add_preflighted_routes => 1, %openapi_parameters});
See "register" in Mojolicious::Plugin::OpenAPI for what %openapi_parameters might contain.
The following example will automatically set default CORS response headers after validating the request against "openapi_cors_allowed_origins":
package MyApp::Controller::User; sub get_user { my $c = shift->openapi->cors_exchange->openapi->valid_input or return; # Will only run this part if both the cors_exchange and valid_input was successful. $c->render(openapi => {user => {}}); }
It's possible to enable preflight and simple CORS support directly in the specification. Here is one example:
"/user/{id}/posts": { "parameters": [ { "in": "header", "name": "Origin", "type": "string", "pattern": "https?://example.com" } ], "options": { "x-mojo-to": "#openapi_plugin_cors_exchange", "responses": { "200": { "description": "Cors exchange", "schema": { "type": "string" } } } }, "put": { "x-mojo-to": "user#add_post", "responses": { "200": { "description": "Add a new post.", "schema": { "type": "object" } } } } }
The special part can be found in the "OPTIONS" request It has the "x-mojo-to" key set to "#openapi_plugin_cors_exchange". This will enable Mojolicious::Plugin::OpenAPI::Cors to take over the route and add a custom callback to validate the input headers using regular OpenAPI rules and respond with a "200 OK" and the default headers as listed under "openapi.cors_exchange" if the input is valid. The only extra part that needs to be done in the "add_post()" action is this:
sub add_post { my $c = shift->openapi->valid_input or return; # Need to respond with a "Access-Control-Allow-Origin" header if # the input "Origin" header was validated $c->res->headers->access_control_allow_origin($c->req->headers->origin) if $c->req->headers->origin; # Do the rest of your custom logic $c->respond(openapi => {}); }
If you need full control, you must pass a callback to "openapi.cors_exchange":
package MyApp::Controller::User; sub get_user { # Validate incoming CORS request with _validate_cors() my $c = shift->openapi->cors_exchange("_validate_cors")->openapi->valid_input or return; # Will only run this part if both the cors_exchange and valid_input was # successful. $c->render(openapi => {user => {}}); } # This method must return undef on success. Any true value will be used as an error. sub _validate_cors { my $c = shift; my $req_h = $c->req->headers; my $res_h = $c->res->headers; # The following "Origin" header check is the same for both simple and # preflighted. return "/Origin" unless $req_h->origin =~ m!^https?://whatever.example.com!; # The following checks are only valid if preflighted... # Check the Access-Control-Request-Headers header my $headers = $req_h->header('Access-Control-Request-Headers'); return "Bad stuff." if $headers and $headers =~ /X-No-Can-Do/; # Check the Access-Control-Request-Method header my $method = $req_h->header('Access-Control-Request-Methods'); return "Not cool." if $method and $method eq "DELETE"; # Set the following header for both simple and preflighted on success # or just let the auto-renderer handle it. $c->res->headers->access_control_allow_origin($req_h->origin); # Set Preflighted response headers, instead of using the default if ($c->stash("openapi_cors_type") eq "preflighted") { $c->res->headers->header("Access-Control-Allow-Headers" => "X-Whatever, X-Something"); $c->res->headers->header("Access-Control-Allow-Methods" => "POST, GET, OPTIONS"); $c->res->headers->header("Access-Control-Max-Age" => 86400); } # Return undef on success. return undef; }
Mojolicious::Plugin::OpenAPI::Cors is a plugin for accepting Preflighted or Simple Cross-Origin Resource Sharing requests. See <https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS> for more details.
This plugin is loaded by default by Mojolicious::Plugin::OpenAPI.
Note that this plugin currently EXPERIMENTAL! Please comment on <https://github.com/jhthorsen/mojolicious-plugin-openapi/pull/102> if you have any feedback or create a new issue.
The following "stash variables" can be set in "defaults" in Mojolicious, "to" in Mojolicious::Routes::Route or "stash" in Mojolicious::Controller.
This variable should hold an array-ref of regexes that will be matched against the "Origin" header in case the default "openapi_cors_default_exchange_callback" is used. Examples:
$app->defaults(openapi_cors_allowed_origins => [qr{^https?://whatever.example.com}]); $c->stash(openapi_cors_allowed_origins => [qr{^https?://whatever.example.com}]);
This value holds a default callback that will be used by "openapi.cors_exchange", unless you pass on a $callback. The default provided by this plugin will simply validate the "Origin" header against "openapi_cors_allowed_origins".
Here is an example to allow every "Origin"
$app->defaults(openapi_cors_default_exchange_callback => sub { my $c = shift; $c->res->headers->header("Access-Control-Allow-Origin" => "*"); return undef; });
Holds the default value for the "Access-Control-Max-Age" response header set by "openapi.cors_preflighted". Examples:
$app->defaults(openapi_cors_default_max_age => 86400); $c->stash(openapi_cors_default_max_age => 86400);
Default value is 1800.
This stash variable is available inside the callback passed on to "openapi.cors_exchange". It will be either "preflighted", "real" or "simple". "real" is the type that comes after "preflighted" when the actual request is sent to the server, but with "Origin" header set.
$c = $c->openapi->cors_exchange($callback); $c = $c->openapi->cors_exchange("MyApp::cors_validator"); $c = $c->openapi->cors_exchange("_some_controller_method"); $c = $c->openapi->cors_exchange(sub { ... }); $c = $c->openapi->cors_exchange;
Used to validate either a simple CORS request, preflighted CORS request or a real request. It will be called as soon as the "Origin" request header is seen.
The $callback will be called with the current Mojolicious::Controller object and must return an error or "undef()" on success:
my $error = $callback->($c);
The $error must be in one of the following formats:
Returning "undef()" means that the CORS request is valid.
Shortcut for generating a 400 Bad Request response with a header name. Example:
return "/Access-Control-Request-Headers";
Used to generate a 400 Bad Request response with a completely custom message.
Used to generate a completely custom 400 Bad Request response. Example:
return [{message => "Some error!", path => "/Whatever"}]; return [{message => "Some error!"}]; return [JSON::Validator::Error->new];
On success, the following headers will be set, unless already set by $callback:
Set to the header of the incoming "Access-Control-Request-Headers" header.
Set to the list of HTTP methods defined in the OpenAPI spec for this path.
Set to the "Origin" header in the request.
Set to "openapi_cors_default_max_age".
Called by Mojolicious::Plugin::OpenAPI.
Mojolicious::Plugin::OpenAPI.
2023-02-21 | perl v5.36.0 |