URL Rewriting in ASP.NET


어떻게 ASP.NET에서 동적으로 URL 재작성(URL Rewriting)을 하는가? URL재작성은 웹요청을 가로채서 이를 다른 URL로 다시보내는 과정이다. URL재작성을 수행하는 다양한 기술을 알아보고 실제 사용하는 방법을 살펴볼 것이다.

 

소개


여러분의 웹 사이트의 URL들을 잠시 머리속에 떠올려보자. 여러분 사이트의 URL이
http://yoursite.com/info/dispEmployeeInfo.aspx?EmpID=459-099&type=summary 이와 같거나 아마 어떤 디렉토리로 또는 다른 사이트로 옮겨진 수많은 웹페이지를 갖는 사이트일지도 모른다. 이런 경우 과거 URL로 즐겨차기한 방문자는 제대로 원하는 곳으로 갈 수 없게된다. 이 글에서 우리는 의미없어 보이는 URL를 의미있는(meangingful), 기억하기 쉬운(memorable)URL로 간단히 작성하는 URL재작성을 시용하는 것을 살펴볼 것이다. 즉, 이전의 http://yoursite.com/info/dispEmployeeInfo.aspx?EmpID=459-099&type=summaryhttp://yoursite.com/people/sales/chuck.smith으로 대체하는 것이다. 또한 우리는 URL재작성이 404 오류를 생성하는 방식을 알아볼 것이다.

 

URL재작성은 들어오는 웹요청(Request)를 가로채고 요청을 다른 자원으로 리다이렉트하는 과정이다. URL재작성을 수행과정은 일반적으로 URL요청값에 기초해서 요청된 URL를 검사되고, 요청을 다른 URL로 리다이렉트한다.

 

예를 들어, 어떤 사이트가 /people/ 디렉토리에서 /info/employees/ 디렉토리로 모든 웹페이지를 이동시키는 경우, 웹요청 /people/ 디렉토리에 있는 파일을 원하는지를 검사하기 위해서 URL재작성을 사용하고 싶을 것이다. 만약 그 요청이 /people/ 디렉토리에 있는 파일이면,그대는 자동적으로 요청을 /info/employees/ 디렉토리에 있는 동일한 파일 을 리다이렉트하고자 할 것이다.

 

전통적인 ASP에서, URL재작성을 하는 유일한 방법은 ISAPI 필터를 작성하거나 이런 기능을 재공하는 제품을 구입하는 것이었다. 그러나 ASP.NET에서는 여러가지 방법으로 URL재작성을 쉽게 구현할 수 있다. 이 글에서 우리는 URL재작성을 구현하는 쓸만한 기술을 살펴볼 것이도,실제 이것들을 적용해 볼 것이다. URL재작성의 기술적인 면을 파고들기 전에 URL재작성이 어떤 곳에서 사용될 만한 것이지 몇가지 시나리오를 생각해 보자.

 

URL재작성의 일반 사용


흔히 데이터 중심 ASP.NET 웹사이트 구축은 하나의 페이지만을 필요한다. 이 페이지는 질의문의 파라미터를 가지고 데이터베이스의 데이터를 출력한다. 예를 들어, e-commerce사이트를 디자인할때 여러분의 임무중 하나는 사용자가 제품을 두루 살펴보게 하는 것이다. 이를 위해서 특정 목록의 제품들을 출력하는 displayCategory.aspx 라는 페이지를 하나 생성한다. 그 목록의 제품을 조회하기 위해서는 질의문 파라미터가 필요할 것이다. 즉, 만약 사용자가 개밥 제품들을 둘러보기를 원하면, 모든 개밥 제품은 CategoryID가 5이다. 이 제품들을 보기 위해서는 :http://yousite.com/displayCategory.aspx?CategoryID=5 이와 같아야 한다.

 

웹사이트의 URL를 이처럼 작성하는 것은 두가지 단점이 있다. 하나, 최종 사용자측면에서 http://yousite.com/displayCategory.aspx?CategoryID=5 URL은 아무런 의미가 없다. Jakob Neilsen은 다음같이 추천한다.

 

   * 짧게하라.
   * 타이핑하기 쉽게하라.
   * 사이트구조를 가시화하라.
   * "Hackable", 사용자가 URL 부분을 가지고도 사이트 여러곳을 다닐 수 있게 하라.

 

여기에 '기억하기 쉽게 하라'를 추가하고 싶다. http://yousite.com/displayCategory.aspx?CategoryID=5 이런 URL은 Neilsen의 기준과 거리가 멀뿐 아니라 기억하기도 어렵다. 일반 사용자에게 질의문 값을 쓰도록하는 것은 URL를 쓰기 어렵게 한다. 이는 질의문 파라미터의 목적과 일름/값의 쌍 구조를 이해하는 웹개발자들에게나 의미가 있다.

 

더 나은 접근은 http://yoursite.com/products/dogfoods 처럼 느낌이 오고 기억하기 좋은 URL를 접하게 하는 것이다. 이 URL를 보는 순간 개밥에 대한 어떤 정보가 보여질 것을 알아차릴 수 있다. 또한 기억하고 공유하기 쉽다.

 

주의: "hackable" URL 예, 많은 블러그사이트를 생각해보시라. 2004년 8월 28일에 올라온 글을 보고싶다면 여러분은 http://someblog.com/2004/08/28 같은 URL로 방문한다. 만약 URL이 http://someblog.com/2004/08처럼 되었다면 8월에 올라온 모든 글을 보게 될 것이다. http://someblog.com/2004 처럼 되면 2004년치 모두를 보게 되는 것이다.

 

URL 재작성은 사이트 리스트럭처링을 처리하기 위해서 사용되는데, 이렇게 하지않으면 링크들이 너무나 많이 깨질 것이다.

 

웹요청이 IIS에 도달할 때 무슨 일이 일어나는가?

 

