Görünüme özgü javascript dosyaları bir ASP.NET MVC uygulamasında nereye yerleştirilir?


96

Görünüme özgü javascript dosyalarını bir ASP.NET MVC uygulamasına koymak için en iyi yer (hangi klasör vb.) Nedir?

Projemi düzenli tutmak için, onları görünümün .aspx dosyalarıyla yan yana koyabilmeyi gerçekten çok isterdim, ancak bunu yaparken ~ / Views'i göstermeden onlara başvurmanın iyi bir yolunu bulamadım / Eylem / klasör yapısı. Bu klasör yapısının ayrıntılarının sızmasına izin vermek gerçekten kötü bir şey mi?

Alternatif olarak bunları ~ / Scripts veya ~ / Content klasörlerine koymak olabilir, ancak küçük bir rahatsızlık çünkü artık dosya adı çatışmaları konusunda endişelenmem gerekiyor. Yine de, eğer "doğru şey" ise, üstesinden gelebileceğim bir rahatsızlık.


2
Bunun için faydalı bölümler buldum. Bakınız: stackoverflow.com/questions/4311783/…
Frison Alexander

1
Bu çılgınca bir soru gibi görünebilir, ancak son derece yararlı bir senaryo, bir sayfanın javascript dosyasını .cshtml altına yerleştirmenizdir. (Örneğin NestIn ile ). Çözüm gezgini etrafında zıplamak zorunda kalmamaya yardımcı olur.
David Sherret

Yanıtlar:


126

Eski soru, ama ben cevabımı başkalarının aramaya gelmesini engellemek istedim.

Ben de görünümler klasörünün altında görünüme özel js / css dosyalarımı istedim ve işte bunu nasıl yaptım:

/ Views'ın kök dizinindeki web.config klasöründe, web sunucusunun dosyaları sunmasını sağlamak için iki bölümü değiştirmeniz gerekir:

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

Ardından, görünüm dosyanızdan beklediğiniz gibi url'lere başvurabilirsiniz:

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

Bu, .js ve .css dosyalarının sunulmasına izin verecek ve başka herhangi bir şeyin sunulmasını yasaklayacaktır.


Teşekkürler davesw. Tam olarak aradığım şey
Bay Bell

1
Bunu yaptığımda httpHandlers'ın pipeline modunda kullanılamayacağı hatasını alıyorum. Sunucuda klasik moda geçmemi istiyor. Sunucunun klasik modu kullanmasını istemediğinde bunu yapmanın doğru yolu nedir?
Bjørn

1
@ BjørnØyvindHalvorsen Web.config'inizde bir veya diğer işleyici bölümünü silebilir veya yapılandırma doğrulamasını kapatabilirsiniz. Buraya bakın
davesw

2
Çalışması için sadece <system.webServer> bölümündeki modlar gerekliydi, <system.web> modları gerekli DEĞİLDİR.
2017

@joedotnot Doğru, yalnızca bir bölüm gereklidir, ancak hangisi web sunucusu yapılandırmanıza bağlıdır. Şu anda çoğu insan eski system.web bölümüne değil system.webServer bölümüne ihtiyaç duyacaktır.
davesw

5

Bunu başarmanın bir yolu, kendinizinkini tedarik etmektir ActionInvoker. Aşağıda bulunan kodu kullanarak kontrol cihazınızın kurucusuna ekleyebilirsiniz:

ActionInvoker = new JavaScriptActionInvoker();

Şimdi, .jsgörünümünüzün yanına bir dosya yerleştirdiğinizde :

görüntü açıklamasını buraya girin

Doğrudan erişebilirsiniz:

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

Kaynak aşağıdadır:

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);
        }
    }
}

Ancak bu iyi bir çözüm gibi görünüyor, ancak harekete geçme süresini etkiliyor mu?
Leandro Soares

İşe yarayabilir, ama ne? Daha az kod yazmak istiyorum, daha çok değil.
joedotnot

1
@joedotk kez daha fazla kod yazmazsanız, sonsuza kadar daha az kod yazarsınız. Bir programcının mantrası, değil mi? :)
Kirk Woll

@KirkWoll. orada anlaşmazlık yok. "Basit bir özellik" olması gereken şey için kutudan çıkmadığını hayal kırıklığına uğrattı. Bu yüzden davesw'nin cevabını tercih ettim (kabul edilen cevap). Ancak kodunuzu paylaştığınız için teşekkürler, başkaları için faydalı olabilir.
2017

@KirkWoll MVC'de yeniyim ve çözümünüzü bir MVC5 sitesinde uygulamaya çalışıyorum. "ActionInvoker = new JavaScriptActionInvoker ()" öğesini nereye yerleştireceğimden veya "kullanacağımdan" emin değilim ??
Drew

3

Davesw'nin önerisini tersine çevirebilir ve yalnızca .cshtml'yi engelleyebilirsiniz.

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

Mükemmel! :) Güzel bir kısa çözüm! Ve çalışıyor! :) Bunun neden varsayılan ayar olmadığı hakkında hiçbir fikrim yok, çünkü görünümlerle ilgili komut dosyalarını gerçek görünümlerle bir arada tutabilmek çok daha iyi. Teşekkürler Vadym.
BruceHill

