Dot character '.' in MVC Web API 2 for request such as api/people/STAFF.45287
Asked Answered
X

9

119

The URL I'm trying to let work is one in the style of: http://somedomain.com/api/people/staff.33311 (just like sites as LAST.FM allow all sort of signs in their RESTFul & WebPage urls, for example "http://www.last.fm/artist/psy'aviah" is a valid url for LAST.FM).

What works are following scenarios: - http://somedomain.com/api/people/ - which returns all people - http://somedomain.com/api/people/staff33311 - would work as well, but it's not what I'm after I'd want the url to accept a "dot", like the example below - http://somedomain.com/api/people/staff.33311 - but this gives me a

HTTP Error 404.0 - Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.

I've set up following things:

  1. The controller "PeopleController"

    public IEnumerable<Person> GetAllPeople()
    {
        return _people;
    }
    
    public IHttpActionResult GetPerson(string id)
    {
        var person = _people.FirstOrDefault(p => p.Id.ToLower().Equals(id.ToLower()));
        if (person == null)
            return NotFound();
    
        return Ok(person);
    }    
    
  2. The WebApiConfig.cs

    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
    
        // Web API routes
        config.MapHttpAttributeRoutes();
    
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
    

I already tried following all the tips of this blogpost http://www.hanselman.com/blog/ExperimentsInWackinessAllowingPercentsAnglebracketsAndOtherNaughtyThingsInTheASPNETIISRequestURL.aspx but it still won't work.. I also think it's quite tedious and I wonder if there isn't another, better and more secure way.

We have our Id's internally like this, so we're going to have to find a solution to fit the dot in one way or another, preferably in the style of "." but I'm open to alternative suggestions for urls if need be...

Ximenes answered 8/1, 2014 at 14:47 Comment(2)
Not an answer, but as to why your're getting a 404 for somedomain.com/api/people/staff.33311 - by default, IIS looks at this URL and sees the . as a file extension and invokes the static file handler, bypassing your MVC API. The answer you accepted (running all managed modules for all requests) works because you force every request to IIS to go through the ASP.NET pipeline (therefore, your controllers)Daisie
Closely related post here.Nowak
C
116

Following setting in your web.config file should fix your issue:

<configuration>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true" />
Christo answered 8/1, 2014 at 15:47 Comment(14)
I will try this out asap when I'm back at the officeXimenes
It did the trick. Though, are there any vulnerabilities by setting this option? Why isn't this the standard behaviour?Ximenes
Ok, for anyone's interest: I see my question above being answered here on the accepted answer at the time of writing (answer by Kapil Khandelwal): #11049363Ximenes
Yes, it worked, bus others, I would like to know which particular module is required for this functionality to work?Dissolve
When I tried this, it would only work if I put a '/' at the end of the path. Any suggestions why this is? On a side note, the answer below that specifically adds the 'UrlRoutingModule' instead of running all modules, also worked for me, although still with the issue that it requires a '/' at the end to work.Altitude
I don't need to add the "/" character for it to work.. Website's running under Windows Server 2008 R2 with IIS 7.5Ximenes
https://mcmap.net/q/98824/-dots-in-url-causes-404-with-asp-net-mvc-and-iis is worth looking at if you are concerned (and rightfully so) about the performance impact of enabling runAllManagedModulesForAllRequests.Daisie
@KiranChalla: Does this work when the app runs under classic mode? It doesn't seem so in my case.Shing
That solved my issue. I was trying to pass an android app ID via the RESTFul api (br.com.google) and it was not working. Thanks !Salubrious
HTTP Error 500.19 - Internal Server Error The requested page cannot be accessed because the related configuration data for the page is invalid.Stansbury
This should work, but it is a bit overkill. It should be ok for WEB API because you should not have any static resources, but I would not use it for MVC.Incunabulum
Thank you. This also works for @ at-sign character in route.Pettit
I also had to add a new attribute in web.config system.web/httpRuntime: relaxedUrlToFileSystemMapping="true"Printmaking
This worked AND ALSO fixed an issue where the Swagger page "Can't read swagger JSON from https://[MACHINE:PORT]/swagger/docs/v1.1" My assumption is that the v.1.1 in the URL was succumbing to the same issue.Withal
O
151

Suffix the URL with a slash e.g. http://somedomain.com/api/people/staff.33311/ instead of http://somedomain.com/api/people/staff.33311.

