【问题标题】:Where to put view-specific javascript files in an ASP.NET MVC application?将特定于视图的 javascript 文件放在 ASP.NET MVC 应用程序中的什么位置?
【发布时间】:2009-03-03 02:30:14
【问题描述】:

在 ASP.NET MVC 应用程序中放置特定于视图的 javascript 文件的最佳位置是什么(哪个文件夹等)?

为了使我的项目井井有条,我真的很希望能够将它们与视图的 .aspx 文件并排放置,但是在这样做时我还没有找到一种很好的方法来引用它们而不暴露~/Views/Action/ 文件夹结构。让那个文件夹结构的细节泄露真的是一件坏事吗?

另一种方法是将它们放在 ~/Scripts 或 ~/Content 文件夹中,但这有点烦人,因为现在我不得不担心文件名冲突。不过,如果这是“正确的事情”,我可以克服这种烦恼。

【问题讨论】:

  • 我发现部分对此有用。见:stackoverflow.com/questions/4311783/…
  • 这听起来像是一个疯狂的问题,但一个非常有用的场景是当您将页面的 javascript 文件嵌套在 .cshtml 下时。 (例如,NestIn)。它有助于不必在解决方案资源管理器中跳来跳去。

标签: asp.net-mvc


【解决方案1】:

老问题,但我想把我的答案写出来,以防其他人来找它。

我也想在 views 文件夹下查看特定于视图的 js/css 文件,我是这样做的:

在 /Views 根目录的 web.config 文件夹中,您需要修改两个部分以使网络服务器能够提供文件:

    <system.web>
        <httpHandlers>
            <add path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
            <add path="*.css" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
            <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
        </httpHandlers>
        <!-- other content here -->
    </system.web>

    <system.webServer>
        <handlers>
            <remove name="BlockViewHandler"/>
            <add name="JavaScript" path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
            <add name="CSS" path="*.css" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
            <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
        </handlers>
        <!-- other content here -->
    </system.webServer>

然后从您的视图文件中,您可以像您期望的那样引用网址:

@Url.Content("~/Views/<ControllerName>/somefile.css")

这将允许提供 .js 和 .css 文件,并禁止提供任何其他文件。

【讨论】:

  • 谢谢,戴夫斯。正是我想要的
  • 当我这样做时,我得到了 httpHandlers 不能在管道模式下使用的错误。它要我切换到服务器上的经典模式。当不希望服务器使用经典模式时,正确的做法是什么?
  • @BjørnØyvindHalvorsen 您可以删除一个或另一个处理程序部分或关闭 web.config 中的配置验证。 See here
  • 只有 部分的模块才能工作,不需要 模块。
  • @joedotnot 正确,只需要一节,但哪一节取决于您的网络服务器配置。目前大多数人需要 system.webServer 部分,而不是旧的 system.web 部分。
【解决方案2】:

实现此目的的一种方法是提供您自己的ActionInvoker。使用下面包含的代码,您可以添加到控制器的构造函数中:

ActionInvoker = new JavaScriptActionInvoker();

现在,每当您在视图旁边放置 .js 文件时:

您可以直接访问它:

http://yourdomain.com/YourController/Index.js

以下是出处:

namespace JavaScriptViews {
    public class JavaScriptActionDescriptor : ActionDescriptor
    {
        private string actionName;
        private ControllerDescriptor controllerDescriptor;

        public JavaScriptActionDescriptor(string actionName, ControllerDescriptor controllerDescriptor)
        {
            this.actionName = actionName;
            this.controllerDescriptor = controllerDescriptor;
        }

        public override object Execute(ControllerContext controllerContext, IDictionary<string, object> parameters)
        {
            return new ViewResult();
        }

        public override ParameterDescriptor[] GetParameters()
        {
            return new ParameterDescriptor[0];
        }

        public override string ActionName
        {
            get { return actionName; }
        }

        public override ControllerDescriptor ControllerDescriptor
        {
            get { return controllerDescriptor; }
        }
    }

    public class JavaScriptActionInvoker : ControllerActionInvoker
    {
        protected override ActionDescriptor FindAction(ControllerContext controllerContext, ControllerDescriptor controllerDescriptor, string actionName)
        {
            var action = base.FindAction(controllerContext, controllerDescriptor, actionName);
            if (action != null)
            {
                return action;
            } 

            if (actionName.EndsWith(".js"))
            {
                return new JavaScriptActionDescriptor(actionName, controllerDescriptor);
            }

            else 
                return null;
        }
    }

    public class JavaScriptView : IView
    {
        private string fileName;

        public JavaScriptView(string fileName)
        {
            this.fileName = fileName;
        }

        public void Render(ViewContext viewContext, TextWriter writer)
        {
            var file = File.ReadAllText(viewContext.HttpContext.Server.MapPath(fileName));
            writer.Write(file);
        }
    }


    public class JavaScriptViewEngine : VirtualPathProviderViewEngine
    {
        public JavaScriptViewEngine()
            : this(null)
        {
        }

        public JavaScriptViewEngine(IViewPageActivator viewPageActivator)
            : base()
        {
            AreaViewLocationFormats = new[]
            {
                "~/Areas/{2}/Views/{1}/{0}.js",
                "~/Areas/{2}/Views/Shared/{0}.js"
            };
            AreaMasterLocationFormats = new[]
            {
                "~/Areas/{2}/Views/{1}/{0}.js",
                "~/Areas/{2}/Views/Shared/{0}.js"
            };
            AreaPartialViewLocationFormats = new []
            {
                "~/Areas/{2}/Views/{1}/{0}.js",
                "~/Areas/{2}/Views/Shared/{0}.js"
            };
            ViewLocationFormats = new[]
            {
                "~/Views/{1}/{0}.js",
                "~/Views/Shared/{0}.js"
            };
            MasterLocationFormats = new[]
            {
                "~/Views/{1}/{0}.js",
                "~/Views/Shared/{0}.js"
            };
            PartialViewLocationFormats = new[]
            {
                "~/Views/{1}/{0}.js",
                "~/Views/Shared/{0}.js"
            };
            FileExtensions = new[]
            {
                "js"
            };
        }

        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            if (viewName.EndsWith(".js"))
                viewName = viewName.ChopEnd(".js");
            return base.FindView(controllerContext, viewName, masterName, useCache);
        }


        protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
        {
            return new JavaScriptView(partialPath);
        }

        protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
        {
            return new JavaScriptView(viewPath);
        }
    }
}

【讨论】:

  • 这似乎是一个不错的解决方案,但是它会影响调用时间吗?
  • 它可能工作得很好,但是什么?我想写更少的代码,而不是更多。
  • @joedot 不是您一次编写更多代码,而永远编写更少代码。程序员的口头禅,不是吗? :)
  • @KirkWoll。那里没有分歧。只是对应该是“简单功能”的东西感到失望,它并没有开箱即用。所以我更喜欢选择 davesw 的答案(接受的答案)。但是感谢您分享您的代码,它可能对其他人有用。
  • @KirkWoll 我是 MVC 新手,我正在尝试在 MVC5 站点中实施您的解决方案。我不确定在哪里放置或“使用”“ActionInvoker = new JavaScriptActionInvoker()”??
【解决方案3】:

您可以颠倒 davesw 的建议并仅阻止 .cshtml

<httpHandlers>
    <add path="*.cshtml" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>

【讨论】:

  • 完美! :) 一个不错的简短解决方案!它有效! :) 我不知道为什么这不是默认设置,因为能够将与视图相关的脚本与实际视图一起保存会更好。谢谢,瓦迪姆。
  • 我会谨慎使用这种方法,即使它看起来很干净。如果将来此应用程序包含 Razor 以外的视图引擎(例如 WebForms、Spark 等),它们将默默地公开。还会影响Site.Master 等文件。白名单似乎是更安全的方法
  • 我同意@arserbin3 的观点,白名单似乎更安全;同时,我感觉不到企业应用程序的视图引擎从一个变为另一个的可能性。没有完美的自动化工具可以做到这一点。转换需要手动完成。一旦我为大型 Web 应用程序这样做;将 WebForm 视图引擎转换为 Razor,我记得噩梦般的日子,几个月来,事情在这里和那里都没有工作......不能再考虑做这样的事情了:)。如果我无论如何都要做这么大的改变,那么,我相信这样的 web.config 设置改变不会被遗忘。
【解决方案4】:

我知道这是一个相当老的话题,但我有几件事想补充。我尝试了 davesw 的回答,但在尝试加载脚本文件时抛出 500 错误,所以我不得不将其添加到 web.config:

<validation validateIntegratedModeConfiguration="false" />

到 system.webServer。这是我所拥有的,我能够让它工作:

<system.webServer>
  <handlers>
    <remove name="BlockViewHandler"/>
    <add name="JavaScript" path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
    <add name="CSS" path="*.css" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
    <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
  </handlers>
  <validation validateIntegratedModeConfiguration="false" />
</system.webServer>
<system.web>
  <compilation>
    <assemblies>
      <add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </assemblies>
  </compilation>
  <httpHandlers>
      <add path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
      <add path="*.css" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
      <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
  </httpHandlers>
</system.web>

这里有更多关于验证的信息:https://www.iis.net/configreference/system.webserver/validation

【讨论】:

    【解决方案5】:

    将此代码添加到 system.web 标签内的 web.config 文件中

    <handlers>
        <remove name="BlockViewHandler"/>
        <add name="JavaScript" path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
        <add name="CSS" path="*.css" verb="GET,HEAD" type="System.Web.StaticFileHandler" />
         <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
    </handlers>
    

    【讨论】:

      【解决方案6】:

      我还想把与视图相关的js文件放在与视图相同的文件夹中。

      我无法让该线程中的其他解决方案正常工作,并不是说它们坏了,而是我对 MVC 太陌生,无法让它们正常工作。

      使用此处提供的信息和其他几个堆栈,我想出了一个解决方案:

      • 允许将 javascript 文件放置在与其关联的视图相同的目录中。
      • 脚本 URL 不会泄露底层物理站点结构
      • 脚本 URL 不必以斜杠 (/) 结尾
      • 不干扰静态资源,例如:/Scripts/someFile.js 仍然 有效
      • 不需要启用 runAllManagedModulesForAllRequests。

      注意:我也在使用 HTTP 属性路由。在我的灵魂中使用的路线可能会被修改为在不启用此功能的情况下工作。

      给定以下示例目录/文件结构:

      Controllers
      -- Example
         -- ExampleController.vb
      
      Views
      -- Example
         -- Test.vbhtml
         -- Test.js
      

      使用下面给出的配置步骤,结合上面的示例结构,测试视图 URL 将通过:/Example/Test 访问,javascript 文件将通过:/Example/Scripts/test.js 引用

      第 1 步 - 启用属性路由:

      编辑您的 /App_start/RouteConfig.vb 文件并在现有 routes.MapRoute 上方添加 routes.MapMvcAttributeRoutes()

      Imports System
      Imports System.Collections.Generic
      Imports System.Linq
      Imports System.Web
      Imports System.Web.Mvc
      Imports System.Web.Routing
      
      Public Module RouteConfig
          Public Sub RegisterRoutes(ByVal routes As RouteCollection)
              routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
      
              ' Enable HTTP atribute routing
              routes.MapMvcAttributeRoutes()
      
              routes.MapRoute(
                  name:="Default",
                  url:="{controller}/{action}/{id}",
                  defaults:=New With {.controller = "Home", .action = "Index", .id = UrlParameter.Optional}
              )
          End Sub
      End Module
      

      第 2 步 - 配置您的网站以将 /{controller}/Scripts/*.js 视为 MVC 路径而非静态资源

      编辑您的 /Web.config 文件,将以下内容添加到文件的 system.webServer --> handlers 部分:

      <add name="ApiURIs-ISAPI-Integrated-4.0" path="*/scripts/*.js" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
      

      这里又是上下文:

        <system.webServer>
          <modules>
            <remove name="TelemetryCorrelationHttpModule"/>
            <add name="TelemetryCorrelationHttpModule" type="Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule, Microsoft.AspNet.TelemetryCorrelation" preCondition="managedHandler"/>
            <remove name="ApplicationInsightsWebTracking"/>
            <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler"/>
          </modules>
          <validation validateIntegratedModeConfiguration="false"/>
          <handlers>
            <remove name="ExtensionlessUrlHandler-Integrated-4.0"/>
            <remove name="OPTIONSVerbHandler"/>
            <remove name="TRACEVerbHandler"/>
            <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/>
            <add name="ApiURIs-ISAPI-Integrated-4.0" path="*/scripts/*.js" verb="GET" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
          </handlers>
        </system.webServer>  
      

      第 3 步 - 将以下脚本操作结果添加到您的 Controller 文件中

      • 确保编辑路由路径以匹配 {controller} 名称 控制器,对于这个例子,它是: 示例/Scripts/{filename}")>
      • 您需要将其复制到您的每个控制器文件中。如果您愿意,可能有一种方法可以以某种方式将其作为单一的、一次性的路由配置来完成。

            ' /Example/Scripts/*.js
            <Route("Example/Scripts/{filename}")>
            Function Scripts(filename As String) As ActionResult
                ' ControllerName could be hardcoded but doing it this way allows for copy/pasting this code block into other controllers without having to edit
                Dim ControllerName As String = System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values("controller").ToString()
        
                ' the real file path
                Dim filePath As String = Server.MapPath("~/Views/" & ControllerName & "/" & filename)
        
                ' send the file contents back
                Return Content(System.IO.File.ReadAllText(filePath), "text/javascript")
            End Function
        

      对于上下文,这是我的 ExampleController.vb 文件:

      Imports System.Web.Mvc
      
      Namespace myAppName
          Public Class ExampleController
              Inherits Controller
      
              ' /Example/Test
              Function Test() As ActionResult
                  Return View()
              End Function
      
      
              ' /Example/Scripts/*.js
              <Route("Example/Scripts/{filename}")>
              Function Scripts(filename As String) As ActionResult
                  ' ControllerName could be hardcoded but doing it this way allows for copy/pasting this code block into other controllers without having to edit
                  Dim ControllerName As String = System.Web.HttpContext.Current.Request.RequestContext.RouteData.Values("controller").ToString()
      
                  ' the real file path
                  Dim filePath As String = Server.MapPath("~/Views/" & ControllerName & "/" & filename)
      
                  ' send the file contents back
                  Return Content(System.IO.File.ReadAllText(filePath), "text/javascript")
              End Function
      
      
          End Class
      End Namespace
      

      最后说明 test.vbhtml 视图/test.js javascript 文件没有什么特别之处,此处未显示。

      我将我的 CSS 保存在视图文件中,但您可以轻松地添加到此解决方案中,以便您可以以类似的方式引用您的 CSS 文件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-08-08
        • 1970-01-01
        • 1970-01-01
        • 2021-06-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多