在ASP.NET MVC应用程序中,将视图特定的javascript文件放在哪里?


96

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

为了使我的项目井井有条,我真的很想能够将它们与视图的.aspx文件并排放置,但是在没有暴露〜/ Views的情况下,我没有找到一种引用它们的好方法/ Action /文件夹结构。让该文件夹结构的详细信息泄漏真的是一件坏事吗?

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


2
我发现部分对此有用。请参阅:stackoverflow.com/questions/4311783/...
Frison亚历山大

1
这听起来像是一个疯狂的问题,但是当您将页面的javascript文件嵌套在.cshtml下时,这是一个非常有用的方案。(例如,使用NestIn)。它有助于不必在解决方案资源管理器附近反弹。
David Sherret 2014年

Answers:


126

老问题了,但是我想把答案回答其他人。

我也想在views文件夹下查看特定于js / css的文件,这是我的操作方法:

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

    <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:

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

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


谢谢,davesw。正是我要找的东西
贝尔先生,

1
当我这样做时,我收到错误消息,即不能在管道模式下使用httpHandlers。它希望我在服务器上切换到经典模式。当不希望服务器使用经典模式时,正确的做法是什么?
比约恩2014年

1
@BjørnØyvindHalvorsen您可以删除一个或另一个处理程序部分,或在web.config中关闭配置验证。看到这里
davesw 2015年

2
要使它工作,仅需要<system.webServer>部分的mod,而不需要<system.web> mods。
joedotnot

@joedotnot正确,仅需要一个部分,但是哪个部分取决于您的Web服务器配置。当前,大多数人将需要system.webServer部分,而不是较早的system.web部分。
davesw

5

实现这一目标的一种方法是自行提供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);
        }
    }
}

不过,这似乎是一个不错的解决方案,但这会影响行动呼吁时间吗?
Leandro Soares

它可能很好用,但是什么呢?我想写更少的代码,而不是更多。
joedotnot

1
@joedot,您不会编写更多代码,而永远编写更少的代码。程序员的口号,不是吗?:)
柯克·沃尔

@KirkWoll。那里没有分歧。只是感到失望的是,对于什么应该是“简单功能”,它并没有开箱即用。所以我更喜欢选择davesw的答案(公认的答案)。但是感谢您分享您的代码,它可能对其他人有用。
joedotnot

@KirkWoll我是MVC的新手,我正在尝试在MVC5站点中实施您的解决方案。我不确定“ ActionInvoker = new JavaScriptActionInvoker()”在哪里放置或“使用”?
Drew

3

您可以反转davesw的建议并仅阻止.cshtml

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

完善!:)一个不错的简短解决方案!而且有效!:)我不知道为什么这不是默认设置,因为最好将与视图相关的脚本与实际视图一起保存。谢谢,瓦迪姆。
BruceHill 2012年

24
即使这种方法看起来不错而且干净,我还是会谨慎。如果将来该应用程序包含Razor之外的其他视图引擎(例如WebForms,Spark等),它们将以静默方式公开。也影响文件,例如Site.Master。将白名单列入名单似乎是更安全的方法
arserbin3 2013年

我同意@ arserbin3的观点,白名单似乎更安全;同时,我无法感觉到企业应用程序的View Engine从一个更改为另一个的可能性。没有完美的自动化工具可以做到这一点。转换需要手工完成。一旦我对大型Web应用程序执行了此操作;将WebForm视图引擎转换为Razor,我还记得噩梦般的日子,几个月来,事情在这里和那里都无法正常工作。如果无论如何我都必须进行这样的巨大更改,那么我相信不会忘记这样的web.config设置更改。
Emran Hussain 2015年

1

我知道这是一个比较老的话题,但是我想补充一些内容。我尝试了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


0

将此代码添加到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>

0

我还想将与视图相关的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.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}名称,在本示例中为:<Route(“ Example / Scripts / {filename}”)>
  • 您将需要将此文件复制到每个Controller文件中。如果您愿意,可能有一种方法可以通过某种方式将其作为单一的一次性路由配置来完成。

        ' /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文件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.