จะวางไฟล์จาวาสคริปต์เฉพาะมุมมองในแอปพลิเคชัน ASP.NET MVC ได้ที่ไหน


96

ตำแหน่งที่ดีที่สุด (โฟลเดอร์ใด ฯลฯ ) ในการใส่ไฟล์จาวาสคริปต์เฉพาะมุมมองในแอปพลิเคชัน ASP.NET MVC คืออะไร

เพื่อให้โครงการของฉันเป็นระเบียบฉันชอบที่จะวางไว้เคียงข้างกับไฟล์. aspx ของมุมมอง แต่ฉันไม่พบวิธีที่ดีในการอ้างอิงเมื่อทำเช่นนั้นโดยไม่เปิดเผย ~ / Views / การดำเนินการ / โครงสร้างโฟลเดอร์ การปล่อยให้รายละเอียดของโครงสร้างโฟลเดอร์นั้นรั่วไหลเป็นเรื่องเลวร้ายหรือไม่?

อีกทางเลือกหนึ่งคือใส่ไว้ในโฟลเดอร์ ~ / Scripts หรือ ~ / Content แต่จะเป็นการระคายเคืองเล็กน้อยเพราะตอนนี้ฉันต้องกังวลเกี่ยวกับการขัดแย้งของชื่อไฟล์ มันเป็นการระคายเคืองที่ฉันสามารถเอาชนะได้หากเป็น "สิ่งที่ถูกต้อง"


2
ฉันพบว่าส่วนที่มีประโยชน์สำหรับสิ่งนี้ ดู: stackoverflow.com/questions/4311783/…
Frison Alexander

1
สิ่งนี้ฟังดูเหมือนเป็นคำถามที่บ้าคลั่ง แต่สถานการณ์ที่มีประโยชน์อย่างยิ่งคือเมื่อคุณซ้อนไฟล์จาวาสคริปต์ของหน้าไว้ใน. cshtml (ตัวอย่างเช่นกับNestIn ) ช่วยให้ไม่ต้องเด้งไปรอบ ๆ ตัวสำรวจโซลูชัน
David Sherret

คำตอบ:


126

คำถามเก่า แต่ฉันต้องการตอบในกรณีที่มีคนอื่นมาหา

ฉันต้องการมุมมองเฉพาะไฟล์ js / css ของฉันในโฟลเดอร์ views ด้วยและนี่คือวิธีที่ฉันทำ:

ในโฟลเดอร์ web.config ในรูทของ / Views คุณต้องแก้ไขสองส่วนเพื่อเปิดใช้งานเว็บเซิร์ฟเวอร์เพื่อให้บริการไฟล์:

    <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 สิ่งที่ฉันกำลังมองหา
Mr Bell

1
เมื่อฉันทำเช่นนี้ฉันได้รับข้อผิดพลาดที่ไม่สามารถใช้ httpHandlers ในโหมดไปป์ไลน์ มันต้องการให้ฉันเปลี่ยนไปใช้โหมดคลาสสิกบนเซิร์ฟเวอร์ อะไรคือวิธีที่ถูกต้องในการทำเช่นนี้เมื่อไม่ต้องการให้เซิร์ฟเวอร์ใช้โหมดคลาสสิก
Bjørn

1
@ BjørnØyvindHalvorsenคุณสามารถลบหนึ่งหรือส่วนตัวจัดการอื่น ๆ หรือปิดการตรวจสอบการกำหนดค่าใน web.config ของคุณ ดูที่นี่
davesw

2
เฉพาะม็อดของส่วน <system.webServer> เท่านั้นจึงจะทำงานได้ไม่จำเป็นต้องใช้ม็อด <system.web>
joedotnot

@joedot ไม่ถูกต้องต้องการเพียงส่วนเดียว แต่ส่วนใดขึ้นอยู่กับการกำหนดค่าเว็บเซิร์ฟเวอร์ของคุณ ปัจจุบันคนส่วนใหญ่ต้องการส่วน 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 คุณไม่ได้เขียนโค้ดอีกครั้งและโค้ดน้อยลงตลอดไป มนต์ของโปรแกรมเมอร์ไม่? :)
Kirk Woll

@KirkWoll. ไม่มีความขัดแย้งที่นั่น เพียงแค่ผิดหวังที่สิ่งที่ควรจะเป็น "คุณสมบัติง่ายๆ" มันไม่ได้ออกมาจากกล่อง ดังนั้นฉันจึงต้องการเลือกคำตอบของ davesw (คำตอบที่ยอมรับ) แต่ขอขอบคุณที่แบ่งปันรหัสของคุณอาจเป็นประโยชน์สำหรับผู้อื่น
joedotnot

@KirkWoll ฉันยังใหม่กับ MVC และฉันกำลังพยายามใช้โซลูชันของคุณในไซต์ MVC5 ฉันไม่แน่ใจว่าจะวางไว้ที่ใดหรือ "ใช้" "ActionInvoker = new JavaScriptActionInvoker ()" ??
Drew

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 Application ขนาดใหญ่ แปลง WebForm viewengine เป็น Razor และฉันจำวันที่ฝันร้ายได้สองสามเดือนสิ่งต่าง ๆ ไม่ทำงานที่นี่และที่นั่น ... ไม่คิดที่จะทำสิ่งนั้นอีกเลย :) หากฉันต้องทำการเปลี่ยนแปลงขนาดใหญ่เช่นนี้ฉันเชื่อว่าการเปลี่ยนแปลงการตั้งค่า web.config ดังกล่าวจะไม่ถูกลืม
Emran Hussain

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

เพิ่มรหัสนี้ในไฟล์ web.config ภายในแท็ก system.web

<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 Attribute Routing ด้วย เป็นไปได้ว่าเส้นทางที่ใช้ในจิตวิญญาณของฉันสามารถแก้ไขให้ทำงานได้โดยไม่ต้องเปิดใช้งานสิ่งนี้

ระบุไดเร็กทอรี / โครงสร้างไฟล์ตัวอย่างต่อไปนี้:

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}")>
  • คุณจะต้องคัดลอกสิ่งนี้ลงในไฟล์ 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 view / test.js และจะไม่แสดงที่นี่

ฉันเก็บ CSS ไว้ในไฟล์มุมมอง แต่คุณสามารถเพิ่มลงในโซลูชันนี้ได้อย่างง่ายดายเพื่อให้คุณสามารถอ้างอิงไฟล์ CSS ของคุณในลักษณะเดียวกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.