我在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设置。
答案 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之后添加它,否则它会抛出错误。