Mvc: Specifying '.{format?}' in a route template throws exception in some cases

Created on 13 Nov 2015  Â·  10Comments  Â·  Source: aspnet/Mvc

Following setup works for the following cases:
/api/values
/api/values.xml
/api/values/10
/api/values/10.xml

[Route("api/[controller]")]
public class ValuesController : Controller
{
    [FormatFilter]
    [HttpGet]
    [HttpGet("/api/[controller].{format}")] 
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    [FormatFilter]
    [HttpGet("{id}.{format?}")]
    public string Get(int id)
    {
        return "value";
    }

But when I try just having one route like [HttpGet("/api/[controller].{format?}")] on the first action I got the following exception:

InvalidOperationException: The following errors occurred with attribute routing information:

For action: 'WebApplication51.Controllers.ValuesController.Get'
Error: In the segment 'Values.{format?}', the optional parameter 'format' is preceded by an invalid segment 'Values.'. Only a period (.) can precede an optional parameter.
Parameter name: routeTemplate

Note that having the ‘.{format?}’ on the second action does not throw this error but it thrown for the first action.

Discussed with @rynowak

1 - Ready bug needs design

Most helpful comment

Hello I got the same problem I have this route attribute on one of my actions:
[Route("/{devId}/{senId}/data.{format?}")]
A workaround I found is to create a route variable with default value like this:
[Route("/{devId}/{senId}/{file=data}.{format?}")]

All 10 comments

Can we also consider adding the FormatFilter globally when the format mappings are setup?

It's also pretty ugly that you have to specify the full route on the first action including the api/[controller] prefix.

It's also pretty ugly that you have to specify the full route on the first action including the api/[controller] prefix.

You can just do [HttpGet, HttpGet(".{format}")]. Once we fix this bug [HttpGet(".{format?}")] should work.~

Yeah, I realize that won't work.

Can we also consider adding the FormatFilter globally when the format mappings are setup?

Mappings are set up today by doing things like AddJsonFormatter(). We might just want to add the format filter always.

@rynowak thoughts on how to make the sub-attribute-route "mergeable" without the intermediate / that gets inserted? An option on HttpGet such as [HttpGet("sub-route", InsertSlash = false)] or something similar?

No bright ideas here. [HttpGet("sub-route", InsertSlash = false)] is pretty gross. I'd rather leave [HttpGet("/api/[controller].{format?}")] than that. There are lots of places in attribute routing where we haven't optimized for being terse.

If we really want to optimize this, we could make . special as the first character of a route template.

Just . would be a super obscure special case. I'm also fine doing nothing for that case now. My suggestion could be implemented at any time, if there was enough demand for it.

Deciding not too do this for now. We don't know of a good fix and the scenario doesn't seem super common. And there's a workaround.

Hello I got the same problem I have this route attribute on one of my actions:
[Route("/{devId}/{senId}/data.{format?}")]
A workaround I found is to create a route variable with default value like this:
[Route("/{devId}/{senId}/{file=data}.{format?}")]

Closing this issue as there was no community involvement for quite a while now and there is also a workaround available.

Was this page helpful?
0 / 5 - 0 ratings