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

时间:2016-05-12 06:32:40

标签: azure iis asp.net-core azure-web-sites asp.net-core-mvc

我在Azure中运行了一个ASP.NET 6 MVC应用程序。我有一个像

这样的动作的控制器
[HttpDelete]
[Route("{image_url}")]
public async Task<IActionResult> RemoveImageUrl([FromRoute(Name = "image_url")] String imageUrlString)

然后我称之为

api/https%3A%2F%2Frepocreator.zoltu.io%2Fimages%2FZoltu-Logo-Full-Size.png"

此应用程序在使用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>

它仍然对URL进行解码,并将解码后的URL传递给没有找到匹配路由的ASP.NET。

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

1 个答案:

答案 0 :(得分:3)

您可以更新路线定义,使其与解码后的图片网址参数匹配。

根据the documentation,在定义路径模板时:

  

您可以使用*字符作为要绑定的路由值名称的前缀   到URI的其余部分。例如,blog / {* slug}将匹配任何URI   以/ blog /开头并且跟随它有任何价值(这会   被分配到段塞路线值。)

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

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

我看到的唯一问题是协议部分被解码为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);         
        }
    }
    
  • 更好的方法可能是更新您的api,因此您甚至不使用图像密钥中的协议部分。这将允许您在需要渲染时将正确的协议添加到完整的图像URL,具体取决于它是否需要http或https(甚至可以从URL中省略主机)。您甚至不必担心在客户端编码图像路径的url,您可以像http://localhost:39216/api/repocreator.zoltu.io/images/Zoltu-Logo-Full-Size.png一样调用它。

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

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