ASP.NET MVC 응용 프로그램에서보기 별 javascript 파일을 어디에 배치합니까?


96

ASP.NET MVC 응용 프로그램에서보기 별 javascript 파일을 배치하는 가장 좋은 위치 (폴더 등)는 무엇입니까?

내 프로젝트를 체계적으로 유지하기 위해 뷰의 .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 폴더에서 웹 서버가 파일을 제공 할 수 있도록 두 섹션을 수정해야합니다.

    <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 2015-04-21

2
작동하는 데 <system.webServer> 섹션의 모드 만 필요했고 <system.web> 모드는 필요하지 않았습니다.
joedotnot

@joedotnot 맞습니다. 하나의 섹션 만 필요하지만 웹 서버 구성에 따라 다릅니다. 현재 대부분의 사람들은 이전 system.web 섹션이 아닌 system.webServer 섹션이 필요합니다.
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 한 번 더 많은 코드를 작성하지 않고 영원히 더 적은 코드를 작성하십시오. 프로그래머의 진언, 아니? :)
커크 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 2012

24
이 접근 방식이 멋지고 깨끗해 보이지만 조심할 것입니다. 앞으로이 앱에 Razor 이외의보기 엔진 (예 : WebForms, Spark 등)이 포함되는 경우 자동으로 공개됩니다. 또한 Site.Master. 화이트리스트는 더 안전한 접근 방식으로 보입니다
arserbin3 2013-09-24

화이트리스트가 더 안전 해 보인다는 @ arserbin3에 동의합니다. 동시에 엔터프라이즈 애플리케이션의 View Engine이 서로 변경 될 가능성을 느낄 수 없습니다. 이를위한 완벽한 자동화 도구는 없습니다. 변환은 손으로해야합니다. 일단 큰 웹 애플리케이션을 위해 그렇게했습니다. WebForm viewengine을 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} 이름과 일치하도록 경로 경로를 편집해야합니다.이 예에서는 다음과 같습니다. <Route ( " Example / 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 view / test.js javascript 파일에는 특별한 것이 없으며 여기에 표시되지 않습니다.

내 CSS를보기 파일에 보관하지만이 솔루션에 쉽게 추가하여 비슷한 방식으로 CSS 파일을 참조 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.