ASP.NET MVC에서 "보기를 검색"할 사용자 지정 위치를 지정할 수 있습니까?


105

내 mvc 프로젝트에 대해 다음 레이아웃이 있습니다.

  • / 컨트롤러
    • /데모
    • / Demo / DemoArea1Controller
    • / 데모 / DemoArea2Controller
    • 기타...
  • /견해
    • /데모
    • /Demo/DemoArea1/Index.aspx
    • /Demo/DemoArea2/Index.aspx

그러나 내가 이것을 가지고있을 때 DemoArea1Controller:

public class DemoArea1Controller : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

일반적인 검색 위치와 함께 "뷰 '인덱스'또는 해당 마스터를 찾을 수 없습니다."오류가 발생합니다.

"Demo"네임 스페이스의 컨트롤러가 "Demo"뷰 하위 폴더에서 검색하도록 지정하려면 어떻게해야합니까?


다음은 Rob Connery의 MVC Commerce 앱에서 가져온 간단한 ViewEngine의 또 다른 샘플입니다. View Engine 코드 및 ViewEngine을 설정하는 Global.asax.cs 코드 : Global.asax.cs 도움이 되기를 바랍니다 .
Robert Dean

답변:


121

WebFormViewEngine을 쉽게 확장하여 찾고자하는 모든 위치를 지정할 수 있습니다.

public class CustomViewEngine : WebFormViewEngine
{
    public CustomViewEngine()
    {
        var viewLocations =  new[] {  
            "~/Views/{1}/{0}.aspx",  
            "~/Views/{1}/{0}.ascx",  
            "~/Views/Shared/{0}.aspx",  
            "~/Views/Shared/{0}.ascx",  
            "~/AnotherPath/Views/{0}.ascx"
            // etc
        };

        this.PartialViewLocationFormats = viewLocations;
        this.ViewLocationFormats = viewLocations;
    }
}

Global.asax.cs에서 Application_Start 메서드를 수정하여보기 엔진을 등록해야합니다.

protected void Application_Start()
{
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new CustomViewEngine());
}

중첩 된 마스터 페이지에서 마스터 페이지의 경로에 어떻게 액세스 할 수 있습니까? CustomViewEngine의 경로 내에서 검색 중첩 된 마스터 페이지 레이아웃 설정에서와 마찬가지로
Drahcir

6
이미 등록 된 엔진 지우기를 건너 뛰고 새 엔진 만 추가하면 viewLocations에 새 엔진 만있는 것이 좋지 않습니까?
Prasanna 2014 년

3
ViewEngines.Engines.Clear ();없이 구현합니다. 모두 잘 작동합니다. * .cshtml을 사용하려면 RazorViewEngine에서 상속해야합니다
KregHEk

컨트롤러에서 "보기 추가"및 "보기로 이동"옵션을 새보기 위치로 연결할 수있는 방법이 있습니까? 저는 Visual Studio 2012를 사용하고 있습니다
Neville Nazerane 2016

@Prasanna에서 언급했듯이 새 위치를 추가하기 위해 기존 엔진을 지울 필요가 없습니다 . 자세한 내용 은 이 답변 을 참조하십시오.
Hooman Bahreini

45

이제 MVC 6 IViewLocationExpander에서는 뷰 엔진을 엉망으로 만들지 않고 인터페이스를 구현할 수 있습니다 .

public class MyViewLocationExpander : IViewLocationExpander
{
    public void PopulateValues(ViewLocationExpanderContext context) {}

    public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context, IEnumerable<string> viewLocations)
    {
        return new[]
        {
            "/AnotherPath/Views/{1}/{0}.cshtml",
            "/AnotherPath/Views/Shared/{0}.cshtml"
        }; // add `.Union(viewLocations)` to add default locations
    }
}

여기서는 {0}대상보기 이름, {1}-컨트롤러 이름 및 {2}-영역 이름입니다.

자신의 위치 목록을 반환하거나 기본값 viewLocations( .Union(viewLocations)) 과 병합 하거나 변경 ( viewLocations.Select(path => "/AnotherPath" + path)) 할 수 있습니다.

MVC에서 사용자 지정보기 위치 확장기를 등록하려면 파일의 ConfigureServices메서드에 다음 줄을 추가 Startup.cs합니다.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<RazorViewEngineOptions>(options =>
    {
        options.ViewLocationExpanders.Add(new MyViewLocationExpander());
    });
}

3
나는 이것을 10 표로 투표 할 수 있기를 바랍니다. Asp.net 5 / MVC 6에서 정확히 필요한 것입니다. 아름답습니다. 더 큰 사이트 또는 논리적 그룹을 위해 영역을 수퍼 영역으로 그룹화하려는 경우 내 경우 (및 기타)에서 매우 유용합니다.
drewid

