Programming/.NET

ASP.NET MVC 에서 요청이 들어오는 URL의 형태는 보통 다음과 같은 형태로 이루어져 있습니다. 예를 들어

 

http://cliel.com/Home/Index

 

위와 같은 URL에서는 호스트명을 제외하고 아래와 같은 패턴을 지정함으로서

 

{controller}/{action}

 

Home은 controller로 Index는 action으로 일치시킬 수 있습니다. 참고로 URL에서 /Home/Index 부분을 세그먼트로 구분하며 Home이 첫번째, Index가 두번째 세그먼트에 해당됩니다.

 

MVC 프로젝트에서 App_Start 폴더에 보면 RouteConfig.cs 라는 파일을 볼 수 있는데 기본적으로 URL을 통한 라우트정보는 이 파일에서 정의됩니다.

 

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name"Default",
        url"{controller}/{action}/{id}",
        defaultsnew { controller = "Home"action = "Index"id = UrlParameter.Optional }
    );
}

 

파일에 정의된 RegisterRoutes 메서드는 Global.asax.cs에 있는 Application_Start 메서드에 의해 호출되며 기본적으로 등록된 라우트는 URL의 세그먼트 순서대로 controller, action, id를 일치시키게 됩니다. 따라서

 

http://cliel.com/Home/Index/Choi

 

URL에서는 Home이 controller, Index가 action, Choi가 id에 해당합니다. 다만 id는 옵션으로서 필수가 아니기에

 

http://cliel.com/Home/Index

 

처럼 2개의 세그먼트를 가진 URL또한 처리가 가능합니다. 이때 http://cliel.com/Home/Index/abc 처럼 id에 대응되는 세그먼트가 존재한다면 RouteData개체의 Values속성을 통해서

 

RouteData.Values["id"];

값을 가져올 수 있습니다.

 

그러나 필요하다면 액션 메서드 차원에서 동일한 처리를 수행할 수 있는데 바로 매개변수를 이용한 처리입니다.

 

public ActionResult Index(string id)
{
    ViewBag.ID = id;
    return View();
}

 

특히 액션 메서드에서는 라우트에서 기본값을 지정하는 대신

 

public ActionResult Index(string id = "myValue")
{
    ViewBag.ID = id;
    return View();
}

 

액션 메서드의 매개변수에서 직접 기본값을 지정하는것도 가능합니다.

 

이때 매개변수의 데이터 형식은 int나 string처럼 임의로 지정할 수 있으며 실제 값을 받을때 해당 데이터형식으로 값을 전달받을 수 있습니다. 물론 URL을 통해 세그먼트로 전달되어 오는 데이터가 지정한 데이터형식으로 변환될 수 있는 값이어야 하는데 참고로 Optional로 지정된 부분에 아무런 값도 전달되지 않으면 기본적으로 Null이 됩니다.

 

컨트롤러와 액션에 따라서 임의의 라우트정보를 등록하려면 routes개체의 MapRoute 메서드를 사용합니다.

 

routes.MapRoute("myRoute""{controller}/{action}");

 

위에서 시도한 라우트 등록정보는 2개의 URL에 대한 세그먼트를 처리하고 있으며 첫번째 세그먼트를 controller에 두번째 세그먼트를 action에 대응시키고 있습니다. 만약 RouteConfig.cs 파일에 이러한 라우트정보만 등록되어 있으면 사용자가 http://cliel.com/ URL만으로 접근을 시도하려고 할때 오류를 발생시키게 됩니다. 이는 라우트정보가 2개의 세그먼트에서만 대응되기 때문입니다. 따라서

 

routes.MapRoute("myRoute""{controller}/{action}"new { action="Index" });

 

이와 같이 기존처럼 2개의 세그먼트에 대응하도록 하되 익명형식으로 속성을 전달하여 action에 대응되는 세그먼트가 없을 경우 기본적으로 Index라는 action메서드를 타도록 할 수 있습니다. 그러면

 

http://cliel.com/Home

 

URL로 요청이 들어오는 경우 세그먼트가 하나뿐이더라도 action에 대한 기본값이 자동으로 적용되어 http://cliel.com/Home/Index 로 처리될 것입니다.

 