어떻게 IIS가 들어오는 요청을 처리하는지 이해하는 것이 중요하다.
요청이 IIS 웹서버에 도착할 때 IIS는 요청된 파일의 확장자를 검사한다. 이는 요청을 어떻게 처리할 지를 결정하기 위해서이다. IIS만이 요청들(HTML 페이지,이미지,다른 정적 내용)을 처리할 수 있거나 IIS는 요청을 ISAPI 익스텐션에 라우트할 수 있다. (ISAPI 익스텐션은 들어오는 웹 요청을 처리하는 unmanaged, compiled 클래스이다. 이것의 임무는 요청된 리소스에 대한 컨텐츠를 발생하는 것이다.

 

예를 들어, 만약 요청이 info.asp로 들어오면, IIS는 그 메시지를 asp.dll ISAPI 익스텐션으로 라우트한다. ISAPI 익스텐션인 요청된 ASP 페이지를 로드하고 실행하고 IIS에 랜더링된 HTML를 반환한다. IIS는 클라이언트에 그 요청결과를 보낸다. ASP.NET 페이지에서는, IIS가 메시지를 aspnet_isapi.dll ISAPI 익스텐션을 라우트한다. aspnet_isapi.dll ISAPI 익스텐션이 관리된 ASP.NET 워커 프로세스에 프로세싱을 넘겨준다.워커 프로세시는 요청을 처리하고 ASP.NET 웹피이지의 랜더링된 HTML 결과를 반한다.

 

Figure 1. Configured mappings for file extensions


요청이 ASP.NET 엔진에 들오올때 무슨 일이 일어나는가?


ASP.NET 이전에는 URL을 재작성하기 위해서는 ISAPI필터가 필요했다. URL재작성은 ASP.NET만으로 가능하다. 왜냐하면 ASP.NET 엔진이 IIS와 매우 유사하기 때문이다. 이런 유사점은 ASP.NET 엔진때문인데.

 

   1. 요청을 처리할 때 이벤트를 발생시키다.
   2. HTTP 모듈(Module)이 발생한 이벤트를 처리하도록 한다.

       이것은 IIS의 ISAPI 필터와 유사하다.
   3. 요청된 리소스의 랜더링은 HTTP 처리기(Handler)가 하도록 한다.

       이것은 ISAPI 익스텐션과 유사하다.

 

IIS처럼 요청 생명주기 동안 ASP.NET 엔진은 이벤트를 발생한다. 이런 이벤트는 한 상태에서 다른 상태로 요청변경을 알리는 것이다. 예를 들어, BeginRequest은 ASP.NET 엔진이 요청에 처음 반응할 때 발생하는 이벤트이다. AuthenticateRequest 이벤트가 그 다음에 발생한다. 이는 사용자 식별기능이 가능할 때 수행된다. (이 외에도 AuthorizeRequest, ResolveRequestCache 그리고 EndRequest 이벤트들이 있다. 이들은 System.Web.HttpApplication 클래스의 이벤트이다.)

 

이전 부분에 논의한 것처럼 ISAPI 필터는 IIS가 발생시킨 이벤트에 반응하도록 생성될 수 있다. 이와 같은 기능을 ASP.NET 는 HTTP 모듈이 담당하도록 한다. HTTP 모듈은 ASP.NET 엔진이 발생시킨 이벤트에 반응할 수 있다. ASP.NET 웹 응용프로그램은 많은 HTTP 모듈을 갖도록 환경정보를 설정할 수 있다. ASP.NET 엔진이 처리하는 각각의 요청을 위해서 각각의 HTTP 모듈이 초기화되고 요청를 처리하는 동안 발생한 이벤트들에 대한 이젠트 처리기를 묶을 수도 있다. 매 요청을 기능하도록 하는 많은 내장된 HTTP  모듈이 있다. 내장된 HTTP 모듈중의 하나가 FormsAuthenticationModule 이다. 이 모듈은 먼저 폼 인증 사용되는지 검사한다. 만약 그렇다면 사용자가 인증된 것인지 아닌지를 본다. 인증이 않되었으면 특정 로그인 페이지로 리다이렉트한다.

 

IIS는 어떤지 상상해보자. 들어온 요청은 결과적으로 ISAPI 익스텐션으로 옮겨진다. 이렇게 하는 것은 특정 요청에 대한 데이터를 반환하기 위함이다. 예를 들어, ASP 웹페이지에서 요청이 도착할 때 IIS를 요청을 asp.dll로 요청을 전달한다. 이 일은 요청된 ASP 페이지에 대한 HTML 문서를 반환하는 것이다. ASP.NET 엔진은 유사한 접근을 기능화했다. HTTP 모듈을 초기화한다. 그 다음 어떤 HTTP 처리기가 요청을 처리해야하는지 결정하는 것이다.

 

ASP.NET 에진에 들어온 모든 요청은 결국 HTTP 처리기 또는 HTTP 처리기 팩토리에 도착하게 된다. HTTP처리기 팩토리는 요청을 처리하기 위해서 사용된 HTTP 처리기의 인스턴스를 리턴한다. 마지막 HTTP 처리기가 요청된 리소스를 랜더링한다. Response를 반한하다. 이 Response는  IIS에 보내는데 이것은  처음 요청한 사용자에게 그 결과를 보내는 것이다.

 

ASP.NET는 많은 내장 HTTP처리기를 포함하고 있다. PageHandlerFactory는 ASP.NET 웹페이지를 랜더링하기 위해서 사용된다. WebServiceHandlerFactory는 ASP.NET 웹서비스를 사용할 때 SOAP envelope를 만들어낼 때 사용한다. TraceHandlertrace.axd에 요청를 HTML 문서를 랜더링한다.

 

Figure 2. Request processing by IIS and ASP.NET

 

그림은  ASP.NET에서 요청이 어떻게 처리되는 보여준다. 먼저, IIS가 요청을 받고 요청을 aspnet_isapi.dll로 보낸다. 다음, ASP.NET엔진이 HTTP모듈을 초기화한다. 마지막으로 적당한 HTTP처리기가 호출되고 요청결과가 랜더링되서 IIS로 보내지고 사용자가 볼 수 있게 된다.

 

사용자 정의 HTTP모듈과 HTTP처리기 생성과 등록하기

 

사용자 정의 HTTP모듈과 HTTP처리기 생성은 쉬운 작업에 속한다.그 일은 정확한 인터페이스를 실행하는 관리된 클래스를 생성하는 것이다. HTTP 모듈은 System.Web.IHttpModule 인터페이스를 반드시 구현해야한다.

 

HTTP처리기 System.Web.IHttpHandler를 구현해야한다. HTTP처리기 팩토리는 System.Web.IHttpHandlerFactory를 구현해야 한다. HTTP처리기와 HTTP모듈을 생성하는 세세한 것은 다른 기회에 하겠다.

참고정보 : HTTP Handlers and HTTP Modules in ASP.NET.

 

HTTP모듈과 HTTP처리기가 생성되면 반드시 웹App에 등록해야 한다. HTTP모듈과 HTTP처리기 등록은 웹 서버는 machine.confg파일에서 웹 App는 web.config에 하면된다.

 

특별히 HTTP모듈을 웹App에 추가하려면 web.config 파일의 configuration/system.web 섹션의 <httpModules> 태그에서 환경정보를 설정한다.


<httpModules>
   <add type="type" name="name" />
</httpModules>

 

type 값은 HTTP모듈의 어셈블리와 클래스 이름을 입력한다.
name 값은 Global.asax파일에서 참조할 수 있는 HTTP모듈의 친근한 명칭을 입력한다.

HTTP처리기과 HTTP처리기 팩토리는 web.config파일의 configuration/system.web 섹션의 <httpHandlers>로 환경정보를 설정한다.

 

<httpHandlers>
   <add verb="verb" path="path" type="type" />
</httpHandlers>

 

각각의 요청들을 상상해보자.ASP.NET엔진은 그 요청을 랜더링하기 위해서 무슨 HTTP처리기가 필요한지 결정한다. 이 결정은 요청의 verb path 값에 의해서 이루어진다. HTTP 요청의 유형을 식별하는 verb는 GET 또는 POST 이다. 반면에 path 는 요청한 파일의 위치와 파일명을 가리킨다. 만약 우리가 .scott 확장자를 갖는 파일의 요청유형(GET or POST) 모두를 처리하는 HTTP처리기를 원한다면 web.config 파일에서 다음과 같이 하면 된다.

 

<httpHandlers>
   <add verb="*" path="*.scott" type="type" />
</httpHandlers>


이곳에서 type는 HTTP처리기이다.

주의: HTTP처리기를 등록할때 HTTP처리기가 사용하는 익스텐션이 ASP.NET엔진상에서 IIS에 맵핑된다는 것을 보증하는 것이 중요한다.  .scott 예제에서 .scott 익스텐션은 aspnet_isapi.dll ISAPI 익스텐션상에서 IIS에 맵핑되지 않는다. 즉 파일 foo.scott에 대한 요청은 IIS가 파일 foo.scott의 내용물을 리턴하도록 하는 것이다. HTTP처리기가 이 요청을 처리하기 위해서는 ASP.NET엔진에 반드시 맵핑되어야 한다.그런 다음에야 ASP.NET엔진이 요청을 적절한 HTTP처리기에 라우트할 것이다.

 

HTTP모듈과 HTTP처리기에 대한 정보를 원하시면  <httpModules> 요소문서 와 <httpHandlers> 요소문서를 참고하시기 바란다.

 

URL재작성 구현하기


URL재작성은 IIS 웹서버수준에서 ISAPI 필터 또는 ASP.NET 수준에서 HTTP모듈과 HTTP처리기에서 구현될 수 있다. 이 글은 ASP.NET에서 URL재작성에 초점을 마추고 있다. 그래서 ISAPI 필터에서 구현은 신경쓰지 않을 것이다. 하지만 ISAPI 필터에 대한 제품을 소개한다.

 

ISAPI Rewrite
IIS Rewrite
PageXChanger

 

다른 많은 것들이 있다.

ASP.NET 수준에서 URL재작성 구현은 System.Web.HttpContext 클래스의 RewritePath() 메소드가 담당한다. HttpContext 클래스는 특정 HTTP Request에 대한 HTTP 정보를 포함한다. HttpContext 인스턴스는 ASP.NET 엔진이 받아들이는 각각의 Request에 의해서 생성된다. 이 클래스의 Request 와 Response 속성은 들어오는 Request와 나가는 Response에 대한 접근정보를 제공한다. Application 과 Session 속성은 application 과 session 변수에 대한 접근 정보를 제공한다. User 속성은 인증된 사용자에 대한 정보를 제공한다. 다른 관련 속성에 대해서 참고하기 바람.

 

닷넷 프레임워크 버전 1.0의 RewritePath()메소는 새로운 경로에 사용하기 위해서 하나의 문자열을 변수로 받는다. 내부적으로 HttpContext 클래스의 RewritePath(string) 메소드는 Request 객체의 Path QueryString 속성을 업데이트한다. 닷넷 프레임워크 버전 1.1 에서 RewritePath(string)은 RewritePath() 메소드의 다른 형식을 포함한다. 즉 3개 문자열 인자(파라미터)를 받는다. 이 오버로드 메소드는 Request 객체의 Path 와 QueryString 속성을 설정할뿐만 아니라 PhysicalPath,PathInfo 그리고 FilePath 속성들에 대한 Request 객체이 값들을 계산하기 위해 사용하는 내부 변수를 설정한다.

 

ASP.NET에서 URL재작성을 구현하기 위해서는 아래에 열거한 것들에 대한 HTTP모듈과 HTTP처리기를 생성할 필요가 있다.


1.URL이 재작성될 필요가 있는 결정하기 위해서 요청된 경로(path)를 검사한다.
2.필요하다면 RewritePath() 매소드를 호출해서 경로를 재작성한다.

 

예를 들어,직원 정보를 보유한 사이트를 상상해보자. 직원 정보를 조회하기 위해서 /info/employee.aspx?empID=employeeID 로 접근하는 사이트이다. URL를 보다 'hackable'하게 만들기 위해서 우리는 직원 정보 페이지를 /people/EmployeeName.aspx 이렇게 접근하게끔 결정한다. 즉, 페이지 /people/ScottMitchell.aspx가 요청될때 우리는 URL를 재작성하고 싶다. 결국 페이지 /info/employee.aspx?empID=1001이 사용되게 된다.

 

HTTP모듈로 URL 재작성하기

 

ASP.NET 수준에서 URL를 작성할 때 재작성을 수행하기 위해서 HTTP모듈 또는 HTTP처리기를 사용할 수 있다. HTTP모듈을 상용할 때 결정해야할 것은 요청의 생명주기 동안 무엇을 검사할 것인가 있다. 언뜻 보기에 이것은 임의적인 선택같지만 여러분이 내린 결정은 크고 작은 상황에서 app에 영향을 줄 수 있다. URL 재작성을 어디에서 수행할지의 선택은 내장 ASP.NET HTTP모듈들이 자기들의 임무를 수행하기 위해서 Request 객체의 속성을 사용하기 때문에 중요하다.(경로 재작성은 Request  객체의 속성값을 변경시킴을 명심하자)


아래 표는 내장 HTTP모듈과 이벤트(Event) 목록이다.

HTTP Module Event Description
FormsAuthenticationModule AuthenticateRequest 사용자가 Form 인증을 사용해서 인증되었는지 결정한다. 인증된 사용자가 아니면 자동적으로 사용자는 특정 로그인 페이지로 리다이렉트된다.)
FileAuthorizationMoudle AuthorizeRequest Windows 인증을 사용할 때,이 HTTP 모듈은 Microsoft® Windows® 계정이 요청된 리소스에 대한 올바른 권한을 갖는지 확인하기 위해서 검사한다
UrlAuthorizationModule AuthorizeRequest 요청자는 특정 URL에 접근할 수 있는지 확인하기 위해서 검사한다.URL 인증은 web.config 파일의 <authorization> 와 <location>에서 확인할 수 있다.)

 

