Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug in content negotiation when specifying a default format and including it in a call to respond_to() helper #2186

Closed
vmmello opened this issue Jun 22, 2024 · 2 comments

Comments

@vmmello
Copy link

vmmello commented Jun 22, 2024

  • Mojolicious version: 9.37
  • Perl version: 5.38.2
  • Operating system: Alpine Linux 3.19

I'm trying to change the default response type of an endpoint to text/plain, so that when a user runs curl / and the app receives the header Accept: */* it will reply with a txt, not html.

A simplified sample code of how I'm trying it:

get '/'  => { format => "txt" } => sub($c) {
              $c->respond_to(
                html => { },
                txt   => { },
                json  => { },
                any  => { format => "txt" },
              );
        }
        => "index";

__DATA__

@@ index.html.ep
<html></html>

@@ index.txt.ep
text

@@ index.json.ep
{"json":""}

The above looks right to me. But it completely breaks content negotiation, replying all requests with the default format. For example, when running:

$ ./app.pl get -H 'Accept: text/html' /

text

$ ./app.pl get -H 'Accept: application/json' /

text

But if I remove the txt => {} line (line 4) from respond_to(), letting text/plain be catched by the any format, it works as expected (i.e. content negotiation through Accept header works, and it correctly fallsback unknowns to txt format). If I omit the any format it replies with 204 No Content for txt and other formats (other than the ones listed in respond_to()).

So I believe this is a subtle bug in content negotiation, the fact that when you explicitly specify a default format and include it in a respond_to() method call, it breaks content negotiation through the Accept header.

This same problem happens when the default format is explicitly specified as html, e.g.:
get '/' => { format => "html" } => sub { ... };
so it's not the case of changing the response format to something other than html.

@kraih
Copy link
Member

kraih commented Jun 22, 2024

I don't quite see the bug i'm afraid, the logic seems to follow the documentation in the precedence order it is described. https://docs.mojolicious.org/Mojolicious/Guides/Rendering#Content-negotiation

@vmmello
Copy link
Author

vmmello commented Jun 22, 2024

From the documentation you pointed, I imagine you're referring to this phrase as the precedence order:

The best possible representation will be automatically selected from the _format GET/POST parameter, 
format stash value or Accept request header and stored in the format stash value.

If so, ok, it looks like the correct behavior.

But then, what would be the correct way to change a single endpoint to a fallback response format using content negotiation? (it seems to me that Accept: */* header doesn't match the any format).

@mojolicious mojolicious locked and limited conversation to collaborators Jun 22, 2024
@kraih kraih converted this issue into discussion #2187 Jun 22, 2024

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants