mojolicious / mojo

:sparkles: Mojolicious - Perl real-time web framework
https://mojolicious.org
Artistic License 2.0
2.66k stars 576 forks source link

Declare formats in the route pattern #1740

Closed kraih closed 3 years ago

kraih commented 3 years ago

After the unfortunate 9.11 release, which had to disable format detection, using formats has become rather verbose.

# /foo
# /foo.html
# /foo.json
$r->get('/foo' => [format => ['html', 'json']])->to('bar#yada', format => undef);

There are many things we could do to improve that. For example we could allow format alternatives in the route pattern.

# /foo.html
# /foo.json
$r->get('/foo<.html,json>')->to('bar#yada');

Or we could use a named placeholder that allows for types to be reused.

# /foo
# /foo.html
# /foo.json
$r->add_type(rest_api => ['html', 'json']);
$r->get('/foo<:format:rest_api>')->to('bar#yada', format => undef);

There could also be variants that make the format optional from the pattern without requiring format => undef.

# /foo
# /foo.html
# /foo.json
$r->get('/foo<.html,json?>')->to('bar#yada');

# /foo
# /foo.html
# /foo.json
$r->add_type(rest_api => ['html', 'json']);
$r->get('/foo<:format:rest_api?>')->to('bar#yada');

What do you think? How would you like to declare formats for a route?

christopherraa commented 3 years ago

My $.02 would simply be that I have always greatly enjoyed being able to smack .json, .html, .txt or whatever on the url and have the application respond based on that. The response being a template rendered without hitting a controller or simply a controller method calling $c->respond_to(...) with the types supported. Usually the calls to respond_to() supplies an any => {...} to catch whenever an unsupported format is requested. And if not, well, then the logs will say so and we can respond to that appropriately.

I'll try to think a bit more about the above suggestions and see if I have something useful to propose. Though I do assume you've covered the options here pretty well.

kraih commented 3 years ago

There are other "more creative" options too that i have not yet mentioned. Such as another special format variant that captures arbitrary extensions ($r->get('/foo' => [format => '*'])), but then does not merge them into the stash (avoiding the 9.11 problem). And then have $c->respond_to(...) still look into the place where this new variant stored the extension (say $c->stash->{'mojo.format'} = 'json').

kraih commented 3 years ago

Another possible solution would be to make all format settings inheritable by nested routes. It's actually completely backwards compatible, i already have a branch with a patch. https://github.com/mojolicious/mojo/compare/format_inheritance

kraih commented 3 years ago

The patch has been merged, so we now have format inheritance. That makes formats in the route pattern a lot less necessary. In fact, if used in the pattern of the parent route it might even end up looking a bit awkward.

kraih commented 3 years ago

And released with 9.16. I think that might be a good enough solution for this issue. We'll have to revisit the issue depending on how the user feedback turns out.