BeginRequest 이벤트는 AuthenticateRequest 전에 발생한다. AuthenticateRequest 이벤트는 AuthorizeRequest 전에 발생한다. 이점들을 명심하자.

 

URL재작성이 수행될 수 있는는 안전한 곳은 BeginRequest 이벤트가 그 첫번째일 것이다. 만약 URL이 재작성될 필요가 있다면 내장된 HTTP 모듈이 실행되는 어떤 시간에는 이 작업을 할 수있다는 말일 것이다. 이런 접근은 단점은 Form 인증을 사용할 때 발생한다. 만약 당신이 Form 인증을 전 사용했었다면 알고 있을 것이다. 사용자가 제한된 리소스를 방문할 때 사용자들이 자동적으로 로그페이지로 이동한다는 것이다. 이런 일은 성공적으로 로그인이 이루어졌지만 사용자가 처음 온 자리로 되돌려 보내지는 것이다.
 
만약 URL 재작성이 BeginRequest 또는 AuthenticateRequest 이벤트에서 수행될 수 있다면, 로그인 페이지(보내지 버튼 클릭할 때)는 사용자를 재작성된 페이지로 리다이럭트할 것이다. 사용자가 브라우저의 주소창에 /people/ScottMitchell.aspx 입력하는 것을 상상해보자. 이 경로는 /info/employee.aspx?empID=1001로 재작성될 것이다. 만약 웹App가 Form 인증을 사용하도록 구성되었다면, 사용자는 처음 /people/ScottMitchell.aspx 할때, 처음에 URL는 /info/employee.aspx?empID=1001 로 재작성될 것이다. 다음에는 FormsAuthenticationModule 모듈이 실행할 것고,필요하다면 사용자를 로그인 페이지로 리다이렉시킬 것이다. 하지만 사용자가 성공적으로 로그인정보를 보낸 URL는 /info/employee.aspx?empID=1001 이 될 것이다. 왜냐하면 FormsAuthenticationModule모듈이 실행할 때 요청한 URL이기 때문이다.

 

BeginRequest 또는 AuthenticateRequest 이벤트에서 URL재작성을 수행할 때와 유사하게 UrlAuthorizationModule 모듈은 재작성된 URL를 본다. 이 말은 web.config 파일에서 특정 URL에 대한 인증을 지정하기 위해서 <location> 요소를 사용하는 것인다. 우리는 재작성된 URL를 잠조해야 한다.

 

