Route definition with extension, that is text after v path var declaration.fails in 21 but works in 20

Yup. I was thinking about this today. I don’t think it should be too hard.

I need help.

@bp.route('/<cat>-<page>.html', methods=['GET'])
async def handler(request, cat: str, page: str):
    return text(cat + '   ' + page)

it works well in sanic 20.12. but fails on 21 iwht 404 not found.

I have no idea how to convert this route into regex pattern in v21.

You know this will not work.

@app.get('/<cat:(?P<cat>.*)>-<page:(?P<page>.*)\.html>')
async def handler(request, cat: str , page: str):
    return text(cat + '   ' + page)

Thank you for your help.

I think the problem is two variables in none path segement.
Can you try with

@bp.route('/<cat>/<page>.html', methods=['GET'])

?
if this works it means all urls need to be changed in all the clients or a rewrite rule on the server…

Is there are one pat, one parameter rule in the new router?

@bp.route('/<cat>/<page>.html', methods=['GET'])

Yes. This route woks fine. But the change from

@bp.route('/<cat>-<page>.html', methods=['GET'])

to

@bp.route('/<cat>/<page>.html', methods=['GET'])

will have a lot of work to do. The cost will be heavy.

I wonder whether there is any graceful solution here. Thanks.

In Sanic, Is there any chance to specify the extra route separator that this separator can cowork with default separator ‘/’ ?

app.route("/path/to/<foo:^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))>")
async def handler(request, foo: str):
    return text(' handler  ---- ' + foo)

This code snippet in snaic document doesn’t work.

sanic_routing.exceptions.InvalidUsage: Invalid matching pattern ^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))

You can… but it would work in place of /. So it is an either/or. This is how the signals work with . as a delimiter instead of /.

@app.route(r"/path/to/<foo:(?:[12]\d{3}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]))>")

or

@app.route(r"/path/to/<foo:[12]\d{3}-0[1-9]|1[0-2]-0[1-9]|[12]\d|3[01]>")

If it is in the docs, I would be very appreciative if you made this PR for us :pray:

Yes. One path segment, one variable.

You could do something like this:

@app.get(r"/<catpage:(?P<catpage>.*\-.*)\.html>")
async def handler(request, catpage: str):
    cat, page = catpage.split("-")
    return text(f"{cat=} {page=}")

Or

@app.get(r"/<catpage:.*\-.*\.html>")
async def handler(request, catpage: str):
    cat, page = catpage[:-5].split("-")
    return text(f"{cat=} {page=}")

If possible, I want the following feature.

@app.get(r"/<cat:(?P<cat>(.*))>-<page:(?P<page>(\d+))>\.html")
async def handler(request, cat: str, page: int):
    return text(f"{cat=} {page=}")

As a contrast, this is route pattern in Django:

urlpatterns = [
	re_path('city/(?P<city>(.*?))-(?P<page>(.*?)).html', index.paginate, name='paginate'),
]
def paginate(request, city, page):
    
    return render(request, 'cities.html', {'city': city, 'page': page})

You know well I love sanic’ async and await. Thanks.

Sanic v21 currently only allows extraction of one value per route segment.

Thanks for your reply.