MVC Web API 2 中的点字符 '.' 在请求中的作用,例如 api/people/STAFF.45287。

119

我想让其运行的URL样式为:http://somedomain.com/api/people/staff.33311(就像LAST.FM等网站允许在其RESTful和WebPage URL中使用各种符号一样,例如“http://www.last.fm/artist/psy'aviah”是LAST.FM的有效URL)。

以下情况是可以工作的: - http://somedomain.com/api/people/ - 返回所有人 - http://somedomain.com/api/people/staff33311 - 也可以,但这不是我想要的 我想要的是url接受“点”,如下例所示: - http://somedomain.com/api/people/staff.33311 - 但是这个给了我一个

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

我已经设置了以下内容:

  1. 控制器 "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);
}    
  • 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 }
        );
    }
    

    我已经尝试了这篇博客文章中的所有提示:http://www.hanselman.com/blog/ExperimentsInWackinessAllowingPercentsAnglebracketsAndOtherNaughtyThingsInTheASPNETIISRequestURL.aspx,但仍然无法解决。我也认为这样做相当繁琐,不知道是否有另一种更好、更安全的方法。

    我们的内部ID是像这样的,所以我们需要找到一种方法来适应".",最好是像点一样,但如果需要的话,我也可以接受URL的替代建议...


    11
    不是答案,但是关于为什么你会在http://somedomain.com/api/people/staff.33311收到404错误消息:默认情况下,IIS查看此URL并将“.”视为文件扩展名,从而调用静态文件处理程序,绕过了你的MVC API。你接受的答案(对所有请求运行所有托管模块)之所以有效,是因为它强制每个请求通过ASP.NET流水线(因此经过你的控制器)进入IIS。 - Henry C
    这里有一个与之密切相关的帖子链接 - RBT
    9个回答

    151
    在URL末尾加上斜杠,例如http://somedomain.com/api/people/staff.33311/而不是http://somedomain.com/api/people/staff.33311

    3
    我的回答可能更多地被认为是一个解决方法而不是实际答案,这取决于你对问题的理解。 - Danny Varod
    5
    如果URL部分以点号结尾,例如/people/member,则这种方法就不起作用。 - eYe
    2
    不,如果我不想在结尾加斜杠怎么办?!这是不必要的。 - Josh M.
    1
    @JoshM。这只是一个解决方法,我没有编写ASP.NET。另外,斜杠不会影响请求。然而,它可以帮助你更清楚地表达意图,例如google.com/my query goes here/google.com/subDomain my query goes here - Danny Varod
    1
    @JoshM. - 这并不是理想的解决方案,是的。我所做的只是在URL中添加了一个“端点”,因此,与其像analytes/method/2008.(w)/这样的东西,我现在有了analytes/method/2008.(w)/detail。这样,带有点的请求仍然可以正常工作,而且我也不会让那个尾随的斜杠像不属于它一样挂在那里。希望这能帮助到某些人。 - Jamie M
    显示剩余5条评论

    116

    在您的web.config文件中进行以下设置即可解决问题:

    <configuration>
        <system.webServer>
            <modules runAllManagedModulesForAllRequests="true" />
    

    4
    做到了。不过,设置这个选项会有什么漏洞吗?为什么这不是标准行为? - Yves Schelpe
    2
    好的,对于任何人的兴趣:我看到我的问题在这里得到回答,写作时是被Kapil Khandelwal接受的答案:https://dev59.com/hWgu5IYBdhLWcg3w0qRK - Yves Schelpe
    2
    是的,它起作用了,但是除此之外,我想知道为了使这个功能正常工作,需要哪个具体的模块? - Greg Z.
    3
    当我尝试这样做时,只有在路径末尾加上 '/' 才能生效。有什么建议吗?另外,下面的答案明确添加了 'UrlRoutingModule' 而不是运行所有模块,对我也起作用,虽然仍然存在需要在末尾添加 '/' 才能正常工作的问题。 - Nikolaj Dam Larsen
    10
    如果你关心(并且理所当然)启用runAllManagedModulesForAllRequests的性能影响,那么看一下https://dev59.com/PGgt5IYBdhLWcg3w5xg_#12151501是值得的。 - Henry C
    显示剩余9条评论

    38

    我发现在标准的ExtensionlessUrlHandler之前添加以下内容可以解决我的问题:

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

    我认为名称并不是很重要,除非你的IDE(在我的情况下是Visual Studio)正在管理你的站点配置,这可能会有所帮助。

    感谢https://dev59.com/fm025IYBdhLWcg3wjGpO#15802305


    1
    我在标准行“后面”添加了它,它也正常工作。 - Jalal El-Shaer
    这应该是被接受的答案。URI结尾不需要斜杠。 - daudihus
    2
    谢谢 BEFORE,因为之后它没能正常工作! - Mese
    我相信模块按照声明的顺序运行,因此始终将更具体(或更重要)的模块放在更通用的模块之前。 - BrianS

    25

    我其实不知道自己在做什么,但在对之前的答案进行一些修改后,我想到了另一个或许更为合适的解决方案:

    <system.webServer>
    <modules>
        <remove name="UrlRoutingModule-4.0" />
        <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" />
    </modules>
    </system.webServer>
    

    谢谢,我也不知道它在做什么,但它看起来比其他解决方案好。 - Thomas
    1
    似乎移动了URL路由模块(它捕获请求包含扩展名,然后尝试将其处理为文件)到模块列表的末尾,这足以将其放在处理API请求的模块之后。在我看来,这是我在SO上看到的最好的解决方法之一,至少目前是这样。 - James Manning
    2
    这不是将其重定位到列表末尾,而是删除此模块仅适用于托管处理程序的默认前提条件。默认配置使用以下格式: <add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="managedHandler" /> - theta-fish
    谢谢,这可以解释这种行为。 - Greg Z.

    12

    我发现不仅需要将runAllManagedModulesForAllRequests属性设置为true,还需要确保无扩展名URL处理程序配置以查看所有路径。此外,您还可以添加一个额外的配置设置,在某些情况下会有所帮助。以下是我的工作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>
    
    请注意,特别是,ExtensionlessUrlHandler-Integrated-4.0path 属性设置为 *,而不是 *. (例如)。

    1
    我刚被 path="*." 咬了一口。只是好奇,人们将其设置为 path="*." 的原因是什么? - JustinP8
    1
    实际上,我将其更改为 path="*",但出现了问题,因为我们在WebAPI旁边托管了一个文档站点,该站点对具有扩展名的 .jpg、.png 和其他文件存在问题。 - JustinP8
    @JustinP8 你是否也设置了 <modules runAllManagedModulesForAllRequests="true" />?这样应该可以让.NET处理那些静态文件。 - Josh M.
    1
    是的,我最终做了那个。但我并不是很喜欢这样做,因为现在所有的静态文件都要通过.NET管道处理。幸运的是,由于这是一个WebAPI服务,只有API文档/帮助站点的Swagger和Swashbuckle相关内容受到影响。 - JustinP8
    我发现这破坏了所有静态文件请求。错误500。我将runAllManaged...设置为true。 - Sam

    3

    我会在Web.config文件中使用以下代码:

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

    在标准的“ExtensionlessUrlHandler”之前。

    例如,在我的情况下,我把它放在这里:

    <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>
    

    因此,您需要强制管理此类模式的URL,而不是将它们作为应用程序目录树中的文件进行标准管理。


    2

    我遇到了这种情况,但在URL末尾添加/对我来说不太好看。

    所以只需在web.confighandlers标签中添加以下内容,就可以解决问题。

    <add name="Nancy" path="api" verb="*" type="Nancy.Hosting.Aspnet.NancyHttpRequestHandler" allowPathInfo="true" />
    

    你能解释一下 Nancy 是什么,以及它是否是需要包含在项目中的库吗?谢谢! - bluish

    1
    我发现两种方法都适合我:要么将runAllManagedModulesForAllRequests设置为true,要么按照以下方式添加ExtentionlessUrlHandler。最终,我选择添加extensionUrLHandler,因为runAllManagedModulesForAllRequests对网站的性能有影响。
    <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>
    

    -1

    我也遇到了这个问题。在某种情况下,我不应该玩IIS和网站配置相关的设置。因此,我只能通过在代码层面上进行更改来使其正常工作。

    关键是,你最有可能在URL中使用点字符的情况是当你从用户那里获取一些输入并将其作为查询字符串或URL片段传递给控制器中操作方法的参数时。

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

    请看下面的网址,用户输入其用户编号以获取个人详细信息:
    http://mywebsite:8080/getuserdetailsbyuserid/foo.bar
    

    由于您需要从服务器获取一些数据,因此我们使用http GET动词。在使用GET调用时,任何输入参数都只能通过URL片段传递。

    所以为了解决我的问题,我将操作的http动词更改为POST。Http POST动词具有在正文中传递任何用户或非用户输入的功能。因此,我创建了一个JSON数据并将其传递到http POST请求的正文中:

    {
      "userid" : "foo.bar"
    }
    

    将您的方法定义更改为以下内容:

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

    注意:关于何时使用GET动词和何时使用POST动词,可以在这里了解更多信息。


    如果你正在构建一个REST API,这不是一个合适的解决方案。 - Mustafa Ozturk

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接