이런한 미묘한 점을 고치기 위해서 AuthorizeRequest 이벤트에서 URL재작성을 수행할 것을 결정한다. 이러한 접근은 URL인증과 Form 인증 에외를 고치는 것이지만,이것은 새로운 걱정거리를 안겨준다. 뭐냐하면 File인증이 더이상 작동하지 않는다는 것이다. Windows인증을 사용할 때,FileAuthorizationModule 모듈은 인증된 사용자가 특정 ASP.NET페이지에 대한 접근권을 소지함을 확인하기 위함이다.

 

어떤 사용자들이 C:\Inetput\wwwroot\info\employee.aspx 에 대한 파일 접근권이 없음을 상상해보자. 만약 그러한 사용자가 /info/employee.aspx?empID=1001 에 방문하려고 시도한다면, 그들은 인증 오류에 직면할 것이다. 그러나 마얀 유리가 URL재작성을 AuthenticateRequest 이벤트로 이동시킨다면, FileAuthorizationModule 모듈이 보안설정정보를 검사할 때,요청되는 파일은/people/ScottMitchell.aspx 이라고 여전히 생각한다. 왜냐하면 그 URL이 재작성되어야 하기 때문이다. 그러므로 File인증 검사는 /info/employee.aspx?empID=1001를 보낸다. 이것은 사용자가 재작성된 URL의 내용을 볼 수 있도록 하는 것이다.

 

언제 URL 재작성은 HTTP 모듈에서 수행되어야 하는가? 답은 여러분이 채택한 인증 유형이 무엇이냐에 달려있다. 만약 여러문이 인증을 사용하지 않는다면, URL재작성이 BeginRequest, AuthenticateRequest 또는 AuthorizeRequest 에서 이루어지든지 어떤지 중요한 문제가 아니다. 만약 여러문이 Form 인증을 사용하고 Windows 인증을 사용하지 않는다면, RL재작성은 AuthorizeRequest 이트 핸들러에서 하도록 한다. 마지막으로 Windows 인증을 사용한다면, BeginRequest 또는 AuthenticateRequest 이벤트가 발생하는 동안 URL재작성을 스케줄링하라.

 

HTTP처리기에서 URL재작성

 

HTTP처리기 또는 HTTP처리기 팩토리 또한 URL 재작성을 수행할 수 있다. HTTP처리기는 특정 요청유형에 대한 내용을 발생시키는 것을 책임지는 클래스이다. HTTP처리기 팩토리는 HTTP처리기의 인스턴스를 반환에 책음을 진다.

 

이 글에서 우리는 ASP.NET 웹페이지에서 HTTP처리기 팩토리가 URL재작성을 생성하는 것을 살펴볼 것이다. HTTP처리기 팩토리는 IHttpHandlerFactory 인터페이스를 반드시 구현해야 한다. 이 인터페이스는 GetHander() 메소드를 갖는다. 적당한 HTTP모듈을 초기화한 후에 ASP.NET엔진은 무슨 HTTP처리기 또는 HTTP처리기 팩토리가 주어진 요청에 호출되는 결정한다. 만약 HTTP처리기 팩토리가 호출되면, ASP.NET엔진은 HTTP처리기 팩토리의 GetHandler() 메소드를 호출한다. 이 메소드는 웹요청에 대한 HttpContext를 전달한다. 그런 다음 HTTP처리기 팩토리는 요청을 처리할 수 있는 IHttpHandler를 구현하는 객체를 반드시 리턴해야 한다.

 

URL재작성을 HTTP처리기릍 통해서 우리는 URL이 재작성될 필요가 있는지를 검사하는 GetHandler() 메소드를 구현하는 HTTP처리기 팩토리를 생성한다. 만약 팩토리가 이전에 말한 것처럼 수행한다면 팩토리는 전달된(passed-in) HttpContext 객체의 RewritePath()메소드를 호출할 수 있다. 마지막으로 HTTP핸들러 팩토리는 System.Web.UI.PageParser 클래스의 GetCompiledPageInstance() 메소드가 리턴한 HTTP 핸들러를 리턴할 수 있다. (이것은 내장된 ASP.NET 웹 페이지 PageHandlerFactory가 작업하는 것과 동일한 기술이다.)

 

모든 HTTP모듈들이 사용자 정의 HTTP처리기팩토리가 초기화되기전에 초기화되기 때문에 HTTP처리기 패토리사용은 이벤트들의 나머지 단계에서 URL재작성할 때와 동일한 문제에 직면한다. 즉 File인증은 작동하지 않게 될 것이다. 그래서, 만약 여러문이 Windows 인증과 File 인증을 사용한다면 여러분은 URL재작성을 위해서 HTTP 모듈방식을 사용하고자 할 것이다.

 

다음 섹션에 걸쳐서 우리는 재사용할 수 있는 URL재사용 엔진을 구축하는 것을 살펴볼 것이다. 아래의 URL재작성 엔진의 예제에서 우리는 나머지 두 섹션으로 나누어 실제 사용할 만한 URL재작성에 대해서 시간을 할애할 것이다. 첫번째 우리는 URL재작성 엔진 사용법을 살피고 단순한 URL재작성 예제를 볼 것이다. 다음으로 우리는 진정한 의미의 "hackable" URL를 제공하기 위해서 재작성 엔진에 정규식 능력을 이용하여 힘을 더해줄 것이다.

 

URL 재작성 엔진 만들기

 

ASP.NET 웹 응용프로그램에서 URL재작성을 실행하는 방식의 설명을 위해서 URL재작성 엔진을 생성했다. 이 재작성 엔진은 아래와 같은 기능을 제공한다.

 

   1. URL재작성엔진을 이용하는 ASP.NET 개발자는 web.config 파일에서 재작성 규칙을

       지정할 수 있다.
   2. 재작성 규칙은 강력한 재작성 규칙을 지원하는 정규식을 이용할 수 있다.
   3. URL재작성규칙은 HTTP모듈 또는 HTTP처리기를 사용해서 쉽게 구성할 수 있다.

 

이 글에서 우리는 단지 HTTP모듈을 가지고 URL재작성을 살펴볼 것이다. HTTP처리기가 어떻게 URL재작성을 수행하는지는 첨부파일에서 확인할 수있다.


URL재작성 엔진에 구성정보 지정하기


web.config 파일에 재작성 규칙의 구조를 살펴보자. 먼저, HTTP모듈 또는 HTTP처리기로 URL재작성을 수행하길 바란다면 여러분은 web.config 파일에 있는 구조을 잘 보시길 바란다. 다운로드한 web.config 파일은 주석처리된 두가지 앤트리를 포함하고 있다. 아래와 같다.

 

<!--
<httpModules>
   <add type="URLRewriter.ModuleRewriter, URLRewriter"
        name="ModuleRewriter" />
</httpModules>
-->

 

<!--
<httpHandlers>
   <add verb="*" path="*.aspx"
        type="URLRewriter.RewriterFactoryHandler, URLRewriter" />
</httpHandlers>
-->

 

<httpModules>엔트리는 재작성을 위해 HTTP모듈을 사용하기 위해서 주석처리를 벗긴다. <httpHandlers>엔트리는 HTTP처리기를 재작성에 사용하기 위해서 주석처리를 벗긴다.

 