Startup.cs 부분은 다음과 같아야합니다. services.Configure <RazorViewEngineOptions>이 메서드는 다음과 같습니다. public void ConfigureServices (IServiceCollection services)
OrangeKing89

42

실제로 경로를 생성자에 하드 코딩하는 것보다 훨씬 쉬운 방법이 있습니다. 다음은 새 경로를 추가하기 위해 Razor 엔진을 확장하는 예입니다. 내가 완전히 확신하지 못하는 한 가지는 여기에 추가하는 경로가 캐시 될지 여부입니다.

public class ExtendedRazorViewEngine : RazorViewEngine
{
    public void AddViewLocationFormat(string paths)
    {
        List<string> existingPaths = new List<string>(ViewLocationFormats);
        existingPaths.Add(paths);

        ViewLocationFormats = existingPaths.ToArray();
    }

    public void AddPartialViewLocationFormat(string paths)
    {
        List<string> existingPaths = new List<string>(PartialViewLocationFormats);
        existingPaths.Add(paths);

        PartialViewLocationFormats = existingPaths.ToArray();
    }
}

그리고 Global.asax.cs

protected void Application_Start()
{
    ViewEngines.Engines.Clear();

    ExtendedRazorViewEngine engine = new ExtendedRazorViewEngine();
    engine.AddViewLocationFormat("~/MyThemes/{1}/{0}.cshtml");
    engine.AddViewLocationFormat("~/MyThemes/{1}/{0}.vbhtml");

    // Add a shared location too, as the lines above are controller specific
    engine.AddPartialViewLocationFormat("~/MyThemes/{0}.cshtml");
    engine.AddPartialViewLocationFormat("~/MyThemes/{0}.vbhtml");

    ViewEngines.Engines.Add(engine);

    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);
}

참고할 사항 : 사용자 지정 위치에는 루트에 ViewStart.cshtml 파일이 필요합니다.


23

새 경로 만 추가하려는 경우 기본보기 엔진에 추가하고 몇 줄의 코드를 절약 할 수 있습니다.

ViewEngines.Engines.Clear();
var razorEngine = new RazorViewEngine();
razorEngine.MasterLocationFormats = razorEngine.MasterLocationFormats
      .Concat(new[] { 
          "~/custom/path/{0}.cshtml" 
      }).ToArray();

razorEngine.PartialViewLocationFormats = razorEngine.PartialViewLocationFormats
      .Concat(new[] { 
          "~/custom/path/{1}/{0}.cshtml",   // {1} = controller name
          "~/custom/path/Shared/{0}.cshtml" 
      }).ToArray();

ViewEngines.Engines.Add(razorEngine);

동일하게 적용됩니다 WebFormEngine


2
보기의 경우 : razorEngine.ViewLocationFormats를 사용합니다.
Aldentev

13

RazorViewEngine을 서브 클래 싱하거나 완전히 대체하는 대신 기존 RazorViewEngine의 PartialViewLocationFormats 속성을 변경할 수 있습니다. 이 코드는 Application_Start에 있습니다.

System.Web.Mvc.RazorViewEngine rve = (RazorViewEngine)ViewEngines.Engines
  .Where(e=>e.GetType()==typeof(RazorViewEngine))
  .FirstOrDefault();

string[] additionalPartialViewLocations = new[] { 
  "~/Views/[YourCustomPathHere]"
};

if(rve!=null)
{
  rve.PartialViewLocationFormats = rve.PartialViewLocationFormats
    .Union( additionalPartialViewLocations )
    .ToArray();
}

2
이것은 면도기 엔진 유형이 'RazorViewEngine'대신 'FixedRazorViewEngine'이라는 예외를 제외하고 저에게 효과적이었습니다. 또한 내 응용 프로그램이 성공적으로 초기화되는 것을 막기 때문에 엔진을 찾을 수 없으면 예외를 throw합니다.
Rob

3

마지막으로 확인한 결과, 사용자 고유의 ViewEngine을 구축해야합니다. 그래도 RC1에서 더 쉽게 만들 었는지 모르겠습니다.

첫 번째 RC 이전에 사용한 기본 접근 방식은 내 ViewEngine에서 컨트롤러의 네임 스페이스를 분할하고 부품과 일치하는 폴더를 찾는 것이 었습니다.

편집하다:

돌아와서 코드를 찾았습니다. 다음은 일반적인 아이디어입니다.