routes.MapRoute("myRoute""{controller}/{action}"new { controller="Home"action="Index" });

 

물론 기본값은 controller와 action모두 지정할 수도 있습니다. 이렇게 하면 http://cliel.com/ 처럼 세그먼트가 전혀 존재하지 않는 URL의 요청도 수용이 가능하게 됩니다.

 

라우트에 대한 기본값은 단순히 문자열 뿐만 아니라

 

routes.MapRoute("myRoute""{controller}/{action}/{id}"new { controller = "^C*"id = "aaa" });

 

정규식등을 사용하여 URL의 세그먼트가 특정 문자로 시작하는 요청에 대해서만 controller에 대응시켜 처리할 수도 있습니다.

 

routes.MapRoute("myRoute""MyPage/{controller}/{action}"new { controller="Home"action="Index" });

 

또한 정적라우트 정보도 생성하여 URL의 고정된 형태를 처리하는 것도 가능합니다. 이 라우트설정은 MyPage라는 정적요소를 포함하고 있어서 http://cliel.com/mypage/Home/Index 로 들어오는 요청에만 대응될 수 있습니다. 이 상태에서 만약 정적요소에 /를 제거하면

 

routes.MapRoute("myRoute""MyPage{controller}/{action}"new { controller="Home"action="Index" });

 

일종의 가변요소로 적용되어 2개의 세그먼트에 대한 URL요청의 라우트정보로 바뀌게 되지만 컨트롤러의 시작은 항상 mypage가 되어야 하기 때문에 컨트롤러가 Home이라면 MyPageHome으로된 요청을 처리할 수 있게 됩니다. 이러한 라우트설정방법은 아예 패턴자체를 제거할 수도 있어서

 

routes.MapRoute("myRoute""MyPage/{action}"new { controller="Home"action="Index" });

 

특정 세그먼트에 대해 타겟을 임의로 지정할 수 있습니다. 따라서 위 예제의 경우 mypage라는 세그먼트는 패턴에서 controller가 생략되고 기본값으로 Home이 지정됨에 따라 mypage/Index는 Home/Index로 처리될 것입니다.

 

routes.MapRoute("myRoute""{controller}/{action}/{id}/{*param}"new { id = "myValue" });

 

URL의 경로자체가 가변적인 상황이라면 라우트 포멧을 지정할때 * 문자를 사용하면 됩니다. 따라서 위와 같은 라우트설정의 경우 http://cliel.com/Home/Index/abc/def 나 http://cliel.com/Home/index/abc/def/ghi 처럼 id부터 계속 늘어나는 URL요청을 받아들일 수 있게 되며 이때 param에는 def/ghi 와 같은 값이 들어가게 되므로 /문자를 통해 각 세그먼크를 분리하여 값을 추출해야 합니다.

 

routes.MapRoute("myRoute""{controller}/{action}/{id}"new[] { "MVC_Test.Home" });

 

뿐만 아니라 라우트 설정에는 네임스페이스를 포함할 수 있습니다. 위 설정은 라우트를 수행할때 가장 먼저 참조해야할 네임스페이스를 지정하고 있습니다. 이 설정은 예를 들어 MVC_Test 네임스페이스 하위에 Home 컨트롤러가 존재하고 MVC_Test.Home 하위에 Home컨트롤러가 존재한다면 http://cliel.com/Home/Index 와 같은 요청이 발생했을때 어떤 Home 컨트롤러에 진입해야할지 모호해지는 경우를 해결할 수 있습니다. 즉, 같은 이름의 컨트롤러가 존재하는 경우 진입하고자 하는 특정 컨트롤러의 네임스페이스를 지정함으로서 모호성 문제를 해결하는 것입니다.

 

그러나 만약 지정한 네임스페이스에서 명시된 컨트롤러를 찾을 수 없다면 기존처럼 기타 다른 네임스페이스에서 일치하는 컨트롤러를 찾게 되는데 컨트롤러 자체를 특정 네임스페이스에서만 찾고 다른 곳에서의 검색을 차단하려면 Route개체를 활용해 다음과 같이 설정하도록 합니다.

 