게다가 HTTP모듈 또는 HTTP처리기를 재작성에 사용할 지를 지정하기 위해서 web.config 파일은 재작성 규칙을 포함하고 있다. 재작성 규칙은 두개의 문자열로 구성된다. 요청된 URL를 검색하는 문자열 패턴, 찾은 패턴을 대체할 문자열이 그것이다. 이 정보는 web.config 파일에 아래의 sytax로 표현된다.

 

<RewriterConfig>
   <Rules>
   <RewriterRule>
      <LookFor>검색할 패턴</LookFor>
      <SendTo>패턴과 대체할 문자열</SendTo>
   </RewriterRule>
   <RewriterRule>
      <LookFor>검색할 패턴</LookFor>
      <SendTo>패턴과 대체할 문자열</SendTo>
   </RewriterRule>
   ...
   </Rules>
</RewriterConfig>

 

각각의 재작성 규칙은 <RewriteRule>요소로 표현된다. 검색할 패턴은 <LookFor>요소로 지정된다. 대체할 문자열은 <SendTo> 요소로 지정된다. 이런 재작성 규칙은 Top-Bottom식이 된다. 매치가 이루어지면 URL는 재작성되고 재작성 규칙을 통한 검색은 종료하게 된다.

 

<LookFor>요소에 패턴을 지정할 때,정규식이 매칭과 문자열 대체를 수행하는데 이용된다. (잠시 실제 예제를 보자. 정규식으로 패턴검색은 어떻게 하는지?) 패턴은 정규식이므로, 정규식에 예약된 character는 무시된다. (어떤 정규식의 예약어들은 .,?,^,$ 기타 다른 character를 포함한다. 이것들은 백스래쉬(\.)로 묶어서 피할 수 있다.

 

HTTP모듈로 URL 재작성하기

 

HTTP모듈은 IHttpModule인터페이스를 구현하는 클래스를 생성하는 것만큼 간단하다. IHttpModule 인터페이스는 두가지 메소스들을 정의한다.

 

   1. Init(HttpApplication). 이 메소드는 HTTP모듈이 초기화될때 발생한다.

       이 메소드에서 적당한 HttpApplication 이벤트에 이벤트 핸틀러를 묶을 것이다.

   2. Dispose(). 이 메소드는 요청이 완료되고 IIS로 보내졌을 때 호출된다.

       마지막의 청소작업이 이 곳에서 수행되어야 한다.

 

URL재작성에 대한 HTTP모듈 생성을 편하게 하기 위해서 추상 기본 클래스, BaseModuleRewriter를 생성하는 것에서 시작한다. Init()이벤트에서 이 클래스는 HttpApplicationAuthorizeRequest 이벤트를 BaseModuleRewriter_AuthorizeRequest 에 묶는다. BaseModuleRewriter_AuthorizeRequest 메소드는 이 클래스의 ReWrite()를 호출한다. BaseModuleRewriter_AuthorizeRequest 메소드는 Init()메소드에 전달된 HttpApplication 객체와 관련한 요청된 경로 정보를 전달한다. ReWrite()메소드는 추상 메소드이다. 추상 클래스 BaseModuleRewriterReWrite() 메소드는 메소드 구현부분이 없다는 것이다. 대개는 BaseModuleRewriter를 상속하는 클래스에서 이 메소드를 오버라이드해서 구현한다.

 

이 베이스 클래스를 가지고서 이제 우리가 해야할 일들은 BaseModuleRewriter를 상속하는 클래스를 생성하고 ReWrite()메소드를 오버라드하고 URL재작성 로직을 작성하는 것이다. BaseModuleRewriter 클래스 코드는 아래와 같다.


public abstract class BaseModuleRewriter : IHttpModule
{
   public virtual void Init(HttpApplication app)
   {
      // 경고! 이것은 Windows 인증에서는 작동하지 않는다.
      // Windows인증을 원한다면 app.BeginRequest 를 변경해야한다.
      app.AuthorizeRequest += new
         EventHandler(this.BaseModuleRewriter_AuthorizeRequest);
   }

   public virtual void Dispose() {}

   protected virtual void BaseModuleRewriter_AuthorizeRequest(
     object sender, EventArgs e)
   {
      HttpApplication app = (HttpApplication) sender;
      Rewrite(app.Request.Path, app);
   }

   protected abstract void Rewrite(string requestedPath,
     HttpApplication app);
}

 

BaseModuleRewriter 클래스는 AuthorizeRequest 이벤트에서 URL 재작성을 수행함을 주의하라. Windows 인증 와 File 인증을 사용한다면 이 코드는 변경될 필요가 있다.즉 URL재작성은 BeginRequest 또는 AuthenticateRequest 이벤트에서 수행될 것이다.

ModuleRewriter 클래스는 BaseModuleRewriter 상속하고 실제 URL재작성을 수행한다. ModuleRewriter 클래스는 하나의 오버라이드 메소드—Rewrite()—를 포함한다. 결과는 아래와 같다.

 

protected override void Rewrite(string requestedPath,
   System.Web.HttpApplication app)
{
   // 환경설정 규칙을 얻는다.
   RewriterRuleCollection rules =
             RewriterConfiguration.GetConfig().Rules;

   // 각각의 규칙을 반복한다.
   for(int i = 0; i < rules.Count; i++)
   {
      // 검색패턴을 얻고
      // Url 분해한다(분석한다)(~를 적당한 디렉토리로 전환한다.)
      string lookFor = "^" +
                RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath,
                rules[i].LookFor) + "$";

      // Regex를 생성한다.(IgnoreCase를 설정하는 것을 잊지말자)
      Regex re = new Regex(lookFor, RegexOptions.IgnoreCase);

      // 매치가 발견되는지 보자
      if (re.IsMatch(requestedPath))
      {
           // 매치가 발견되었다. 필요하면 대체작업을 해라
         string sendToUrl =
                  RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath,
                  re.Replace(requestedPath, rules[i].SendTo));

          // URL를 다시 쓰자
         RewriterUtils.RewriteUrl(app.Context, sendToUrl);
         break;      // exit the for loop
      }
   }
}

 

ReWrite() 메소드는 web.config 파이로 뿌터 재작성 규칙을 가져와서 작업을 시작한다. 이것는 한번에 하나씩 재작성 규칙을 반복한다. LookFor 속성을 가져오고 요청 URL에서 매칭이 발견되는지를 결정하기 위해서 정규식을 사용한다.

 

만약 매치가 발견되면 정규식은 요청경로을 SendTo 속성값로 대체하는 작업을 수행한다. 이 대체된 URL은 RewriterUtils.RewriteUrl() 메소드로 전달된다. RewriterUtils는 HTTP모듈과 HTTP처리기에서 URL재작서에 사용되는 몇가지 정적 메소드를  제공하는 핼퍼클래스이다. RewriterUrl() 메소드는 단순히 HttpContext 객체의 RewriteUrl() 메소드를 호출한다.

 

주의: 여러분은 정규식 매치와 대체를 수행할 때 RewriterUtils.ResolveUrl() 호출됨을 주의할 것이다. 이 핼퍼 메소드는 단순히 문자열에 있는 ~의 인스턴스를 application의 경로 값으로 대체한다.

 