public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName)
{
    string ns = controllerContext.Controller.GetType().Namespace;
    string controller = controllerContext.Controller.GetType().Name.Replace("Controller", "");

    //try to find the view
    string rel = "~/Views/" +
        (
            ns == baseControllerNamespace ? "" :
            ns.Substring(baseControllerNamespace.Length + 1).Replace(".", "/") + "/"
        )
        + controller;
    string[] pathsToSearch = new string[]{
        rel+"/"+viewName+".aspx",
        rel+"/"+viewName+".ascx"
    };

    string viewPath = null;
    foreach (var path in pathsToSearch)
    {
        if (this.VirtualPathProvider.FileExists(path))
        {
            viewPath = path;
            break;
        }
    }

    if (viewPath != null)
    {
        string masterPath = null;

        //try find the master
        if (!string.IsNullOrEmpty(masterName))
        {

            string[] masterPathsToSearch = new string[]{
                rel+"/"+masterName+".master",
                "~/Views/"+ controller +"/"+ masterName+".master",
                "~/Views/Shared/"+ masterName+".master"
            };


            foreach (var path in masterPathsToSearch)
            {
                if (this.VirtualPathProvider.FileExists(path))
                {
                    masterPath = path;
                    break;
                }
            }
        }

        if (string.IsNullOrEmpty(masterName) || masterPath != null)
        {
            return new ViewEngineResult(
                this.CreateView(controllerContext, viewPath, masterPath), this);
        }
    }

    //try default implementation
    var result = base.FindView(controllerContext, viewName, masterName);
    if (result.View == null)
    {
        //add the location searched
        return new ViewEngineResult(pathsToSearch);
    }
    return result;
}

1
실제로 훨씬 쉽습니다. WebFormsViewEngine을 하위 클래스로 만든 다음 생성자에서 이미 검색하는 경로 배열에 추가하십시오.
Craig Stuntz

알아 둘만 한. 마지막으로 해당 컬렉션을 수정해야했을 때는 그렇게 할 수 없었습니다.
Joel

"baseControllerNamespace"변수를 기본 컨트롤러 네임 스페이스 (예 : "Project.Controllers")로 설정해야한다는 점을 언급 할 가치가 있지만, 그렇지 않으면 게시 된 지 7 년 후 정확히 필요한 작업을 수행했습니다.
prototype14

3

다음과 같이 시도하십시오.

private static void RegisterViewEngines(ICollection<IViewEngine> engines)
{
    engines.Add(new WebFormViewEngine
    {
        MasterLocationFormats = new[] {"~/App/Views/Admin/{0}.master"},
        PartialViewLocationFormats = new[] {"~/App/Views/Admin//{1}/{0}.ascx"},
        ViewLocationFormats = new[] {"~/App/Views/Admin//{1}/{0}.aspx"}
    });
}

protected void Application_Start()
{
    RegisterViewEngines(ViewEngines.Engines);
}

3

참고 : ASP.NET MVC 2의 경우 '영역'에서보기에 대해 설정해야하는 추가 위치 경로가 있습니다.

 AreaViewLocationFormats
 AreaPartialViewLocationFormats
 AreaMasterLocationFormats

영역에 대한보기 엔진을 만드는 방법은 Phil의 블로그에 설명되어 있습니다.

참고 : 이것은 프리뷰 릴리스 1 용이므로 변경 될 수 있습니다.


1

여기에있는 대부분의 답변 은 전화로 기존 위치지운ViewEngines.Engines.Clear() 다음 다시 추가합니다.이 작업을 수행 할 필요가 없습니다.

아래와 같이 기존 위치에 새 위치를 추가하기 만하면됩니다.

// note that the base class is RazorViewEngine, NOT WebFormViewEngine
public class ExpandedViewEngine : RazorViewEngine
{
    public ExpandedViewEngine()
    {
        var customViewSubfolders = new[] 
        {
            // {1} is conroller name, {0} is action name
            "~/Areas/AreaName/Views/Subfolder1/{1}/{0}.cshtml",
            "~/Areas/AreaName/Views/Subfolder1/Shared/{0}.cshtml"
        };

        var customPartialViewSubfolders = new[] 
        {
            "~/Areas/MyAreaName/Views/Subfolder1/{1}/Partials/{0}.cshtml",
            "~/Areas/MyAreaName/Views/Subfolder1/Shared/Partials/{0}.cshtml"
        };

        ViewLocationFormats = ViewLocationFormats.Union(customViewSubfolders).ToArray();
        PartialViewLocationFormats = PartialViewLocationFormats.Union(customPartialViewSubfolders).ToArray();

        // use the following if you want to extend the master locations
        // MasterLocationFormats = MasterLocationFormats.Union(new[] { "new master location" }).ToArray();   
    }
}

이제 RazorViewEngineGlobal.asax 에서 위를 사용하도록 프로젝트를 구성 할 수 있습니다 .

protected void Application_Start()
{
    ViewEngines.Engines.Add(new ExpandedViewEngine());
    // more configurations
}

자세한 정보는 이 튜토리얼 을 참조하십시오.

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