24
Güzel ve temiz görünse de bu yaklaşımla temkinli davranırdım. Gelecekte, bu uygulama Razor dışındaki görünüm motorlarını içeriyorsa (eski WebForms, Spark vb.), Bunlar sessizce herkese açık olacaktır. Gibi dosyaları da etkiliyor Site.Master. Beyaz
listeye eklemek

@ Arserbin3'e beyaz listenin daha güvenli göründüğüne katılıyorum; aynı zamanda, bir kurumsal uygulamanın View Engine'in birinden diğerine değişme olasılığını da hissedemiyorum. Bunu yapmak için mükemmel bir otomasyon aracı yok. Dönüşümün elle yapılması gerekiyor. Bunu büyük bir Web Uygulaması için yaptım; WebForm viewengine'i Razor'a dönüştürdü ve kabus günlerini hatırlıyorum, birkaç aydır işler burada ve orada çalışmıyordu ... Böyle bir şeyi tekrar yapmayı düşünemiyorum :). Yine de böyle büyük bir değişiklik yapmak zorunda kalırsam, bu tür web.config ayar değişikliklerinin unutulmayacağına inanıyorum.
Emran Hussain

1

Bunun oldukça eski bir konu olduğunu biliyorum, ancak eklemek istediğim birkaç şey var. Davesw'nin cevabını denedim ama betik dosyalarını yüklemeye çalışırken 500 hatası veriyordu, bu yüzden bunu web.config dosyasına eklemem gerekiyordu:

<validation validateIntegratedModeConfiguration="false" />

system.webServer'a. İşte sahip olduğum şey ve onu çalıştırmayı başardım:

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

Doğrulama hakkında daha fazla bilgi: https://www.iis.net/configreference/system.webserver/validation


0

bu kodu web.config dosyasına system.web etiketinin içine ekleyin

<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

Ayrıca bir görünümle ilgili js dosyalarını görünümle aynı klasöre yerleştirmek istedim.

Bu iş parçacığındaki diğer çözümlerin çalışmasını sağlayamadım, bozuk oldukları için değil ama MVC'de onları çalıştırmak için çok yeniyim.

Burada verilen bilgileri ve diğer birkaç yığını kullanarak şu özelliklere sahip bir çözüm buldum:

  • Javascript dosyasının ilişkili olduğu görünümle aynı dizine yerleştirilmesine izin verir.
  • Komut dosyası URL'leri, temeldeki fiziksel site yapısını vermez
  • Komut dosyası URL’lerinin sonda eğik çizgi (/) ile bitmesi gerekmez
  • Statik kaynaklara müdahale etmez, örneğin: /Scripts/someFile.js hala çalışıyor
  • RunAllManagedModulesForAllRequests'in etkinleştirilmesini gerektirmez.

Not: Ayrıca HTTP Öznitelik Yönlendirme kullanıyorum. Benim ruhumda kullanılan rotanın, bunu etkinleştirmeden çalışacak şekilde değiştirilebilmesi mümkündür.

Aşağıdaki örnek dizin / dosya yapısı göz önüne alındığında:

Controllers
-- Example
   -- ExampleController.vb

Views
-- Example
   -- Test.vbhtml
   -- Test.js

Aşağıda verilen yapılandırma adımlarını kullanarak, yukarıdaki örnek yapıyla birlikte, test görünümü URL'sine şu yolla erişilebilir: /Example/Test ve javascript dosyasına şu yolla başvurulur:/Example/Scripts/test.js

Adım 1 - Öznitelik Yönlendirmeyi Etkinleştirin:

/App_start/RouteConfig.vb dosyanızı düzenleyin ve routes.MapMvcAttributeRoutes()mevcut rotaların hemen üstüne ekleyin.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. Adım - Sitenizi, /{controller}/Scripts/*.js'yi statik bir kaynak değil, MVC yolu olarak ele alacak ve işleyecek şekilde yapılandırın

Aşağıdakileri dosyanın system.webServer -> işleyiciler bölümüne ekleyerek /Web.config dosyanızı düzenleyin:

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

İşte yine bağlamla:

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

Adım 3 - Aşağıdaki komut dosyası eylem sonucunu Denetleyici dosyanıza ekleyin

  • Yol yolunu denetleyicinin {controller} adıyla eşleşecek şekilde düzenlediğinizden emin olun, bu örnek için: <Route (" Example / Scripts / {filename}")>
  • Bunu Kontrolör dosyalarınızın her birine kopyalamanız gerekecek. İsterseniz, muhtemelen bunu tek, tek seferlik bir rota yapılandırması olarak yapmanın bir yolu vardır.

        ' /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
    

Bağlam için, bu benim ExampleController.vb dosyam:

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

Son Notlar test.vbhtml view / test.js javascript dosyalarıyla ilgili özel bir şey yoktur ve burada gösterilmez.

CSS'mi görünüm dosyasında tutuyorum, ancak CSS dosyalarınıza benzer bir şekilde başvurabilmeniz için bu çözüme kolayca ekleyebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.