URL재작성 엔진에 대한 전체 코드는 첨부파일에 있다. 우리는 대부분 중요부분은 조사했다. 그러나 web.config파일에 있는  XML로 포맷된 재작성규칙를 객체로 deserializing하는 클래스,URL재작성을 위한  HTTP처리기 팩토리와 같은 클래스에 대해서는 건너 뛰었다. 이 글의 나머지 부분에서는 URL재작성의 현실에서 이용하는 것을 조사할 것이다.

 

URL 재작성 엔진을 가지고 단순한 URL재작성을 수행하기

 

실제 URL 재작성을 시현하기 위해서 우리는 ASP.NET 웹프로그램을 작성한다. 이 프로그램은 단순한 URL재작성을 이용할 것이다. 우리가 온라인으로 상품을 판매하는 업체에 근무한다고 생각하자. 제품들은 아래처럼 카테고리로 분류되었다.

 

Category ID Category Name
1 Beverages
2 Condiments
3 Confections
4 Dairy Products
... ...

 

ListProductsByCategory.aspx라는 ASP.NET 웹페이지를 생성했다고 가정하다. 이 페이지는 질의문에서 Category ID값을 받고 그 카테고리에 속하는 모든 제품을 보여줄 것이다. 그래서 Beverages 를 조회하고자 하는 사용자는 ListProductsByCategory.aspx?CategoryID=1을 방문할 것이다. 반면에 Dairy Products 를 조회하고자 하는 사용자는  ListProductsByCategory.aspx?CategoryID=4 를 방문할 것이다. 또한 우리는 모든 카테고리를 보여주는 ListCategories.aspx 페이지를 이미 만들었다.

 

분명히 이것은 URL재작성의 경우이다. 사용자에게 보여지는 URL들은 사용자에게 어떤 중요성을 주지못할 뿐만아니아 "hackability"를 주지못한다.  이런 방식보다는 URL재작성을 채택해서 사용자가 /Products/Beverages.aspx 방문할 때 사용자들의 URL이 ListProductsByCategory.aspx?CategoryID=1로 재작성될 것이다. 우리는 web.config 파일에서 다음과 같이 URL 재작성을 완성할 수 있다.


<RewriterConfig>
   <Rules>
      <!-- 제품 리스트 규칙 -->
      <RewriterRule>
         <LookFor>~/Products/Beverages\.aspx</LookFor>
         <SendTo>~/ListProductsByCategory.aspx?CategoryID=1</SendTo>
      </RewriterRule>
      <RewriterRule>
   </Rules>
</RewriterConfig>

 

여러분들이 보는 것처럼,이 규칙은 사용자가 요청한 경로가 /Products/Beverages.aspx 인지를 보기위해서 검색하는 것이다. 만약 그렇다면 이 규칙은 URL는 /ListProductsByCategory.aspx?CategoryID=1 로 다시 작성할 것이다.

 

주의: <LookFor>요소는 Beverages.aspx에서 마침표(.)를 무시한다. 왜냐하면 <LookFor> 값은 정규식 패턴에서 사용되기 때문이다. 마침표(.)는 정규식에서 특수문자이다. (어떤 문자도 매치된다라는 의미이다) 예를 들어 /Products/BeveragesQaspx 이런 URL도 매치되는 것이다.그러면 이상한잔아. 마침표를 피함으로써(\. 요걸 사용해서) 문자그대로의 마침표를 매치하고자 함을 알리는 것이다.

 

이런 규칙으로 /Products/Beverages.aspx 에 사용자가 방문할때 사용자들은 beverages 리스트를 보게될 것이다. 그림에서는 /Products/Beverages.aspx 방문한 브라우저의 스크린샷이다. 브라우저의 주소창에서  URL는 /Products/Beverages.aspx이지만, 실제 사용자는 ListProductsByCategory.aspx?CategoryID=1 내용을 보고있는 것이다. (사실, 웹서버상에는 /Products/Beverages.aspx 파일이 존재하지도 않는다.)

 


Figure 3. Requesting category after rewriting URL


/Products/Beverages.aspx 와 유사하게, 다음에 우리는 다른 제품 카테고리에 작성규칙을 추가할 것이다. 이것은 web.config 파일의 <Rules>용소 내부에 <RewriteRule> 요소를 추가만 하는 것이다. 첨부파일을 참조하기 바람.

 

Download the source code for this article.

 

URL 보다 "hackable"하게 만들기 위해서,만약 사용자가 /Products/Beverages.aspx 로부터 Beverages.aspx를 간단히 분리할 수 있고 제품 카테고리의 리스가 보여질 수 있다면 좋을 것이다. 언뜻보기에 이것은 사소한 것같다. 단지 /Products//ListCategories.aspx에 매핑하는 재작성규칙을 추가하는 것이다. 그러나 상당히 불가사의한 것이 있다. 뭐냐하면 여러분이 /Products/ 디렉토리를 먼저 생성해야 한다. 그리고 /Products/ 디렉토리에 아무런 내용이 없는 빈 Default.aspx 파일을 추가하는 것이다.

 

왜 이런 추가적인 단계가 필요한지 이해하기 위해서 URL재작성 엔진이 ASP.NET 수준에서 존재한다는 점을 상기시키기 바란다. 즉, ASP.NET 엔진이 요청을 처리하는 기회를 전혀 갖는 못한다면 URL재작성 엔진이 들어오는 URL를 감지할 수 있는 어떤 방법도 존재하지 않게 된다. 더구나 요청된 파일이 적당한 확장자를 갖기만 한다면 IIS는 들어오는 요청정보를 ASP.NET 엔진에 전달한다는 점을 기억을 해보라. 그래서 사용자가 /Products/에 방문하면 IIS는 어떤 파일 확장자를 감지할 수 없게 된다. 따라서 IIS는 기본 파일명의 하나에 그러한 파일이 존재하는지를 확인하기 위해서 디렉토리를 검사하게 된다. (Default.aspx, Default.html, Default.asp 등등. 이런 기본 파일명들은 IIS 관리자의 웹 서버 속성 대화상자의 문서탭에서 정의된다.)

 

그래서, 우리는 /Products/ 디렉토리를 생성할 필요가 있다. 추가적으로, 우리는 이 디렉토리에 하나의 파일(Default.aspx)을 생성할 필요가 있다. 사용자가 /Products/에 방문할 때 IIS 이 디렉토리를 검사하게 된다. 이는 Default.aspx 파일이 이 디렉토리에 있는지 확인하는 것이다. 그런 다음 ASP.NET 엔진에 처리를 넘겨주는 것이다. 그런 다음에 URL재작성기가 URL 재작성을 시도하게 되는 것이다.

 

디렉토리와 Default.aspx 파일을 생성한 후에 <Rules> 요소에 다음과 같은 재작성 규칙을 추가한다.


<RewriterRule>
   <LookFor>~/Products/Default\.aspx</LookFor>
   <SendTo>~/ListCategories.aspx</SendTo>
</RewriterRule>

 

이런 규칙으로 사용자가 /Products/ 또는 /Products/Default.aspx 에 방문할 때,사용자들은 제품 카테고리의 리스트를 보게 될 것이다.(그림 4)


Figure 4. Adding "hackability" to the URL

 

PostBack 처리하기

 