Om answered 28/7, 2015 at 12:57 Comment(10)
@AgustinMeriles my answer could be considered more of a workaround than an actual answer, depends on your interpretation of the question.Om
That does not work though when the dot is at the end of the URL section as in /people/member.Biflagellate
If the input isn't simple, perhaps an odata style query suffix is more suitable that a rest URL.Om
No, what if I don't want a slash at the end?! It should not be required.Madelenemadelin
@JoshM. This is a workaround, I didn't write ASP.NET. Also, the slash doesn't affect the request. It does, however, help make your intentions more clear as in google.com/my query goes here/ vs. google.com/subDomain my query goes here.Om
This is best answer. No need to change in web.config. UpvoteBrenan
Really love this answer. After spending a day looking at a variety of routing options in WebApi, this solution just is so simple, it is beautiful.Kimberleekimberley
@JoshM. - it's not ideal, yes. What i did was just add an 'endpoint' to the url, so instead of something like analytes/method/2008.(w)/ i have analytes/method/2008.(w)/detail. That way requests with a dot in them still work and i don't have that trailing slash hanging out like it doesn't belong. Hope that helps someone.Muddleheaded
Yes this is the best you shouldn't have to modify the web.config.Rockey
it doesn't work for me. Maybe some breaking changes in WebAPIStiff
C
116

Following setting in your web.config file should fix your issue:

<configuration>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true" />
Christo answered 8/1, 2014 at 15:47 Comment(14)
I will try this out asap when I'm back at the officeXimenes
It did the trick. Though, are there any vulnerabilities by setting this option? Why isn't this the standard behaviour?Ximenes
Ok, for anyone's interest: I see my question above being answered here on the accepted answer at the time of writing (answer by Kapil Khandelwal): #11049363Ximenes
Yes, it worked, bus others, I would like to know which particular module is required for this functionality to work?Dissolve
When I tried this, it would only work if I put a '/' at the end of the path. Any suggestions why this is? On a side note, the answer below that specifically adds the 'UrlRoutingModule' instead of running all modules, also worked for me, although still with the issue that it requires a '/' at the end to work.Altitude
I don't need to add the "/" character for it to work.. Website's running under Windows Server 2008 R2 with IIS 7.5Ximenes
https://mcmap.net/q/98824/-dots-in-url-causes-404-with-asp-net-mvc-and-iis is worth looking at if you are concerned (and rightfully so) about the performance impact of enabling runAllManagedModulesForAllRequests.Daisie
@KiranChalla: Does this work when the app runs under classic mode? It doesn't seem so in my case.Shing
That solved my issue. I was trying to pass an android app ID via the RESTFul api (br.com.google) and it was not working. Thanks !Salubrious
HTTP Error 500.19 - Internal Server Error The requested page cannot be accessed because the related configuration data for the page is invalid.Stansbury
This should work, but it is a bit overkill. It should be ok for WEB API because you should not have any static resources, but I would not use it for MVC.Incunabulum
Thank you. This also works for @ at-sign character in route.Pettit
I also had to add a new attribute in web.config system.web/httpRuntime: relaxedUrlToFileSystemMapping="true"Printmaking
This worked AND ALSO fixed an issue where the Swagger page "Can't read swagger JSON from https://[MACHINE:PORT]/swagger/docs/v1.1" My assumption is that the v.1.1 in the URL was succumbing to the same issue.Withal
D
38

I've found that adding the following before the standard ExtensionlessUrlHandler solves the issue for me:

<add name="ExtensionlessUrlHandler-Integrated-4.0-ForApi"
     path="api/*"
     verb="*"
     type="System.Web.Handlers.TransferRequestHandler"
     preCondition="integratedMode,runtimeVersionv4.0" />

I don't think the name actually matters all that much except it probably helps if your IDE (Visual Studio in my case) is managing your site configuration.

H/T to https://mcmap.net/q/152371/-unable-to-map-an-httphandler-to-a-quot-path-quot-wildcard-mapping

Dabney answered 1/6, 2016 at 21:10 Comment(4)
I added it 'after' the standard line and it also worked fine.Maximomaximum
This should be the accepted answer. Does not require / at end of uriAstounding
Thanks for the BEFORE, because it wasn't working after!Humes
I believe the modules run in order of declaration, so always put more specific (or more important) ones before more generic ones.Dabney
D
25

I don't know what I am doing really, but after playing with the previous answer a bit I came up with another, perhaps more appropriate, solution:

<system.webServer>
<modules>
    <remove name="UrlRoutingModule-4.0" />
    <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
