Closed augensalat closed 3 years ago
Yes, it right there in the docs that you linked to. In addition, you need to define your own renderer and rewrite the "errors" for the default cases https://metacpan.org/pod/Mojolicious::Plugin::OpenAPI#RENDERER
Thank you for the quick response but this doesn't look easy to me.
I'm not sure if I understood it correctly, but I started with
$self->plugin(OpenAPI => { ... });
$self->renderer->add_handler(openapi => \&_render);
sub _render {
# basically copy code from Mojolicious::Plugin::OpenAPI with minor changes for error response
...
}
sub _self {
# c&p from Mojolicious::Plugin::OpenAPI without really understanding it
}
sub _log {
# c&p from Mojolicious::Plugin::OpenAPI - nice to have
}
That alone is conufsing enough for application code, but after adjusting my openapi spec for my error response format it failed on the first try: 401 Unauthorized, because the error response is hard coded in Mojolicious::Plugin::OpenAPI::Security
.
What can I do?
Not sure what you are trying to do, but here is a test that shows what I tried to explain: https://github.com/jhthorsen/mojolicious-plugin-openapi/blob/master/t/basic-custom-renderer.t The important thing is:
plugin OpenAPI => {renderer => \&custom_openapi_renderer, ...};
Ah, now I understand. So my first very simple custom renderer only replaces "errors" by "error":
sub _render ($c, $data) {
$data->{error} = delete $data->{errors} if $data->{errors};
return Mojo::JSON::encode_json($data);
}
But output validation happens before rendering, so my OpenAPI spec has to define an error response as in https://metacpan.org/pod/distribution/Mojolicious-Plugin-OpenAPI/lib/Mojolicious/Plugin/OpenAPI/Guides/OpenAPIv3.pod#Default-response-schema, but the actual response is different from that. In the end I'm violating my own spec.
The test that you mention only works, because there you define the error schema as {"type":"object"}
which is basically disabling output validation.
You can redefine the default response schema as mentioned in a previous comment.
No, does not work. The closest I can achive is the following:
This is my DefaultResonse:
Error:
type: object
properties:
error:
$ref: '#/components/schemas/ErrorList'
status:
type: integer
minimum: 400
maximum: 599
description: HTTP status code.
required:
- error
ErrorEntity:
type: object
properties:
message:
type: string
path:
oneOf:
- type: string
- type: array
required:
- message
ErrorList:
type: array
items:
$ref: '#/components/schemas/ErrorEntity'
My OpenAPI plugin config:
$self->plugin(OpenAPI => {
renderer => sub ($c, $data) {
$data->{error} = delete $data->{errors} if $data->{errors};
$c->res->headers->content_type('application/json;charset=UTF-8')
unless $c->res->headers->content_type;
return Mojo::JSON::encode_json($data);
},
url => $self->home->rel_file('openapi.yml'),
schema => 'v3',
default_response_codes => [500],
default_response_name => 'Error',
security => {
bearerAuth => sub ($c, $definition, $scopes, $cb) {
my $auth = $c->req->headers->authorization
or return $c->$cb('Authorization header is not present');
my ($token) = $auth =~ /^Bearer\s+(\S+)$/
or return $c->$cb('Authorization header is not bearer');
return $c->$cb($token eq $bearer_token ? undef : 'Wrong bearer token');
}
}
});
And an error is rendered with
$self->render(
openapi => {
status => $e->status,
errors => [{message => $e->status == 500 ? 'Internal Server Error' : $e->message}]
},
status => $e->status,
);
and my custom renderer above will translate the key errors
to error
.
This will get me a 500 status for every error response and
[warn] OpenAPI >>> POST /v0/orders [{"message":"\/allOf\/0 Missing property.","path":"\/error"}]
in the log.
Why? Because the openapi.yml
requires "error"
, but the validator sees "errors"
since the custom renderer comes after the validator.
I found two options:
require
"error" nor "errors" in the OpenAPI spec. Then we have the key "error" in the openapi spec, but we actually send "errors" to the validator, which is ignored whereas the (missing) error is not checked.$self->render(
openapi => {
status => $e->status,
error => [{message => $e->status == 500 ? 'Internal Server Error' : $e->message}]
},
status => $e->status,
);
This works fine for all kind of errors - even those created in Mojolicious::Plugin::OpenAPI::_render
(which are munged by my custom renderer), but as I wrote yesterday this fails with Mojolicious::Plugin::OpenAPI::Security
, because that module still has hard-wired code to create error responses, which are probably generated before the response validation.
HTTP/1.1 500 Internal Server Error
Content-Length: 84
Server: Mojolicious (Perl)
Content-Type: application/json;charset=UTF-8
Date: Tue, 08 Dec 2020 22:20:16 GMT
{"error":[{"message":"\/allOf\/0 Missing property.","path":"\/error"}],"status":401}
Whichever way I tried - I didn't find a way to customize the error responses in Mojolicious::Plugin::OpenAPI
without disabling the output validation - and this is no serious option.
I know that this is tl:dr and hard to follow, but I would be very happy if you could show me where my mistake might be. Otherwise please re-open this issue.
Hi,
Please open an issue, since I'm very bad at replying to emails.
On Wed, Dec 9, 2020 at 7:41 AM Bernhard Graf notifications@github.com wrote:
No, does not work. The closest I can achive is the following:
This is my DefaultResonse:
Error: type: object properties: error: $ref: '#/components/schemas/ErrorList' status: type: integer minimum: 400 maximum: 599 description: HTTP status code. required: - error ErrorEntity: type: object properties: message: type: string path: oneOf: - type: string - type: array required: - message ErrorList: type: array items: $ref: '#/components/schemas/ErrorEntity'
My OpenAPI plugin config:
$self->plugin(OpenAPI => { renderer => sub ($c, $data) { $data->{error} = delete $data->{errors} if $data->{errors}; $c->res->headers->content_type('application/json;charset=UTF-8') unless $c->res->headers->content_type; return Mojo::JSON::encode_json($data); }, url => $self->home->rel_file('openapi.yml'), schema => 'v3', default_response_codes => [500], default_response_name => 'Error', security => { bearerAuth => sub ($c, $definition, $scopes, $cb) { my $auth = $c->req->headers->authorization or return $c->$cb('Authorization header is not present'); my ($token) = $auth =~ /^Bearer\s+(\S+)$/ or return $c->$cb('Authorization header is not bearer'); return $c->$cb($token eq $bearer_token ? undef : 'Wrong bearer token'); } } });
And an error is rendered with
$self->render( openapi => { status => $e->status, errors => [{message => $e->status == 500 ? 'Internal Server Error' : $e->message}] }, status => $e->status, );
and my custom renderer above will translate the key errors to error.
This will get me a 500 status for every error response and
[warn] OpenAPI >>> POST /v0/orders [{"message":"\/allOf\/0 Missing property.","path":"\/error"}]
in the log. Why? Because the openapi.yml requires "error", but the validator sees "errors" since the custom renderer comes after the validator.
I found two options:
- Neither require "error" nor "errors" in the OpenAPI spec. Then we have the key "error" in the openapi spec, but we actually send "errors" to the validator, which is ignored whereas the (missing) error is not checked.
Change my own error rendering code to have "error" rather than "errors".
$self->render( openapi => { status => $e->status, error => [{message => $e->status == 500 ? 'Internal Server Error' : $e->message}] }, status => $e->status, );
This works fine for all kind of errors - even those created in Mojolicious::Plugin::OpenAPI::_render (which are munged by my custom renderer), but as I wrote yesterday this fails with Mojolicious::Plugin::OpenAPI::Security, because that module still has hard-wired code to create error responses, which are probably generated before the response validation.
HTTP/1.1 500 Internal Server Error Content-Length: 84 Server: Mojolicious (Perl) Content-Type: application/json;charset=UTF-8 Date: Tue, 08 Dec 2020 22:20:16 GMT
{"error":[{"message":"\/allOf\/0 Missing property.","path":"\/error"}],"status":401}
Whichever way I tried - I didn't find a way to customize the error responses in Mojolicious::Plugin::OpenAPI without disabling the output validation - and this is no serious option.
I know that this is tl:dr and hard to follow, but I would be very happy if you could show me where my mistake might be. Otherwise please re-open this issue.
— You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/jhthorsen/mojolicious-plugin-openapi/issues/198#issuecomment-741156281, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAALFIMQTSWXBSFK5YSFFKDST2TSLANCNFSM4UNTUDWQ .
Is it possible to alter the error response schema format that is described in https://metacpan.org/pod/distribution/Mojolicious-Plugin-OpenAPI/lib/Mojolicious/Plugin/OpenAPI/Guides/OpenAPIv3.pod#Default-response-schema and (afaics) is generated in
Mojolicious::Plugin::OpenAPI::_before_render
?If not I would like to express my interest in such a feature.
To answer the inevitable question for the use case: The company I currently work for has some sort of API building style guide that defines the format of error responses.