만약 여러분이 다시 작성하고 있는 URL는 서버사이트 웹폼을 포함하고 Postback을 수행한다면 폼이 postback될때 기본적인 URL이 사용될 것이다. 즉, 만약 사용자가 브라우저에 들어오면, /Products/Beverages.aspx, 그들은 주조창에서 /Products/Beverages.aspx 를 보게 될 것이다. 그러나 ListProductsByCategory.aspx?CategoryID=1에 대한 내용이 보여질 것이다. 만약 ListProductsByCategory.aspxpostback을 수행하면 사용자는 /Products/Beverages.aspx로 이동하는 것이 아니고 ListProductsByCategory.aspx?CategoryID=1로 이동하게 될 것이다. 이것은 어떤 것을 망치는 것은 아니지만 버튼을 클릭했는데 URL이 갑자기 변해버리는 것을 보게 되는 사용자들은 당황스러울 수 있다.

 

이런 현상이 발생하는 이유는 웹폼이 랜더링될때, 웹폼은 명시적으로 Request 객체의 파일 경로 값에 액션(action)특성을 설정하기 때문이다. 물론, 웹폼 랜더링 될때,URL은 /Products/Beverages.aspx 에서 ListProductsByCategory.aspx?CategoryID=1로 다시 작성되었다. 즉 Request 객체는 사용자가 ListProductsByCategory.aspx?CategoryID=1에 방문하고 있다는 것을 리포팅하고 있다. 이 문제는 서버사이드 폼이 액션특성(action attribute)를 랜더링하지 못하게 하면 고칠 수 있다. (기본적으로 브라우저는 폼이 액션 특성을 포함하지 않으면 postback 할 것이다.)

 

불행하게도, 웹폼에 액션특성을 명시적으로 지정할 수 없다. 또한 액션 특성의 랜더링을 비활성시키도록 어떤 속성들을 설정하는 것을 막고있다. 이런 점 때문에 우리는 System.Web.HtmlControls.HtmlForm 클래스를 상속해서 확장할 것이다. RenderAttribute() 메소드를 오버라이드하고 명시적으로 액션특성들을 랜더링하지 못하게 할 것이다.

 

강력한 상속개념 덕택에 우리는 HtmlForm클래스의 모든 기능을 얻을 수 있다. 단지 몇 라인만으로 추가해서 우리가 원하는 기능을 얻을 수 있다. 우리가 만든 클래스의 전체 소스는 다음과 같다.

 

namespace ActionlessForm {
  public class Form : System.Web.UI.HtmlControls.HtmlForm
  {
     protected override void RenderAttributes(HtmlTextWriter writer)
     {
        writer.WriteAttribute("name", this.Name);
        base.Attributes.Remove("name");

        writer.WriteAttribute("method", this.Method);
        base.Attributes.Remove("method");

        this.Attributes.Render(writer);

        base.Attributes.Remove("action");

        if (base.ID != null)
           writer.WriteAttribute("id", base.ClientID);
     }
  }
}


오버라이드된 RenderAttributes() 메소드는 HtmlForm 클래스의 RenderAttributes() 메소드로부터 그대로 얻어왔다. 단지 action 특성에 없어서 추가했을 뿐이다.(Lutz Roeder Reflector 사용)


일단 여러문이 이 클래스를 생성하고 컴파일하면 웹프로그램의 참조폴더에 추가하시라. ASP.NET 웹페이지이 상단에 다음 내용을 추가하시라.

 

<%@ Register TagPrefix="skm" Namespace="ActionlessForm"
   Assembly="ActionlessForm" %>

 

그런 다음, <form runat="server"> 이 부분을
<skm:Form id="Form1" method="post" runat="server">  
이렇게 바꾸고

닫기 태그 </form></skm:Form> 로 바꾸길..


주의: 만약 다시 작성하는 URL이 postback를 수행하지 않으면 위와 같은 사용자 정의 클래스는 필요없다.

 

진정한 "Hackable" URL 만들기

 

이전 섹션에서 본 URL재작성 데모는 URL 재작성엔진이 새로운 재작성규칙으로 쉽게 구성될 수 있는 방법을 보여주는 것이었다. 재작성규칙의 진정으로 강력함은 이 세션에서 알 수 있듯이 정규식을 사용할 때 챙피할 정도이다.

 

블러그(Blog)는 점점더 인기를 얻고 있다. 모든 사람들이 자신의 블러그를 잦고 있는 것 같다. 만약 여러문이 블러그에 익숙하지 않으면 블러그는 전형적으로 온라인 저널에서의 자주 업데이트되는 개인 페이지와 같은 것이다. 대부분의 블러거들은 매일매일 일어나는 일들과 같은 것들을 작성하고 다른 이들은 특정 테마에-영화리뷰,스포츠팀,컴퓨터공학- 대한 블러깅에 치중한다.

 

한 저자에 의하면 블러그는 하루에 몇번씩에서 매주 또는 두주씩 해서 업데이트된다고 한다.  전형적으로 블러그 홈페이지는 대개 10개의 엔트리를 보여준다. 그러나 모든 블러깅 소프트웨어는 보다 오래된 글들을 읽을 수 있도록 기록보관(Archive) 기능을 제공한다. 블러그들은 "hackable" URL에 관한한 대단한 프로그램이다. 블러그의 Archive에서 검색하는 동안 URL /2004/07/14.aspx에 당신 자신을 찾았다는 것을 상상해보라.  2004년 7월 14일에 올린 글을 읽고 있는 자신을 발견한다면 얼마나 놀라운 일인가? 더구나 당신 2004년 7월에 올린 모든 글을 보기를 원할지도 모르는 일이다. 이럴 경우 URL에 단순히 /2004/07/를 치기만 하면된다. 2004년에 올린 모든 글을 보고싶다면 /2004/만 치면 된다.

 

블러그를 유지할 때,방문자에게 "hackable" URL를 제공하면 좋을 것이다.  많은 블러그들이 이런 기능을 제공한다. 자 ,이것은 URL재작성 기법을 사용해서 어떻게 이룩할 수 있는 지를 살펴보자.

 

첫째, 우리는 하나의 ASP.NET 웹페이지가 필요하다. 이 페이지는 년별, 월별, 일별로 블러그 엔트리를 보여준다. 페이지 ShowBlogContent.aspx의 질의문 파라미터는 year, month, day를 취한다. 2004년 7월 14일자의 글들을 보기위해서 ShowBlogContent.aspx?year=2004&month=7&day=14 을 방문할 수 있다. 2004년 7월의 글들을 보고위해서 ShowBlogContent.aspx?year=2004&month=2, 2004년 전체 글을 보기위해서 ShowBlogContent.aspx?year=2004 그래서, 만약 사용자 /2004/02/14.aspx 방문한다면, 우리는 ShowBlogContent.aspx?year=2004&month=2&day=14의 내용을 보여주기 위해서 URL를 다시 작성할 필요가 있다. 이런 모든 경우—URL이 년월일 지정할때, 년월지정할때, 년만 지정할 때—3 가지 재작성 규칙을 처리할 수 있어야 한다.


<RewriterConfig>
   <Rules>
      <!-- Rules for Blog Content Displayer -->
      <RewriterRule>
         <LookFor>~/(\d{4})/(\d{2})/(\d{2})\.aspx</LookFor>
         <SendTo>~/ShowBlogContent.aspx?year=$1&month=$2&day=$3</SendTo>
      </RewriterRule>
      <RewriterRule>
         <LookFor>~/(\d{4})/(\d{2})/Default\.aspx</LookFor>
         <SendTo><![CDATA[~/ShowBlogContent.aspx?year=$1&month=$2]]></SendTo>
      </RewriterRule>
      <RewriterRule>
         <LookFor>~/(\d{4})/Default\.aspx</LookFor>
         <SendTo>~/ShowBlogContent.aspx?year=$1</SendTo>
      </RewriterRule>
   </Rules>
