用户代理导致MVC DisplayFor抛出ArgumentException: 路径中有非法字符

14

我遇到了一个问题,移动设备上的用户在 MVC 中遇到了一个错误,但在桌面电脑上查看网站时不会出现此问题。我可以通过使用 Chrome 的开发者工具并应用除默认 UA 以外的任何其他 UA 来不断重现该错误。

抛出的底层异常为:ArgumentException: Illegal characters in path. 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.Mvc.VirtualPathProviderViewEngine.GetPathFromGeneralName(ControllerContext controllerContext, List'1 locations, String name, String controllerName, String areaName, String cacheKey, String[]& searchedLocations) at System.Web.Mvc.VirtualPathProviderViewEngine.GetPath(ControllerContext controllerContext, String[] locations, String[] areaLocations, String locationsPropertyName, String name, String controllerName, String cacheKeyPrefix, Boolean useCache, String[]& searchedLocations) at System.Web.Mvc.VirtualPathProviderViewEngine.FindPartialView(ControllerContext controllerContext, String partialViewName, Boolean useCache) at System.Web.Mvc.ViewEngineCollection.<>c__DisplayClass2.<FindPartialView>b__1(IViewEngine e) at System.Web.Mvc.ViewEngineCollection.Find(Func'2 lookup, Boolean trackSearchedPaths) at System.Web.Mvc.ViewEngineCollection.FindPartialView(ControllerContext controllerContext, String partialViewName) at System.Web.Mvc.Html.TemplateHelpers.ExecuteTemplate(HtmlHelper html, ViewDataDictionary viewData, String templateName, DataBoundControlMode mode, GetViewNamesDelegate getViewNames, GetDefaultActionsDelegate getDefaultActions) at System.Web.Mvc.Html.TemplateHelpers.TemplateHelper(HtmlHelper html, ModelMetadata metadata, String htmlFieldName, String templateName, DataBoundControlMode mode, Object additionalViewData, ExecuteTemplateDelegate executeTemplate)

在使用fiddler进行请求时,仅成功和失败请求之间的User-Agent(以及作为查询字符串参数的一部分附加的缓存破坏器)不同。仅更改UA为什么会导致此异常?如何避免在系统中的每个可能发生此问题的位置编写特定的解决方法?

Note: 保留HTML标记。


你找到解决办法了吗? - Roman Mik
@RomanMik - 我实际上找到了与CSJ类似的解决方法,就是避免在我的视图模型中使用yield块。当我将代码更改为实例化列表而不是使用yield时,问题得到了解决。我仍然不完全理解为什么在.NET中对某些用户代理会出现这个问题,但至少有一种标准的解决方法。 - SignalRichard
我找到了一个与ASP.NET DisplayModeProvider相关的不同解决方案,并在相关的SO文章中分享了https://dev59.com/oVsX5IYBdhLWcg3whv7Q#40229384。 - Mark Nadig
2个回答

11

我曾经遇到过完全相同的问题,并且解决了它。

我的问题最终是由于在我的视图模型中使用了一个 yield 块导致的:

控制器:

var vm = new BigVM {
    SmallVMs = BuildSmallOnes()
};
return View(vm);

private IEnumerable<SmallVM> BuildSmallOnes()
{
    // complex logic
    yield return new SmallVM(1);
    yield return new SmallVM(2);
}

查看:

@model BigVM
@Html.DisplayFor(x => x.SmallVMs)   <-- died

令人费解的是,这在桌面设备上可以工作,但在iPad和iPhone上失败,并引用相同的错误堆栈。类似问题已在这里这里报告过。通过添加.ToList()调用解决了问题,如下所示:

var vm = new BigVM {
    SmallVMs = BuildSmallOnes().ToList()
};

很可能编译器为了表示yield块而生成的类包含某些字符,而这些字符会被一些用户代理程序所不喜欢。使用ToList()调用可以使用List<>替代。


ToList() 对我也解决了问题。仅使用 Where(...) 似乎可以正常工作(因此传递给视图的实际类型是 WhereListIterator<T>),但是在应用 OrderBy(...).Take(...)(因此最终结果是 Enumerable.<TakeIterator>...)后,视图在移动设备上崩溃了。 - Arve Systad
1
我猜这个问题存在于多个IEnumerable<T>类型中; 或许在视图模型中使用IList<T>(从而防止您忘记‘ToList()’)应该成为一个“最佳实践”,以创建编译时安全网络? - Arve Systad
我刚遇到了同样的问题。是由ViewModel Builder使用的IEnumerable<T>和yield引起的。有人知道具体是什么原因导致这个问题吗?这似乎是视图引擎中的一个bug。 - Øyvind
救命稻草。我不知道类名最终会变成路径,这有点疯狂。 - toddmo

0

我相信我们遇到这个问题的原因是由于引入了MultipleViews和DisplayMode提供程序来分别处理移动设备。这些代码仍然存在于.NET 4中,但已被弃用。

可以在此处找到完全禁用功能的解决方案。 根据User-Agent而定的路径中的非法字符?


1
欢迎提供解决方案的链接,但请确保您的答案在没有链接的情况下也是有用的:添加链接周围的上下文,以便您的同行用户了解它是什么以及为什么存在,然后引用您链接到的页面中最相关的部分,以防目标页面不可用。仅仅是一个链接的答案可能会被删除。 - Samuel Liew
当然可以。这实际上是一条旨在提供帮助的评论,但由于我在这里并不活跃,且没有达到所需的声望分数,因此将其留作答案。 - HXavS

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