如何在Azure中允许URL编码的路径段

Mic*_*ltu 12 iis azure azure-web-sites asp.net-core-mvc asp.net-core

我有一个在Azure中运行的ASP.NET 6 MVC应用程序.我有一个像这样的动作的控制器

[HttpDelete]
[Route("{image_url}")]
public async Task<IActionResult> RemoveImageUrl([FromRoute(Name = "image_url")] String imageUrlString)
Run Code Online (Sandbox Code Playgroud)

然后我称之为

api/https%3A%2F%2Frepocreator.zoltu.io%2Fimages%2FZoltu-Logo-Full-Size.png"
Run Code Online (Sandbox Code Playgroud)

使用Kestrel进行自托管时,此应用程序正常工作,但是一旦部署到Azure,我就会收到500个错误.我已尽可能多地调试,经过大量的谷歌搜索和戳戳后,似乎IIS正在尝试帮助URL解码请求,然后将其转发给ASP.NET来处理.当然,问题是即使我可以说服IIS接受请求

<system.webServer>
    <security>
        <requestFiltering allowDoubleEscaping="true" />
    </security>
</system.webServer>
<system.web>
    <httpRuntime requestValidationMode="2.0" requestPathInvalidCharacters="" relaxedUrlToFileSystemMapping="true"/>
    <pages validateRequest="false" />
</system.web>
Run Code Online (Sandbox Code Playgroud)

它仍然解码URL并将解码后的URL传递给ASP.NET,后者找不到匹配的路由.

我该怎么做才能告诉IIS停止尝试在这里提供帮助,只是传递它获得的任何URL,而不进行任何类型的预验证或重写?注意:这是一个Azure Web App,因此我无法直接访问IIS设置.

Dan*_*.G. 5

您只需更新路线定义,使其与解码的图像 url 参数匹配即可。

根据文档,定义路由模板时:

您可以使用 * 字符作为路由值名称的前缀来绑定到 URI 的其余部分。例如,blog/{*slug} 将匹配以 /blog/ 开头且其后具有任何值(将分配给 slug 路由值)的任何 URI。

因此,您可以创建一个与路线匹配的操作[Route("{*image_url}")]

[Route("{*image_url}")]
public IActionResult RemoveImageUrl([FromRoute(Name = "image_url")]String imageUrlString)
{
    return Json(new { ReceivedImage = imageUrlString });
}
Run Code Online (Sandbox Code Playgroud)

我看到的唯一问题是协议部分被解码为http:/单个/. 您有几个选择:

  • 您可以在控制器中手动修复它。更好的是,您可以创建一个模型绑定器和参数约定来自动执行此操作:

    [HttpDelete]
    [Route("{*image_url}")]
    public IActionResult RemoveImageUrl([FullUrlFromEncodedRouteParam(Name = "image_url")] String imageUrlString)            
    {
        return Json(new { ReceivedImage = imageUrlString });
    }
    
    public class FullUrlFromUrlEncodedPathSegmentModelBinder : IModelBinder
    {
        //Matches a url that starts with protocol string and is followed by exactly ":/" instead of "://"
        private static Regex incorrectProtocolRegex = new Regex(@"^([a-z][\w-]+:)\/{1}(?!\/)");
    
        //A url path included as a url encoded path segment like http://localhost:39216/image2/https%3A%2F%2Frepocreator.zoltu.io%2Fimages%2FZoltu-Logo-Web.png
        //will be decoded by IIS as https:/repocreator.zoltu.io/images/Zoltu-Logo-Web.png, note the single '/' after the protocol
        //This model binder will fix it replacing "http:/" with "http://"
        public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext.ValueProvider.GetValue(bindingContext.ModelName) == null)
                return Task.FromResult(ModelBindingResult.NoResult);                            
    
            var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).FirstValue as string;
            var fixedVal = incorrectProtocolRegex.Replace(val, @"$1//");                                                
            return Task.FromResult(ModelBindingResult.Success(bindingContext.ModelName, fixedVal));                        
        }
    }
    
    public class FullUrlFromEncodedRouteParamAttribute : Attribute, IParameterModelConvention
    {
        public string Name { get; set; }
    
        public void Apply(ParameterModel parameter)
        {
            parameter.BindingInfo = parameter.BindingInfo ?? new BindingInfo();
            parameter.BindingInfo.BinderModelName = Name;
            parameter.BindingInfo.BindingSource = BindingSource.Path;
            parameter.BindingInfo.BinderType = typeof(FullUrlFromUrlEncodedPathSegmentModelBinder);         
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 更好的方法可能是更新您的 api,这样您甚至不使用图像密钥中的协议部分。当您需要渲染完整图像 url 时,这将允许您将正确的协议添加到完整图像 url,具体取决于它是否需要 http 还是 https(甚至可以从 url 中省略主机)。您甚至不需要担心客户端对图像路径进行 url 编码,您只需像http://localhost:39216/api/repocreator.zoltu.io/images/Zoltu-Logo-Full-Size.png.

恕我直言,我更喜欢第二种方法。如果您确实需要在路由中编码完整的 url,那么至少您有一种在控制器外部以干净的方式实现它的方法。

注意:如果你想在图片url中保留协议部分,看起来静态文件中间件不喜欢它们,所以必须在Startup.configure中的MVC之后添加它,否则会抛出错误。

  • 虽然您的建议可以让我解决眼前的问题(对前端和后端进行更改),但它并没有解决根本问题,即如何在传递之前在 Azure 中*NOT* URL 解码请求它符合我的框架。IIS 的行为违反了 URL 规范,我真的想要一种按照设计的方式使用 URL 编码的方法,并且以一种可跨 Web 服务器移植且灵活的方式(API 用户不必知道国际信息系统)。 (3认同)