</RewriterConfig>

 

이런 재작성 규칙은 강력한 정규식의 힘에 있다. 첫번째 규칙에서,우리는 (\d{4})/(\d{2})/(\d{2})\.aspx 패턴을 갖는 URL를 찾는다. 영어에서 이것은 4자리 문자열, 슬래쉬, 2두자리문자열, 슬래쉬, 2두자리 문자열와 매치되는 것이다. 각 숫자을 묶는 괄호는 중요하다. 이것은 <SendTo> 요소의 속성에 괄호내부에 있는 매치된 문자를 참조할 수 있게 한다. 특히,우리는 $1은 첫번째 괄호, $2는 두번째 괄호, $3은 세번째 괄호와 매치되는 것이다.

 

주의: web.config 파일은 XML로 포맷되었기 때문에  &, <, > 이와 같은 문자는 무시되어야 한다. 첫번째 규칙의 <SendTo> 요소, &&amp;로 전화된다. 두번째 규칙의 <SendTo>에서 대안으로 사용되는 것을 제시하고 있다. 다른 방법도 가능하며 동일한 목적을 달성한다—by using a <![CDATA[...]]> element, the contents inside do not need to be escaped. Either approach is acceptable and accomplishes the same end.

 

Figures 5, 6, and 7 show the URL rewriting in action. The data is actually being pulled from my blog, http://ScottOnWriting.NET. In Figure 5, the posts for November 7, 2003 are shown; in Figure 6 all posts for November 2003 are shown; Figure 7 shows all posts for 2003.


Figure 5. Posts for November 7, 2003

 

Figure 6. All posts for November 2003

 

Figure 7. All posts for 2003

 

주의: URL 재작성 엔진은 <LookFor>요소에서 정규식 패턴을 이용한다. 만약 정규식에 낯설면, http://www.4guysfromrolla.com/webtech/090199-1.shtml
An Introduction to Regular Expressions 참조하시길. 또한 일반적으로 사용되는 정규식을 얻을 수있다. http://www.RegExLib.com


필요한 디렉토리 구조 만들기

 

요청 /2004/03/19.aspx 로 들오어면, IIS는 .aspx 확장자를 알리고 요청을 ASP.NET 엔진으로 라우트한다. 요청이 ASP.NET 엔진으로 이동할 때, URL은 ShowBlogContent.aspx?year=2004&month=03&day=19 로 재작성되고 방문자는 2004년 3월 19일짜 엔트리를 볼 것이다. 그러나 사용자가 /2004/03/ 로 이동할 때 무슨 일이 발생하는가? /2004/03/ 디렉토리가 존재하지 않으면, IIS 는 404 에러를 리턴할 것이다. 더우기 그 디렉토리에는 Default.aspx 페이지가 필요하다. 그 결과 요청정보가 ASP.NET 엔진으로 넘겨지는 것이다.

 

이런 방식으로 여러분은 매년 블러그 엔트리가 있는 년도 디렉토리를 손으로 생성해야 한다. 추가적으로 각각의 년도 디렉토리에는 월별 디렉토리와 default.aspx 파일이 필요하다. (이전에 우리는 똑같은 일을 했다.이전 섹션에서 /Products/ 디렉토리, Default.aspx 파일, 그래서 /Products/에 방문하면 ListCategories.aspx 가 보게된다.)

 

분명히,이런 디렉토리를 추가하는 구조는 고통스럽다. 이 문제의 해결책은 모든 IIS 요청을 ASP.NET 엔진에 맵핑한 것에 있다. URL /2004/03/로 방문하더라도 IIS는 /2004/03/ 디렉토리가 존재하지 않아도 요청을 ASP.NET 엔진에 충실히 전달하는 방식이다. 그러나 이런식의 접근을 사용하면 ASP.NET 엔진이 웹서버에 들어오는 모든 요청들의 유형(이미지, CSS파일, Javascript파일, Flash 등등)을 처리해야 하는 책임을 지게된다.

 

모든 파일 유형을 처리하는 것을 토론하는 것은 이 글의 범위를 넘어선다. 이에 대한 예제는 .Text,공개소스 블러그 엔진이 있다. .Text는 ASP.NET 엔진에 모든요청을 맵핑하도록 할 수 있다. 이것은  사용자 정의 HTTP처리기를 이용해서 모든 파일 유형의 서비스를 처리할 수 있다. 이곳의 HTTP처리기는 전형적인 정적 파일 유형(image,CSS etc)을 서비스하는 방법을 알고 있다.

 

결과


이 글에서 우리는 HttpContext 클래스의 RewriteUrl() 메소드를 통해서 ASP.NET 수준에서 URL재작성을 수행하는 방법을 살펴보았다. 우리가 보았던 것처럼 RewirteUrl()는 특정  HttpContextRequest 속성을 업데이트한다. 즉 무슨 파일과 경로가 요청되고 있는지를 업데이트한다. 사용자측면에서 순수한 효과는 사용자들이 특정 URL를 방문하는 것이지 웹서버측면에 요청하는 것과는 다른 URL이다.

 

URL는 HTTP모듈 또는 HTTP처리기에서 재작성될 수 있다. 이 글에서 우리는 재작성을 수행하기 위해서 HTTP모듈을 이용해서 살펴보았다. 다른 단계에서 재작성을 수행하는 결과도 보았다.

물론, ASP.NET수준의 재작성, URL재작성은 만약 요청이 IIS에서 ASP.NET 엔진으로 성공적으로 전달되면 발생할 수 있는 것이다.  사용자가 확장자 .aspx인 페이지를 요청할 때 자연스럽게 발생한다. 그러나 만약 실제로 존재하지 않는 URL를 사람들이 입력할 수 있도록 원한다면  거짓 디렉토리와 Default.aspx 페이지를 생성하거나 모든 요청 정보를 ASP.NET 엔진에 라우트하도록 IIS 구성정보를 설정해야 한다.


첨언.

 

URL재작성은 ASP.NET 과 경쟁이 심한 서버사이드 웹 기술에 많은 주목을 받고 있는 주제이다. 예를 들어 아파치 웹 서버는 mod_rewrite이라는 URL재작성 모듈을 재공한다. mod_rewrite는 견고한 재작성 엔진이다.

 

ASP.NET에 URL재작성에 관한 참고 글이다.
Rewrite.NET - A URL Rewriting Engine for .NET


mod_rewrite의 정규식 규칙을 모방해서 URL재작성 엔진을 생성하는 예제이다.

URL Rewriting With ASP.NET

 

ASP.NET의 URL 재작성 능력에 대한 좋은 소개글이다.

Ian Griffiths 은 이 글에서 논의한 Postback문제와 같은 URL재작성에 대한  blog entry

Fabrice Marguerie (read more) 과Jason Salas (read more) 은 검색엔진을 향상시키기 위해서 URL 재작성 사용.

 

원문.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnaspp/html/urlrewriting.asp

 

관련글.

http://scottwater.com/blog/archive/2004/04/14/RedirectingModule.aspx

http://ewal.net/PermaLink,guid,f314a8bc-4a97-4a77-b2de-c2771b77f222.aspx

http://www.lastknight.com/DotNet-Url-Rewriting-and-Caching-Engine.aspx

+ Recent posts