Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC Child Action throws Illegal Characters in Path

I'm getting an unexpected exception when my url contains a quote character when my view contains a child action.

http://mysite.info/home/index/"

When the view for this action does not contain a child action everything works fine. When it does contain a child action (e.g. @Html.Action("Menu")) I get the exception "System.ArgumentException: Illegal characters in path" at the call to Html.Action.

Looking at this post the double quote character is not one of the default invalid characters. It seems to me that an action with or without a child action should behave the same. Either a double quote is valid or it is not.

In addition, I'm not sure how best to work around this seeming double standard. I'm hesitant to change the illegal characters list to contain " (the defaults are the defaults for a reason). Child actions are very useful so I don't want to not use them. Putting a try catch around every child action is hacky.

I'm not actively trying to use quotes in my routes, but if " is not in the illegal characters list then it shouldn't result in an exception, right?

Example

Controller:

public class HomeController : Controller
{
    public ActionResult WithChildAction() // throws exception with quote in path
    {
        return View();
    }

    public ActionResult WithoutChildAction() // works with quote in path
    {
        return View();
    }

    public ActionResult ChildAction()
    {
        return View();
    }
}

WithChildAction.cshtml:

<h2>With Child Action</h2>

@Html.Action("ChildAction")

WithoutChildAction.cshtml:

<h2>Without Child Action</h2>

ChildAction.cshtml:

<h2>Child Action</h2>

Stack Trace

[ArgumentException: Illegal characters in path.]
   System.IO.Path.CheckInvalidPathChars(String path, Boolean checkAdditional) +11113918
   System.Security.Permissions.FileIOPermission.CheckIllegalCharacters(String[] str) +30
   System.Security.Permissions.FileIOPermission.AddPathList(FileIOPermissionAccess access, AccessControlActions control, String[] pathListOrig, Boolean checkForDuplicates, Boolean needFullPath, Boolean copyPathList) +97
   System.Security.Permissions.FileIOPermission..ctor(FileIOPermissionAccess access, String path) +63
   System.Web.InternalSecurityPermissions.PathDiscovery(String path) +29
   System.Web.HttpRequest.MapPath(VirtualPath virtualPath, VirtualPath baseVirtualDir, Boolean allowCrossAppMapping) +149
   System.Web.HttpRequest.MapPath(VirtualPath virtualPath) +33
   System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm, Boolean setPreviousPage) +44
   System.Web.HttpServerUtility.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm) +28
   System.Web.HttpServerUtilityWrapper.Execute(IHttpHandler handler, TextWriter writer, Boolean preserveForm) +19
   System.Web.Mvc.Html.ChildActionExtensions.ActionHelper(HtmlHelper htmlHelper, String actionName, String controllerName, RouteValueDictionary routeValues, TextWriter textWriter) +461
   System.Web.Mvc.Html.ChildActionExtensions.Action(HtmlHelper htmlHelper, String actionName, String controllerName, RouteValueDictionary routeValues) +83
   System.Web.Mvc.Html.ChildActionExtensions.Action(HtmlHelper htmlHelper, String actionName, Object routeValues) +29
   ASP._Page_Areas_Site_Views_Content_File_cshtml.Execute() in c:\inetpub\Websites\MyWebsite\Source\Areas\Site\Views\Content\File.cshtml:22
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +199
   System.Web.Mvc.WebViewPage.ExecutePageHierarchy() +104
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +78
   System.Web.Mvc.RazorView.RenderView(ViewContext viewContext, TextWriter writer, Object instance) +235
   System.Web.Mvc.BuildManagerCompiledView.Render(ViewContext viewContext, TextWriter writer) +107
   System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +291
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +13
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +56
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilterRecursive(IList`1 filters, Int32 filterIndex, ResultExecutingContext preContext, ControllerContext controllerContext, ActionResult actionResult) +420
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +52
   System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +173
   System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +100
   System.Web.Mvc.Async.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult) +10
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +49
   System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +27
   System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +13
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +36
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +54
   System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +39
   System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller) +12
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +28
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +54
   System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +29
   System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult) +10
   System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +21
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult) +36
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.End() +54
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +31
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +9690164
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +155
like image 547
jdehlin Avatar asked Apr 07 '14 18:04

jdehlin


2 Answers

Found one potential solution by setting the following in web.config:

<httpRuntime relaxedUrlToFileSystemMapping="true"/>

This seems dangerous so I'm just going to ignore the errors.

like image 79
jdehlin Avatar answered Nov 16 '22 23:11

jdehlin


Html.Action has a check that processes the Current Route to see if it is in an Area. This process returns accesses the Virtual Path (File System). Quotes aren't allowed in the Filesystem and thus throw the error.

bool usingAreas;
VirtualPathData vpd = htmlHelper.RouteCollection
    .GetVirtualPathForArea(htmlHelper.ViewContext.RequestContext, null /* name */, routeValues, out usingAreas);

With the RouteCollection Extensions code here:

http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/RouteCollectionExtensions.cs

you may be able to handle the RouteValues and set the area name to avoid the error, but not allowing the invalid file system characters in the first place would be ideal.

like image 38
neehouse Avatar answered Nov 16 '22 22:11

neehouse