ビュー固有のJavaScriptファイルをASP.NET MVCアプリケーションのどこに配置しますか?


96

ビュー固有のJavaScriptファイルをASP.NET MVCアプリケーションに配置するのに最適な場所(どのフォルダーなど)ですか?

私のプロジェクトを整理しておくために、ビューの.aspxファイルと並べて配置できるようにしたいのですが、〜/ Viewsを公開せずにそれらを参照する方法を見つけられませんでした/ Action /フォルダー構造。そのフォルダ構造の詳細を漏らすのは本当に悪いことですか?

代わりに、〜/ Scriptsまたは〜/ Contentフォルダーに配置することもできますが、ファイル名の競合を心配する必要があるため、少し気になります。それが「正しいこと」だとしたら、私は乗り越えられる苛立ちです。


2
これに役立つセクションが見つかりました。参照:stackoverflow.com/questions/4311783/…–
Frison Alexander

1
これはおかしな質問のように聞こえますが、非常に便利なシナリオは、.cshtmlの下にページのJavaScriptファイルをネストする場合です。(たとえば、NestInを使用)。これは、ソリューションエクスプローラーを跳ね回る必要がないのに役立ちます。
David Sherret、2014年

回答:


126

古い質問ですが、他の誰かがそれを探しに来た場合に備えて、私の答えを出したいと思いました。

私もビューフォルダーの下にビュー固有のjs / cssファイルが必要でした。

/ Viewsのルートにあるweb.configフォルダーで、2つのセクションを変更して、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

2
セクション<system.webServer>のmodのみが機能するために必要でしたが、<system.web> modsは必要ありませんでした。
joedotnot

@joedotnot正解です。必要なセクションは1つだけですが、どちらのセクションがウェブサーバーの構成に依存するかを確認してください。現在、ほとんどの人は、古いsystem.webセクションではなく、system.webServerセクションを必要としています。
davesw

5

これを達成する1つの方法は、独自に供給すること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 2016年

それはうまくいくかもしれませんが、何ですか?私はより多くではなく、より少ないコードを書きたいです。
joedotnot

1
@joedotnotあなたは一度より多くのコードを書き、永遠に少ないコードを書きます。プログラマーのマントラですね。:)
Kirk Woll

@KirkWoll。そこに意見の相違はありません。「単純な機能」とすべきものについては、箱から出していないことにがっかりしただけです。だから私はdaveswの答え(受け入れられた答え)を選ぶことを好んだ。しかし、あなたのコードを共有してくれてありがとう、それは他の人に役立つかもしれません。
joedotnot

@KirkWoll私はMVCを初めて使用し、MVC5サイトにソリューションを実装しようとしています。「ActionInvoker = new JavaScriptActionInvoker()」を配置または「使用」する場所がわかりません??
ドリュー

3

daveswの提案を逆にして、.cshtmlのみをブロックすることができます

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

パーフェクト!:)素敵な短い解決策!そしてそれはうまくいきます!:)これがデフォルト設定ではない理由がわかりません。実際のビューと一緒にビューに関連するスクリプトを保持できる方がはるかに良いためです。ありがとう、Vadym。
BruceHill

24
私はこのアプローチに慎重になるでしょう。将来的に、このアプリにRazor以外のビューエンジン(例:WebForms、Sparkなど)が含まれている場合、それらは黙って公開されます。のようなファイルにも影響しますSite.Master。ホワイトリストはより安全なアプローチのようです
arserbin3

ホワイトリストのほうが安全に見えるという@ 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()、既存のルートのすぐ上に追加します。MapRoute:

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->ハンドラーセクションに以下を追加します。

<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}名と一致するようにしてください。この例では、次のようになります。<Route( " Example / Scripts / {filename}")>
  • これを各コントローラーファイルにコピーする必要があります。必要に応じて、これを単一の1回限りのルート構成として何らかの方法で実行する方法がおそらくあります。

        ' /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 view / test.js javascriptファイルについて特別なことは何もないため、ここには示されていません。

私はCSSをビューファイルに保持していますが、同じ方法でCSSファイルを参照できるように、このソリューションに簡単に追加できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.