“路径中有非法字符。”仅当从移动浏览器查看时
“Illegal characters in path.” only when viewing from mobile browser
我正在使用传统的 WebAPI 控制器:
[Route("api/results/{query}")]
[AcceptVerbs("GET")]
public HttpResponseMessage GetQueryResults(string query)
{
var userAgent = Request.Headers.UserAgent;
var result = _fooService.GetResults(GetUsername(), query);
var response = Request.CreateResponse(HttpStatusCode.OK, result);
return response;
}
GetResults
returns 元素数组,如下所示:
[{
"resultId":2039016,
"text":null,
"dateCreated":"2020-09-10T02:24:36.003",
"targetPlatform":"FooBar"
}]
在大多数浏览器上,这都可以正常工作。我的用户代理 header 看起来像这样:
{Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36}
然而,当我使用设备工具栏从 Chrome 进行调试时,或者当我在 iPhone 上从 Safari 访问我的站点时,我的用户代理发生了变化。在 Chrome 的设备工具栏(移动模拟器)中,它看起来像这样:
{Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Mobile Safari/537.36}
并且在这种情况下,CheckInvalidPathChars
被调用并且针对 JSON:
失败
"type": "System.ArgumentException",
"message": "Illegal characters in path.",
"stackTrace": " at System.IO.Path.CheckInvalidPathChars(String path, Boolean checkAdditional)
at System.IO.Path.GetExtension(String path)
at System.Web.WebPages.DefaultDisplayMode.TransformPath(String virtualPath, String suffix)
at System.Web.WebPages.DefaultDisplayMode.GetDisplayInfo(HttpContextBase httpContext, String virtualPath, Func`2 virtualPathExists)
at System.Web.WebPages.DisplayModeProvider.GetDisplayInfoForVirtualPath(String virtualPath, HttpContextBase httpContext, Func`2 virtualPathExists, IDisplayMode currentDisplayMode, Boolean requireConsistentDisplayMode)
at System.Web.WebPages.WebPageRoute.GetRouteLevelMatch(String pathValue, String[] supportedExtensions, Func`2 virtualPathExists, HttpContextBase context, DisplayModeProvider displayModeProvider)
at System.Web.WebPages.WebPageRoute.MatchRequest(String pathValue, String[] supportedExtensions, Func`2 virtualPathExists, HttpContextBase context, DisplayModeProvider displayModes)
at System.Web.WebPages.WebPageRoute.DoPostResolveRequestCache(HttpContextBase context)
at System.Web.WebPages.WebPageHttpModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)"
我可以通过尝试将序列化 JSON 解析为文件路径来手动重现:
try
{
var isValid = System.IO.Path.GetExtension(jsonString);
}
catch (Exception e)
{
throw e;
}
当然,尝试将序列化的 JSON object 解析为文件会引发错误。为什么 ASP.NET 修改基于用户代理 headers 的解析行为?
我能否以某种方式覆盖 Request
的入站 header 以强制框架实现功能行为?
要清楚 - 我可以从 Chrome(标准)调用控制器,没有任何问题。当我使用来自 Chrome 的相同请求调用控制器时(打开 devtools 并激活移动模拟器),抛出异常。同样,当我在 iPhone 上使用来自 Safari 的完全相同的请求调用它时,抛出异常。在这些情况下,用户代理 header 是自变量 - 因此必须遵循此 header 以某种方式 导致 调用不同的执行路径。对吗?
在执行所有控制器逻辑后抛出异常 - response
在抛出时由控制器返回。
我使用了 Asp.net 核心请找到下面的代码和屏幕截图,假设我完全理解你的问题。
[Route("api/results/{query}")]
[HttpGet]
public IActionResult GetQueryResults(string query)
{
var userAgent = Request.Headers["User-Agent"].ToString();
var result = @"[{\'resultId\':2039016,\'text\':null,\'dateCreated\':\'2020-09-10T02:24:36.003\',\'targetPlatform\':\'FooBar\'}]";
// _fooService.GetResults(GetUsername(), query);
return Ok(new { result = result, userAgent = userAgent });
}
- 这是来自 vscode
的图像截图
- 这是使用 chrome 移动响应
的屏幕截图
- 这是使用 chrome 网络或桌面视图
的屏幕截图
正如@Dipen Shah 指出的那样,解决方案在另一个 post。
Why is ASP.NET modifying parsing behavior based on user agent headers?
在您的 ASP.NET 应用程序 (reference) 中 自动 注册了一个 WebPageHttpModule
。在内部,WebPageHttpModule
尝试根据 显示模式 .
路由请求
// NOTE: Excluded unnecessary code
public sealed class DisplayModeProvider
{
public static readonly string MobileDisplayModeId = "Mobile";
private readonly List<IDisplayMode> _displayModes = new List<IDisplayMode>()
{
(IDisplayMode) new DefaultDisplayMode(DisplayModeProvider.MobileDisplayModeId)
{
ContextCondition = (Func<HttpContextBase, bool>) (context => context.GetOverriddenBrowser().IsMobileDevice)
},
(IDisplayMode) new DefaultDisplayMode()
};
public IList<IDisplayMode> Modes
{
get
{
return (IList<IDisplayMode>) this._displayModes;
}
}
}
您可以在那里看到字符串 Mobile
并根据 IsMobileDevice
检查条件(reference)。这就是为什么上述 post 中的解决方案有意义。了解其内部工作原理后,您还可以执行以下 hack。
public static void RegisterDisplayModes()
{
var displayModes = DisplayModeProvider.Instance.Modes;
var mobileDisplayMode = displayModes.FirstOrDefault(d => d.DisplayModeId == DisplayModeProvider.MobileDisplayModeId);
if (mobileDisplayMode != null)
{
displayModes.Remove(mobileDisplayMode);
}
}
注意,这是基于位于 C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies\
.
的反编译 System.Web.WebPages.dll
我正在使用传统的 WebAPI 控制器:
[Route("api/results/{query}")]
[AcceptVerbs("GET")]
public HttpResponseMessage GetQueryResults(string query)
{
var userAgent = Request.Headers.UserAgent;
var result = _fooService.GetResults(GetUsername(), query);
var response = Request.CreateResponse(HttpStatusCode.OK, result);
return response;
}
GetResults
returns 元素数组,如下所示:
[{
"resultId":2039016,
"text":null,
"dateCreated":"2020-09-10T02:24:36.003",
"targetPlatform":"FooBar"
}]
在大多数浏览器上,这都可以正常工作。我的用户代理 header 看起来像这样:
{Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36}
然而,当我使用设备工具栏从 Chrome 进行调试时,或者当我在 iPhone 上从 Safari 访问我的站点时,我的用户代理发生了变化。在 Chrome 的设备工具栏(移动模拟器)中,它看起来像这样:
{Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Mobile Safari/537.36}
并且在这种情况下,CheckInvalidPathChars
被调用并且针对 JSON:
"type": "System.ArgumentException",
"message": "Illegal characters in path.",
"stackTrace": " at System.IO.Path.CheckInvalidPathChars(String path, Boolean checkAdditional)
at System.IO.Path.GetExtension(String path)
at System.Web.WebPages.DefaultDisplayMode.TransformPath(String virtualPath, String suffix)
at System.Web.WebPages.DefaultDisplayMode.GetDisplayInfo(HttpContextBase httpContext, String virtualPath, Func`2 virtualPathExists)
at System.Web.WebPages.DisplayModeProvider.GetDisplayInfoForVirtualPath(String virtualPath, HttpContextBase httpContext, Func`2 virtualPathExists, IDisplayMode currentDisplayMode, Boolean requireConsistentDisplayMode)
at System.Web.WebPages.WebPageRoute.GetRouteLevelMatch(String pathValue, String[] supportedExtensions, Func`2 virtualPathExists, HttpContextBase context, DisplayModeProvider displayModeProvider)
at System.Web.WebPages.WebPageRoute.MatchRequest(String pathValue, String[] supportedExtensions, Func`2 virtualPathExists, HttpContextBase context, DisplayModeProvider displayModes)
at System.Web.WebPages.WebPageRoute.DoPostResolveRequestCache(HttpContextBase context)
at System.Web.WebPages.WebPageHttpModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStepImpl(IExecutionStep step)
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)"
我可以通过尝试将序列化 JSON 解析为文件路径来手动重现:
try
{
var isValid = System.IO.Path.GetExtension(jsonString);
}
catch (Exception e)
{
throw e;
}
当然,尝试将序列化的 JSON object 解析为文件会引发错误。为什么 ASP.NET 修改基于用户代理 headers 的解析行为?
我能否以某种方式覆盖 Request
的入站 header 以强制框架实现功能行为?
要清楚 - 我可以从 Chrome(标准)调用控制器,没有任何问题。当我使用来自 Chrome 的相同请求调用控制器时(打开 devtools 并激活移动模拟器),抛出异常。同样,当我在 iPhone 上使用来自 Safari 的完全相同的请求调用它时,抛出异常。在这些情况下,用户代理 header 是自变量 - 因此必须遵循此 header 以某种方式 导致 调用不同的执行路径。对吗?
在执行所有控制器逻辑后抛出异常 - response
在抛出时由控制器返回。
我使用了 Asp.net 核心请找到下面的代码和屏幕截图,假设我完全理解你的问题。
[Route("api/results/{query}")]
[HttpGet]
public IActionResult GetQueryResults(string query)
{
var userAgent = Request.Headers["User-Agent"].ToString();
var result = @"[{\'resultId\':2039016,\'text\':null,\'dateCreated\':\'2020-09-10T02:24:36.003\',\'targetPlatform\':\'FooBar\'}]";
// _fooService.GetResults(GetUsername(), query);
return Ok(new { result = result, userAgent = userAgent });
}
- 这是来自 vscode 的图像截图
- 这是使用 chrome 移动响应 的屏幕截图
- 这是使用 chrome 网络或桌面视图 的屏幕截图
正如@Dipen Shah 指出的那样,解决方案在另一个 post。
Why is ASP.NET modifying parsing behavior based on user agent headers?
在您的 ASP.NET 应用程序 (reference) 中 自动 注册了一个 WebPageHttpModule
。在内部,WebPageHttpModule
尝试根据 显示模式 .
// NOTE: Excluded unnecessary code
public sealed class DisplayModeProvider
{
public static readonly string MobileDisplayModeId = "Mobile";
private readonly List<IDisplayMode> _displayModes = new List<IDisplayMode>()
{
(IDisplayMode) new DefaultDisplayMode(DisplayModeProvider.MobileDisplayModeId)
{
ContextCondition = (Func<HttpContextBase, bool>) (context => context.GetOverriddenBrowser().IsMobileDevice)
},
(IDisplayMode) new DefaultDisplayMode()
};
public IList<IDisplayMode> Modes
{
get
{
return (IList<IDisplayMode>) this._displayModes;
}
}
}
您可以在那里看到字符串 Mobile
并根据 IsMobileDevice
检查条件(reference)。这就是为什么上述 post 中的解决方案有意义。了解其内部工作原理后,您还可以执行以下 hack。
public static void RegisterDisplayModes()
{
var displayModes = DisplayModeProvider.Instance.Modes;
var mobileDisplayMode = displayModes.FirstOrDefault(d => d.DisplayModeId == DisplayModeProvider.MobileDisplayModeId);
if (mobileDisplayMode != null)
{
displayModes.Remove(mobileDisplayMode);
}
}
注意,这是基于位于 C:\Program Files (x86)\Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies\
.
System.Web.WebPages.dll