</modules>
</system.webServer>
Dissolve answered 17/3, 2014 at 20:49 Comment(4)
Thanks, I also don't know what it is doing but it looks better than the other solution.Haughty
appears to move the url routing module (which is what's catching that the request includes an extension and then tries to handle it as a file) to the end of the list of modules, which is sufficient to put it after the module handling the api request. IMHO, this is the best workaround available from the ones I've seen on SO, at least ATTOWGoodygoody
It isn't the relocation to the end of the list, it's the removal of the default precondition that this module only run for managed handlers. The default config uses this format: <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler" />Humidifier
Thanks - that would explain the behavior.Dissolve
M
13

I found that I needed to do more than just set the runAllManagedModulesForAllRequests attribute to true. I also had to ensure that the extensionless URL handler was configured to look at all paths. In addition, there is one more bonus configuration setting you can add which will help in some cases. Here is my working Web.config:

<system.web>
    <httpRuntime relaxedUrlToFileSystemMapping="true" />
</system.web>
<system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <handlers>
        <remove name="WebDAV" />
        <remove name="OPTIONSVerbHandler" />
        <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
        <add name="ExtensionlessUrlHandler-Integrated-4.0"  path="*" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
</system.webServer>

Note, specifically, that the ExtensionlessUrlHandler-Integrated-4.0 has its path attribute set to * as opposed to *. (for instance).

Madelenemadelin answered 6/5, 2016 at 19:58 Comment(5)
I just got bit by the path="*.". Just curious, what is the reason people set that to path="*."?Amin
Actually, I changed it to path="*" and had a problem because we're hosting a documentation site side by side with our WebAPI and that site had problems with .jpg, .png, and other files with extensions.Amin
@Amin Did you also set <modules runAllManagedModulesForAllRequests="true" /> ? That should make .NET handle those static files.Madelenemadelin
Yes. I wound up doing that. I don't really like that though because now all the static files go through the .NET pipeline. Luckily, as this is a webAPI service, only the Swagger and Swashbuckle stuff for the API docs/helper site are affected.Amin
i found this broke all static file requests. error 500. I had runAllManaged... set to true.Mesitylene
F
3

I'd use this in Web.config file:

<add name="ManagedSpecialNames" path="api/people/*" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />

before standard "ExtensionlessUrlHandler".

For instance in my case I put it here:

<handlers>
  <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  <remove name="OPTIONSVerbHandler" />
  <remove name="TRACEVerbHandler" />
  <add name="ManagedFiles" path="api/people/*" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>

So you force URLs of such pattern to be managed by you, instead of standard management as files in application directory tree.

Furnish answered 6/11, 2019 at 10:38 Comment(0)
A
2

I got stuck in this situation but appending / at the end of URL wasn't look clean for me.

so just add below in web.config handlers tag and you will be good to go.

<add name="Nancy" path="api" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" allowPathInfo="true" />
Aplacental answered 14/5, 2018 at 8:28 Comment(1)
Can you explain what Nancy is and whether it is a library to be included in the project? Thanks!Furnish
T
1

I found that both way works for me: either setting runAllManagedModulesForAllRequests to true or add ExtentionlessUrlHandler as following. Finally i choose to add extensionUrLHandler since runAllManagedModulesForAllRequests do have performance impact to the site.

<handlers>
  <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
  <remove name="OPTIONSVerbHandler" />
  <remove name="TRACEVerbHandler" />
  <remove name="WebDAV" />
  <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*" verb="*" 
       type="System.Web.Handlers.TransferRequestHandler" 
       preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
Trinetta answered 19/5, 2017 at 5:29 Comment(0)
N
-1

I also faced this issue. I was under a circumstance where I was not supposed to play with IIS and website config related settings. So I had to make it working by making changes at the code level only.

The point is that the most common case where you would end up having dot character in the URL is when you get some input from the user and pass it as a query string or url fragment to pass some argument to the parameters in the action method of your controller.

public class GetuserdetailsbyuseridController : ApiController
{
     string getuserdetailsbyuserid(string userId)
     {
        //some code to get user details
     }
}

Have a look at below URL where user enters his user id to get his personal details:

http://mywebsite:8080/getuserdetailsbyuserid/foo.bar

Since you have to fetch some data from the server we use http GET verb. While using GET calls any input parameters can be passed only in the URL fragments.

So to solve my problem I changed the http verb of my action to POST. Http POST verb has the facility of passing any user or non-user input in body also. So I created a JSON data and passed it into the body of the http POST request:

{
  "userid" : "foo.bar"
}

Change your method definition as below:

public class GetuserdetailsbyuseridController : ApiController
{
     [Post]
     string getuserdetailsbyuserid([FromBody] string userId)
     {
        //some code to get user details
     }
}

Note: More on when to use GET verb and when to use POST verb here.

Nowak answered 19/10, 2017 at 2:40 Comment(1)
Not an appropriate solution if you're building a REST API.Slope

© 2022 - 2024 — McMap. All rights reserved.