Route r = routes.MapRoute("myRoute""{controller}/{action}/{id}"new[] { "MVC_Test" });
r.DataTokens["useNamespaceFallback"] = false;

 

다만

 

routes.MapRoute("myRoute""{controller}/{action}/{id}"new[] { "MVC_Test""MVC_Test.Home" });

 

네임스페이스는 위와 같이 여러개 지정할 수도 있는데 이런 경우 MVC_Test와 MVC_Test.Home 네임스페이스 모두에게서 해당하는 컨트롤을 찾게 됩니다. 다만 이것은 네임스페이스를 확인하는 우선순위를 의미하는 것이 아니므로 둘 모두에 같은 컨트롤러가 존재한다면 역시 예외를 발생시키게 됩니다.

 

따라서 특정 네임스페이스부터 컨트롤러를 확인하는 순위개념을 적용하고자 한다면 각각의 네임스페이스마다 라우트정보를 추가해주는 방식을 사용해야 합니다.

 

routes.MapRoute("myRoute""{controller}/{action}/{id}"new[] { "MVC_Test" });
routes.MapRoute("myRoute""{controller}/{action}/{id}"new[] { "MVC_Test.Home" });

 

라우트 설정은 단순 URL뿐만 아니라 http 메서드를 사용할 수도 있기 때문에

 

routes.MapRoute("myRoute""{controller}/{action}"new { httpMethod = new HttpMethodConstraint("GET") } );

 

http://cliel.com/Home/Index 와 같은 요청을 GET방식으로만 대응되도록 할 수 있고 아니면 여러 메서드를 지정해

 

routes.MapRoute("myRoute""{controller}/{action}"new { httpMethod = new HttpMethodConstraint("GET""POST") } );

 

2개 이상의 메서드를 처리하는 것도 가능합니다.

 

지금까지는 직접 라우트를 설정하는 방식을 살펴보았는데 이 외에도 System.Web.Mvc.Routing.Constraints의 제약조건 클래스를 사용하면 간단하고도 쉽게 다양한 형식의 URL 라우트 설정을 제어할 수 있습니다.

 

routes.MapRoute("myRoute""{controller}/{action}/{id}"new { id = new BoolRouteConstraint() } );

 

한가지 예로 이 설정은 id로 넘어오는 세그먼트가 bool 형식이어야 한다는 것을 의미하므로 id가 bool형식일때만 대응되며

 

routes.MapRoute("myRoute""{controller}/{action}/{id}"new { id = new LengthRouteConstraint(10) } );

 

LengthRouteConstraint 클래로 값을 지정하면 id 요청값의 길이가 정확히 10자인 경우에만 요청을 대응시킬 수 있습니다.

 

아래에서는 위와 같이 사용가능한 클래스를 정리한 것입니다.

 

 AlphaRouteConstraint

 알파벳 문자에 대응합니다. 대소문자는 구분하지 않습니다.

 BoolRouteConstraint

 bool 형식에 대응합니다.

 DateTimeRouteConstraint

 DateTime 형식에 대응합니다.

 DecimalRouteConstraint

 Decimal 형식에 대응합니다.

 DoubleRouteConstraint

 Double 형식에 대응합니다.

 FloatRouteConstraint

 Float 형식에 대응합니다.

 IntRouteConstraint

 Int 형식에 대응합니다.

 LengthRouteConstraint

 단일 값이면 해당 길이로, 2개 값이면 최소/최대 길이로 대응합니다.

 LongRouteConstraint

 Long 형식에 대응합니다.

 MaxRouteConstraint

 지정한 정수값을 최대로 하고 해당 값안에 포함하는 경우에만 대응합니다.

 MaxLengthRouteConstraint

 지정한 정수값을 최대로 하고 길이값이 해당 값안에 포함하는 경우에만 대응합니다.

 MinRouteConstraint

 지정한 정수값을 최소로 하고 해당 값 이상인 경우에만 대응합니다.

 MinLengthRouteConstraint

 지정한 정수값을 최소로 하고 길이값이 해당 값 이상인 경우에만 대응합니다.

 RangeRouteConstraint

 지정한 최소값과 최대값사이에 해당하는 경우에만 대응합니다.

 

