User Agent Causes MVC DisplayFor ArgumentException: Illegal characters in path
asp.net, asp.net-mvc, c#, user-agent
Solution
I had the exact same problem, and fixed it.
My problem turned out to be the use of a `yield` block in my viewmodel:
Controller:
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);
}
View:
@model BigVM
@Html.DisplayFor(x => x.SmallVMs) <-- died
Inexplicably, this worked for desktops but failed for iPads and iPhones, citing the exact same stacktrace. Similar problems were reported here and here. The problem was solved by adding a `.ToList()` call, thus:
var vm = new BigVM {
SmallVMs = BuildSmallOnes().ToList()
};
Presumably the class that the compiler generates to represent the yield block includes some characters that some User Agents just don't like. Including the ToList() call uses a List<> instead.
Problem
I'm having a problem where users on mobile devices are encountering an error in MVC that does not occur when viewing the site on a regular desktop. I can consistently reproduce the error by using Chrome's developer tools and applying any other UA than the default. The underlying exception thrown is: `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) at System.Web.Mvc.Html.TemplateHelpers.TemplateHelper(HtmlHelper html, ModelMetadata metadata, String htmlFieldName, String templateName, DataBoundControlMode mode, Object additionalViewData) at System.Web.Mvc.Html.TemplateHelpers.TemplateFor[TContainer,TValue](HtmlHelper'1 html, Expression'1 expression, String templateName, String htmlFieldName, DataBoundControlMode mode, Object additionalViewData, TemplateHelperDelegate templateHelper) at System.Web.Mvc.Html.TemplateHelpers.TemplateFor[TContainer,TValue](HtmlHelper'1 html, Expression'1 expression, String templateName, String htmlFieldName, DataBoundControlMode mode, Object additionalViewData) at System.Web.Mvc.Html.DisplayExtensions.DisplayFor[TModel,TValue](HtmlHelper'1 html, Expression'1 expression)` Using fiddler, the only difference in requests when comparing a successful one to a failed request is the User-Agent (and the cache buster appended by jQuery as part of the query string parameters). Why is only changing the UA causing this exception and how can I avoid this issue without writing a specific work around into the system for every place that this is and could occur?