참고로 여러 클래스를 결합해 다중적으로 대응되는 라우트를 설정해야 한다면 CompoundRouteConstraint 클래스를 사용해서 원하는 바를 구현할 수 있습니다.

 

routes.MapRoute("myRoute""{controller}/{action}/{id}"new { id = new CompoundRouteConstraint(new IRouteConstraint[] { new LengthRouteConstraint(10)new AlphaRouteConstraint() }) } );

 

만약 지금까지 제시된 모든 라우트설정에서 원하는 방향으로의 구현이 불가능하다면 직접 클래스를 만들어 대응규칙을 정의할 수 있습니다.

 

public class myRoute : IRouteConstraint
{
    public bool Match(HttpContextBase httpContextRoute routestring parameterNameRouteValueDictionary valuesRouteDirection routeDirection)
    {
        if (values["controller"].ToString() == "Customer")
            return true;
        else
            return false;
    }
}

 

클래스는 반드시 IRouteConstraint 인터페이스를 상속받아야 하며 Match 메서드를 구현해야 합니다. 이 메서드는 라우트나 매개변수, 세그먼트등 라우트처리에 관한 다양한 매개변수를 넘겨주는데 이를 토대로 적절한 대응조건을 작성하기만 하면 되며 이미 언급한것과 동일한 방법으로 클래스를 적용시키기만 하면 됩니다.

 

routes.MapRoute("myRoute""{controller}/{action}"new { controller = "Customer"action = "Index" }new { mR = new myRoute() } );

 

지금까지 설명드린 라우트 설정은 모두 RouteConfig.cs 파일을 통해 이루어 집니다. 하지만 전혀 다른 방법으로 라우트를 설정할 수도 있는데 그것은 바로 액션메서드에 속성을 직접 부여함으로서 이루어 집니다. 이 속성을 통한 라우트기능은 기본적으로 비활성화상태인데

 

routes.MapMvcAttributeRoutes();

 

위와같이 MapMvcAttributeRoutes 메서드를 호출함으로서 속성기반 라우팅기능을 이용할 수 있게 되고 다음과 같이

 

public class MyController : Controller
{
    // GET: My
    [Route("my")]
    public ActionResult Index()
    {
        ViewBag.Content = "abc";
        return View();
    }
}

 

액션메서드에 라우트 설정에 관한 속성을 추가하면 MyController의 Index 액션메서드는 http://cliel.com/my URL로 대응이 이루어 지게 됩니다. 물론 아래와 같이 다중 세그먼트에 대한 설정도 가능하며

 

[Route("my/index")]
public ActionResult Index(string id)
{
    return View();
}

[Route("my/default")]
public ActionResult Default(string id)
{
        return View();
}

 

같은 컨트롤러안에 동일한 라우트규칙이 존재하는 경우 위에서 처럼 반복해서 라우트정보를 명시하기 보다 RoutePrefix를 통해 접두어로서 처리하면 훨씬 편리하게 라우트설정을 유지할 수 있습니다.

 

[RoutePrefix("my")]
public class MyController : Controller
{
    // GET: My
    [Route("index")]
    public ActionResult Index(string id)
    {
        return View();
    }

    [Route("default")]
    public ActionResult Default(string id)
    {

            return View();
    }
}

 

따라서 다음과 같이 내부 액션메서드의 속성에서 접두어에 해당하는 세그먼트를 생략할 수 있게 됩니다. 그러나 특정 액션메서드에서만은 접두어를 적용시키고 싶지 않은 경우도 있을 수 있는데 이때는

 

[Route("~/default")]
public ActionResult Default(string id)
{
        return View();
}

 

~문자를 앞에 붙여주기만 하면 됩니다. 그러면 접두어가 컨트롤러에 지정되어 있다 하더라도 여전히 http://cliel.com/default 처럼 접근이 가능하도록 할 수 있습니다.

 

속성을 통한 라우트설정은 규칙이 RouteConfig.cs에서와 완전히 동일하기 때문에 동일한 포멧을 유지할 수 있으며

 

[Route("my/{id}")]
public ActionResult Index(string id)
{
    ViewBag.Content = id;
    return View();
}

 

System.Web.Mvc.Routing.Constraints의 제약조건과 동일한 방식을 적용하는 것도 가능합니다.

 

[Route("my/{id:int}")]
public ActionResult Index(string id)
{
    ViewBag.Content = id;
    return View();
}

 

위 설정은 id가 int형식으로 들어올때만 해당 액션메서드에 대응되도록 한 것입니다. 이때 제약조건은 : 문자를 통해 여러조건을 결합할 수 있기 때문에

 

[Route("my/{id:int:bool}")]
public ActionResult Index(string id)
{
    ViewBag.Content = id;

    return View();
}

 

다소 이상하지만 id가 int이며 bool형식에도 해당되도록 강제할 수 있습니다.

 

[Route("my/{id:int:bool}", Name="Default")]
public ActionResult Index(string id)
{
    ViewBag.Content = id;

    return View();
}

 

또한 Name을 통해 RouteConfig.cs 파일에 지정한 라우트의 이름을 지정하면 해당 라우트를 기반으로 액션메서드의 라우팅 URL을 설정할 수 있습니다.

 

위와 같은 URL에 대한 직접적인 라우트설정 이외에도 route개체에는 설정가능한 유용한 속성이 몇가지 존재합니다. 대략 2가지 정도의 설정을 알아보고자 하는데 첫번째는 특정 파일에 대한 직접적인 요청이 URL을 통해 들어오는 경우입니다.

 

Route와는 조금 다르지만 비슷하게 동작하는 속성중에는 ActionName속성이 존재합니다.

 

[ActionName("myIndex")]
public ActionResult MyPage()
{
    return View();
}

 

이 설정은 기존의 MyPage의 요청을 대신해 myIndex로 액션 메서드의 이름을 재정의하도록 합니다. 다만 이런 경우에는 myindex라는 뷰가 보여질 수 있도록 해당 뷰가 마련되어 있어야 합니다.

 

ASP.NET MVC는 기본적으로 모든 요청에 대해 라우트를 통해서 요청과 응답이 진행되지만 만약 라우트설정과는 별개로 실제 파일에 대한 요청이 이루어 지는 경우

 

/Data/mypage.html

 

예를 들어 위와 같은 요청이 발생하면 해당 위치에 실제 파일이 존재했을때 라우트 설정을 타기전 먼저 URL에서 지정한 파일을 우선으로 반환하게 됩니다. 이는 URL에 대한 라우트처리 이전에 실제 파일의 존재유무를 먼저확인하는 처리방식을 기본으로 하기 때문입니다.

 

그런데 라우트개체에 RouteExistingFiles 속성을 true로 설정한다면 이 순서를 뒤집을 수 있습니다. 그러면 실제 파일에 대한 요청이 들어올때 이를 특정 컨트롤러와 뷰로 라우트설정을 통해 연결짓는것이 가능하게 됩니다.

 

routes.MapRoute("mypage""Data/mypage.html"new { controller = "Home"action = "Index" });

 

반면 요청이 들어오는 특정 URL을 아예 무시할 수도 있는데 이때는 IgnoreRoute 메서드를 사용합니다.

 

routes.IgnoreRoute("Home/Index");

 

특정 파일에 대한 요청도 적용하는 방법은 동일합니다.

 

routes.IgnoreRoute("mydata/mypage.html");

'Programming > .NET' 카테고리의 다른 글

[C#] File / FileInfo  (0) 2018.03.07
[C#] Hashtable / SortedList  (0) 2018.02.27
[ASP.NET MVC] 라우팅(Routing)  (0) 2018.02.21
[C#] Stack / Queue  (0) 2018.02.13
[ASP.NET MVC] 프로젝트 폴더 구성  (0) 2018.02.09
[C#] EventWaitHandle  (0) 2018.01.16
0 0