본문 바로가기
개발언어/ASP.NET

rewritepath

by 엔돌슨 2008. 3. 24.
반응형
2008/03/24 - [개발언어/ASP.NET] - RewritePath


http://msdn2.microsoft.com/en-us/library/ms972974.aspx

관련  msdn이다
URL Rewriting in ASP.NET
ASP.NET Technical Articles
URL Rewriting in ASP.NET
 

Scott Mitchell
4GuysFromRolla.com

March 2004

Applies to:
   Microsoft® ASP.NET

Summary: Examines how to perform dynamic URL rewriting with Microsoft ASP.NET. URL rewriting is the process of intercepting an incoming Web request and automatically redirecting it to a different URL. Discusses the various techniques for implementing URL rewriting, and examines real-world scenarios of URL rewriting. (31 printed pages)

Download the source code for this article.

Contents

Introduction
Common Uses of URL Rewriting
What Happens When a Request Reaches IIS
Implementing URL Rewriting
Building a URL Rewriting Engine
Performing Simple URL Rewriting with the URL Rewriting Engine
Creating Truly "Hackable" URLs
Conclusion
Related Books

Introduction

Take a moment to look at some of the URLs on your website. Do you find URLs like http://yoursite.com/info/dispEmployeeInfo.aspx?EmpID=459-099&type=summary? Or maybe you have a bunch of Web pages that were moved from one directory or website to another, resulting in broken links for visitors who have bookmarked the old URLs. In this article we'll look at using URL rewriting to shorten those ugly URLs to meaningful, memorable ones, by replacing http://yoursite.com/info/dispEmployeeInfo.aspx?EmpID=459-099&type=summary with something like http://yoursite.com/people/sales/chuck.smith. We'll also see how URL rewriting can be used to create an intelligent 404 error.

URL rewriting is the process of intercepting an incoming Web request and redirecting the request to a different resource. When performing URL rewriting, typically the URL being requested is checked and, based on its value, the request is redirected to a different URL. For example, in the case where a website restructuring caused all of the Web pages in the /people/ directory to be moved to a /info/employees/ directory, you would want to use URL rewriting to check if a Web request was intended for a file in the /people/ directory. If the request was for a file in the /people/ directory, you'd want to automatically redirect the request to the same file, but in the /info/employees/ directory instead.

With classic ASP, the only way to utilize URL rewriting was to write an ISAPI filter or to buy a third-party product that offered URL rewriting capabilities. With Microsoft® ASP.NET, however, you can easily create your own URL rewriting software in a number of ways. In this article we'll examine the techniques available to ASP.NET developers for implementing URL rewriting, and then turn to some real-world uses of URL rewriting. Before we delve into the technological specifics of URL rewriting, let's first take a look at some everyday scenarios where URL rewriting can be employed.

Common Uses of URL Rewriting

Creating data-driven ASP.NET websites often results in a single Web page that displays a subset of the database's data based on querystring parameters. For example, in designing an e-commerce site, one of your tasks would be to allow users to browse through the products for sale. To facilitate this, you might create a page called displayCategory.aspx that would display the products for a given category. The category's products to view would be specified by a querystring parameter. That is, if the user wanted to browse the Widgets for sale, and all Widgets had a had a CategoryID of 5, the user would visit: http://yousite.com/displayCategory.aspx?CategoryID=5.

There are two downsides to creating a website with such URLs. First, from the end user's perspective, the URL http://yousite.com/displayCategory.aspx?CategoryID=5 is a mess. Usability expert Jakob Neilsen recommends that URLs be chosen so that they:

  • Are short.
  • Are easy to type.
  • Visualize the site structure.
  • "Hackable," allowing the user to navigate through the site by hacking off parts of the URL.

I would add to that list that URLs should also be easy to remember. The URL http://yousite.com/displayCategory.aspx?CategoryID=5 meets none of Neilsen's criteria, nor is it easy to remember. Asking users to type in querystring values makes a URL hard to type and makes the URL "hackable" only by experienced Web developers who have an understanding of the purpose of querystring parameters and their name/value pair structure.

A better approach is to allow for a sensible, memorable URL, such as http://yoursite.com/products/Widgets. By just looking at the URL you can infer what will be displayed—information about Widgets. The URL is easy to remember and share, too. I can tell my colleague, "Check out yoursite.com/products/Widgets," and she'll likely be able to bring up the page without needing to ask me again what the URL was. (Try doing that with, say, an Amazon.com page!) The URL also appears, and should behave, "hackable." That is, if the user hacks of the end of the URL, and types in http://yoursite.com/products, they should see a listing of all products, or at least a listing of all categories of products they can view.

Note   For a prime example of a "hackable" URL, consider the URLs generated by many blog engines. To view the posts for January 28, 2004, one visits a URL like http://someblog.com/2004/01/28. If the URL is hacked down to http://someblog.com/2004/01, the user will see all posts for January 2004. Cutting it down further to http://someblog.com/2004 will display all posts for the year 2004.

In addition to simplifying URLs, URL rewriting is also often used to handle website restructuring that would otherwise result in numerous broken links and outdated bookmarks.

What Happens When a Request Reaches IIS

Before we examine exactly how to implement URL rewriting, it's important that we have an understanding of how incoming requests are handled by Microsoft® Internet Information Services (IIS). When a request arrives at an IIS Web server, IIS examines the requested file's extension to determine how handle the request. Requests can be handled natively by IIS—as are HTML pages, images, and other static content—or IIS can route the request to an ISAPI extension. (An ISAPI extension is an unmanaged, compiled class that handles an incoming Web request. Its task is to generate the content for the requested resource.)

For example, if a request comes in for a Web page named Info.asp, IIS will route the message to the asp.dll ISAPI extension. This ISAPI extension will then load the requested ASP page, execute it, and return its rendered HTML to IIS, which will then send it back to the requesting client. For ASP.NET pages, IIS routes the message to the aspnet_isapi.dll ISAPI extension. The aspnet_isapi.dll ISAPI extension then hands off processing to the managed ASP.NET worker process, which processes the request, returning the ASP.NET Web page's rendered HTML.

You can customize IIS to specify what extensions are mapped to what ISAPI extensions. Figure 1 shows the Application Configuration dialog box from the Internet Information Services Administrative Tool. Note that the ASP.NET-related extensions—.aspx, .ascx, .config, .asmx, .rem, .cs, .vb, and others—are all mapped to the aspnet_isapi.dll ISAPI extension.

Figure 1. Configured mappings for file extensions

A thorough discussion of how IIS manages incoming requests is a bit beyond the scope of this article. A great, in-depth discussion, though, can be found in Michele Leroux Bustamante's article Inside IIS and ASP.NET. It's important to understand that the ASP.NET engine gets its hands only on incoming Web requests whose extensions are explicitly mapped to the aspnet_isapi.dll in IIS.

Examining Requests with ISAPI Filters

In addition to mapping the incoming Web request's file extension to the appropriate ISAPI extension, IIS also performs a number of other tasks. For example, IIS attempts to authenticate the user making the request and determine if the authenticated user has authorization to access the requested file. During the lifetime of handling a request, IIS passes through several states. At each state, IIS raises an event that can be programmatically handled using ISAPI filters.

Like ISAPI extensions, ISAPI filters are blocks of unmanaged code installed on the Web server. ISAPI extensions are designed to generate the response for a request to a particular file type. ISAPI filters, on the other hand, contain code to respond to events raised by IIS. ISAPI filters can intercept and even modify the incoming and outgoing data. ISAPI filters have numerous applications, including:

  • Authentication and authorization.
  • Logging and monitoring.
  • HTTP compression.
  • URL rewriting.

While ISAPI filters can be used to perform URL rewriting, this article examines implementing URL rewriting using ASP.NET. However, we will discuss the tradeoffs between implementing URL rewriting as an ISAPI filter versus using techniques available in ASP.NET.

What Happens When a Request Enters the ASP.NET Engine

Prior to ASP.NET, URL rewriting on IIS Web servers needed to be implemented using an ISAPI filter. URL rewriting is possible with ASP.NET because the ASP.NET engine is strikingly similar to IIS. The similarities arise because the ASP.NET engine:

  1. Raises events as it processes a request.
  2. Allows an arbitrary number of HTTP modules handle the events that are raised, akin to IIS's ISAPI filters.
  3. Delegates rendering the requested resource to an HTTP handler, which is akin to IIS's ISAPI extensions.

Like IIS, during the lifetime of a request the ASP.NET engine fires events signaling its change from one state of processing to another. The BeginRequest event, for example, is fired when the ASP.NET engine first responds to a request. The AuthenticateRequest event fires next, which occurs when the identity of the user has been established. (There are numerous other events—AuthorizeRequest, ResolveRequestCache, and EndRequest, among others. These events are events of the System.Web.HttpApplication class; for more information consult the HttpApplication Class Overview technical documentation.)

As we discussed in the previous section, ISAPI filters can be created to respond to the events raised by IIS. In a similar vein, ASP.NET provides HTTP modules that can respond to the events raised by the ASP.NET engine. An ASP.NET Web application can be configured to have multiple HTTP modules. For each request processed by the ASP.NET engine, each configured HTTP module is initialized and allowed to wire up event handlers to the events raised during the processing of the request. Realize that there are a number of built-in HTTP modules utilized on each an every request. One of the built-in HTTP modules is the FormsAuthenticationModule, which first checks to see if forms authentication is being used and, if so, whether the user is authenticated or not. If not, the user is automatically redirected to the specified logon page.

Recall that with IIS, an incoming request is eventually directed to an ISAPI extension, whose job it is to return the data for the particular request. For example, when a request for a classic ASP Web page arrives, IIS hands off the request to the asp.dll ISAPI extension, whose task it is to return the HTML markup for the requested ASP page. The ASP.NET engine utilizes a similar approach. After initializing the HTTP modules, the ASP.NET engine's next task is to determine what HTTP handler should process the request.

All requests that pass through the ASP.NET engine eventually arrive at an HTTP handler or an HTTP handler factory (an HTTP handler factory simply returns an instance of an HTTP handler that is then used to process the request). The final HTTP handler renders the requested resource, returning the response. This response is sent back to IIS, which then returns it to the user that made the request.

ASP.NET includes a number of built-in HTTP handlers. The PageHandlerFactory, for example, is used to render ASP.NET Web pages. The WebServiceHandlerFactory is used to render the response SOAP envelopes for ASP.NET Web services. The TraceHandler renders the HTML markup for requests to trace.axd.

Figure 2 illustrates how a request for an ASP.NET resource is handled. First, IIS receives the request and dispatches it to aspnet_isapi.dll. Next, the ASP.NET engine initializes the configured HTTP modules. Finally, the proper HTTP handler is invoked and the requested resource is rendered, returning the generated markup back to IIS and back to the requesting client.

Figure 2. Request processing by IIS and ASP.NET

Creating and Registering Custom HTTP Modules and HTTP Handlers

Creating custom HTTP modules and HTTP handlers are relatively simple tasks, which involve created a managed class that implements the correct interface. HTTP modules must implement the System.Web.IHttpModule interface, while HTTP handlers and HTTP handler factories must implement the System.Web.IHttpHandler interface and System.Web.IHttpHandlerFactory interface, respectively. The specifics of creating HTTP handlers and HTTP modules is beyond the scope of this article. For a good background, read Mansoor Ahmed Siddiqui's article, HTTP Handlers and HTTP Modules in ASP.NET.

Once a custom HTTP module or HTTP handler has been created, it must be registered with the Web application. Registering HTTP modules and HTTP handlers for an entire Web server requires only a simple addition to the machine.config file; registering an HTTP module or HTTP handler for a specific Web application involves adding a few lines of XML to the application's Web.config file.

Specifically, to add an HTTP module to a Web application, add the following lines in the Web.config's configuration/system.web section:

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

The type value provides the assembly and class name of the HTTP module, whereas the name value provides a friendly name by which the HTTP module can be referred to in the Global.asax file.

HTTP handlers and HTTP handler factories are configured by the <httpHandlers> tag in the Web.config's configuration/system.web section, like so:

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

Recall that for each incoming request, the ASP.NET engine determines what HTTP handler should be used to render the request. This decision is made based on the incoming requests verb and path. The verb specifies what type of HTTP request was made—GET or POST—whereas the path specifies the location and filename of the file requested. So, if we wanted to have an HTTP handler handle all requests—either GET or POST—for files with the .scott extension, we'd add the following to the Web.config file:

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

where type was the type of our HTTP handler.

Note   When registering HTTP handlers, it is important to ensure that the extensions used by the HTTP handler are mapped in IIS to the ASP.NET engine. That is, in our .scott example, if the .scott extension is not mapped in IIS to the aspnet_isapi.dll ISAPI extension, a request for the file foo.scott will result in IIS attempting to return the contents of the file foo.scott. In order for the HTTP handler to process this request, the .scott extension must be mapped to the ASP.NET engine. The ASP.NET engine, then, will route the request correctly to the appropriate HTTP handler.

For more information on registering HTTP modules and HTTP handlers, be sure to consult the <httpModules> element documentation along with the <httpHandlers> element documentation.

Implementing URL Rewriting

URL rewriting can be implemented either with ISAPI filters at the IIS Web server level, or with either HTTP modules or HTTP handlers at the ASP.NET level. This article focuses on implementing URL rewriting with ASP.NET, so we won't be delving into the specifics of implementing URL rewriting with ISAPI filters. There are, however, numerous third-party ISAPI filters available for URL rewriting, such as:

Implementing URL rewriting at the ASP.NET level is possible through the System.Web.HttpContext class's RewritePath() method. The HttpContext class contains HTTP-specific information about a specific HTTP request. With each request received by the ASP.NET engine, an HttpContext instance is created for that request. This class has properties like: Request and Response, which provide access to the incoming request and outgoing response; Application and Session, which provide access to application and session variables; User, which provides information about the authenticated user; and other related properties.

With the Microsoft® .NET Framework Version 1.0, the RewritePath() method accepts a single string, the new path to use. Internally, the HttpContext class's RewritePath(string) method updates the Request object's Path and QueryString properties. In addition to RewritePath(string), the .NET Framework Version 1.1 includes another form of the RewritePath() method, one that accepts three string input parameters. This alternate overloaded form not only sets the Request object's Path and QueryString properties, but also sets internal member variables that are used to compute the Request object's values for its PhysicalPath, PathInfo, and FilePath properties.

To implement URL rewriting in ASP.NET, then, we need to create an HTTP module or HTTP handler that:

  1. Checks the requested path to determine if the URL needs to be rewritten.
  2. Rewrites the path, if needed, by calling the RewritePath() method.

For example, imagine that our website had information each employee, accessible through /info/employee.aspx?empID=employeeID. To make the URLs more "hackable," we might decide to have employee pages accessible by: /people/EmployeeName.aspx. Here is a case where we'd want to use URL rewriting. That is, when the page /people/ScottMitchell.aspx was requested, we'd want to rewrite the URL so that the page /info/employee.aspx?empID=1001 was used instead.

URL Rewriting with HTTP Modules

When performing URL rewriting at the ASP.NET level you can use either an HTTP module or an HTTP handler to perform the rewriting. When using an HTTP module, you must decide at what point during the request's lifecycle to check to see if the URL needs to be rewritten. At first glance, this may seem to be an arbitrary choice, but the decision can impact your application in both significant and subtle ways. The choice of where to perform the rewrite matters because the built-in ASP.NET HTTP modules use the Request object's properties to perform their duties. (Recall that rewriting the path alters the Request object's property values.) These germane built-in HTTP modules and the events they tie into are listed below:

HTTP Module Event Description
FormsAuthenticationModule AuthenticateRequest Determines if the user is authenticated using forms authentication. If not, the user is automatically redirected to the specified logon page.
FileAuthorizationMoudle AuthorizeRequest When using Windows authentication, this HTTP module checks to ensure that the Microsoft® Windows® account has adequate rights for the resource requested.
UrlAuthorizationModule AuthorizeRequest Checks to make sure the requestor can access the specified URL. URL authorization is specified through the <authorization> and <location> elements in the Web.config file.

Recall that the BeginRequest event fires before AuthenticateRequest, which fires before AuthorizeRequest.

One safe place that URL rewriting can be performed is in the BeginRequest event. That means that if the URL needs to be rewritten, it will have done so by the time any of the built-in HTTP modules run. The downside to this approach arises when using forms authentication. If you've used forms authentication before, you know that when the user visits a restricted resource, they are automatically redirected to a specified login page. After successfully logging in, the user is sent back to the page they attempted to access in the first place.

If URL rewriting is performed in the BeginRequest or AuthenticateRequest events, the login page will, when submitted, redirect the user to the rewritten page. That is, imagine that a user types into their browser window, /people/ScottMitchell.aspx, which is rewritten to /info/employee.aspx?empID=1001. If the Web application is configured to use forms authentication, when the user first visits /people/ScottMitchell.aspx, first the URL will be rewritten to /info/employee.aspx?empID=1001; next, the FormsAuthenticationModule will run, redirecting the user to the login page, if needed. The URL the user will be sent to upon successfully logging in, however, will be /info/employee.aspx?empID=1001, since that was the URL of the request when the FormsAuthenticationModule ran.

Similarly, when performing rewriting in the BeginRequest or AuthenticateRequest events, the UrlAuthorizationModule sees the rewritten URL. That means that if you use <location> elements in your Web.config file to specify authorization for specific URLs, you will have to refer to the rewritten URL.

To fix these subtleties, you might decide to perform the URL rewriting in the AuthorizeRequest event. While this approach fixes the URL authorization and forms authentication anomalies, it introduces a new wrinkle: file authorization no longer works. When using Windows authentication, the FileAuthorizationModule checks to make sure that the authenticated user has the appropriate access rights to access the specific ASP.NET page.

Imagine if a set of users does not have Windows-level file access to C:\Inetput\wwwroot\info\employee.aspx; if such users attempt to visit /info/employee.aspx?empID=1001, then they will get an authorization error. However, if we move the URL rewriting to the AuthenticateRequest event, when the FileAuthorizationModule checks the security settings, it still thinks the file being requested is /people/ScottMitchell.aspx, since the URL has yet to be rewritten. Therefore, the file authorization check will pass, allowing this user to view the content of the rewritten URL, /info/employee.aspx?empID=1001.

So, when should URL rewriting be performed in an HTTP module? It depends on what type of authentication you're employing. If you're not using any authentication, then it doesn't matter if URL rewriting happens in BeginRequest, AuthenticateRequest, or AuthorizeRequest. If you are using forms authentication and are not using Windows authentication, place the URL rewriting in the AuthorizeRequest event handler. Finally, if you are using Windows authentication, schedule the URL rewriting during the BeginRequest or AuthenticateRequest events.

URL Rewriting in HTTP Handlers

URL rewriting can also be performed by an HTTP handler or HTTP handler factory. Recall that an HTTP handler is a class responsible for generating the content for a specific type of request; an HTTP handler factory is a class responsible for returning an instance of an HTTP handler that can generate the content for a specific type of request.

In this article we'll look at creating a URL rewriting HTTP handler factory for ASP.NET Web pages. HTTP handler factories must implement the IHttpHandlerFactory interface, which includes a GetHandler() method. After initializing the appropriate HTTP modules, the ASP.NET engine determines what HTTP handler or HTTP handler factory to invoke for the given request. If an HTTP handler factory is to be invoked, the ASP.NET engine calls that HTTP handler factory's GetHandler() method passing in the HttpContext for the Web request, along with some other information. The HTTP handler factory, then, must return an object that implements IHttpHandler that can handle the request.

To perform URL rewriting through an HTTP handler, we can create an HTTP handler factory whose GetHandler() method checks the requested path to determine if it needs to be rewritten. If it does, it can call the passed-in HttpContext object's RewritePath() method, as discussed earlier. Finally, the HTTP handler factory can return the HTTP handler returned by the System.Web.UI.PageParser class's GetCompiledPageInstance() method. (This is the same technique by which the built-in ASP.NET Web page HTTP handler factory, PageHandlerFactory, works.)

Since all of the HTTP modules will have been initialized prior to the custom HTTP handler factory being instantiated, using an HTTP handler factory presents the same challenges when placing the URL rewriting in the latter stages of the events—namely, file authorization will not work. So, if you rely on Windows authentication and file authorization, you will want to use the HTTP module approach for URL rewriting.

Over the next section we'll look at building a reusable URL rewriting engine. Following our examination of the URL rewriting engine—which is available in this article's code download—we'll spend the remaining two sections examining real-world uses of URL rewriting. First we'll look at how to use the URL rewriting engine and look at a simple URL rewriting example. Following that, we'll utilize the power of the rewriting engine's regular expression capabilities to provide truly "hackable" URLs.

Building a URL Rewriting Engine

To help illustrate how to implement URL rewriting in an ASP.NET Web application, I created a URL rewriting engine. This rewriting engine provides the following functionality:

  • The ASP.NET page developer utilizing the URL rewriting engine can specify the rewriting rules in the Web.config file.
  • The rewriting rules can use regular expressions to allow for powerful rewriting rules.
  • URL rewriting can be easily configured to use an HTTP module or an HTTP handler.

In this article we will examine URL rewriting with just the HTTP module. To see how HTTP handlers can be used to perform URL rewriting, consult the code available for download with this article.

Specifying Configuration Information for the URL Rewriting Engine

Let's examine the structure of the rewrite rules in the Web.config file. First, you'll need to indicate in the Web.config file if you want perform URL rewriting with the HTTP module or the HTTP handler. In the download, the Web.config file contains two entries that have been commented out:

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

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

Comment out the <httpModules> entry to use the HTTP module for rewriting; comment out the <httpHandlers> entry instead to use the HTTP handler for rewriting.

In addition to specifying whether the HTTP module or HTTP handler is used for rewriting, the Web.config file contains the rewriting rules. A rewriting rule is composed of two strings: the pattern to look for in the requested URL, and the string to replace the pattern with, if found. This information is expressed in the Web.config file using the following syntax:

<RewriterConfig>
   <Rules>
   <RewriterRule>
      <LookFor>pattern to look for</LookFor>
      <SendTo>string to replace pattern with</SendTo>
   </RewriterRule>
   <RewriterRule>
      <LookFor>pattern to look for</LookFor>
      <SendTo>string to replace pattern with</SendTo>
   </RewriterRule>
   ...
   </Rules>
</RewriterConfig>

Each rewrite rule is expressed by a <RewriterRule> element. The pattern to search for is specified by the <LookFor> element, while the string to replace the found pattern with is entered in the <SentTo> element. These rewrite rules are evaluated from top to bottom. If a match is found, the URL is rewritten and the search through the rewriting rules terminates.

When specifying patterns in the <LookFor> element, realize that regular expressions are used to perform the matching and string replacement. (In a bit we'll look at a real-world example that illustrates how to search for a pattern using regular expressions.) Since the pattern is a regular expression, be sure to escape any characters that are reserved characters in regular expressions. (Some of the regular expression reserved characters include: ., ?, ^, $, and others. These can be escaped by being preceded with a backslash, like \. to match a literal period.)

URL Rewriting with an HTTP Module

Creating an HTTP module is as simple as creating a class that implements the IHttpModule interface. The IHttpModule interface defines two methods:

  • Init(HttpApplication). This method fires when the HTTP module is initialized. In this method you'll wire up event handlers to the appropriate HttpApplication events.
  • Dispose(). This method is invoked when the request has completed and been sent back to IIS. Any final cleanup should be performed here.

To facilitate creating an HTTP module for URL rewriting, I started by creating an abstract base class, BaseModuleRewriter. This class implements IHttpModule. In the Init() event, it wires up the HttpApplication's AuthorizeRequest event to the BaseModuleRewriter_AuthorizeRequest method. The BaseModuleRewriter_AuthorizeRequest method calls the class's Rewrite() method passing in the requested Path along with the HttpApplication object that was passed into the Init() method. The Rewrite() method is abstract, meaning that in the BaseModuleRewriter class, the Rewrite() method has no method body; rather, the class being derived from BaseModuleRewriter must override this method and provide a method body.

With this base class in place, all we have to do now is to create a class derived from BaseModuleRewriter that overrides Rewrite() and performs the URL rewriting logic there. The code for BaseModuleRewriter is shown below.

public abstract class BaseModuleRewriter : IHttpModule
{
   public virtual void Init(HttpApplication app)
   {
      // WARNING!  This does not work with Windows authentication!
      // If you are using Windows authentication, 
      // change to 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);
}

Notice that the BaseModuleRewriter class performs URL rewriting in the AuthorizeRequest event. Recall that if you use Windows authentication with file authorization, you will need to change this so that URL rewriting is performed in either the BeginRequest or AuthenticateRequest events.

The ModuleRewriter class extends the BaseModuleRewriter class and is responsible for performing the actual URL rewriting. ModuleRewriter contains a single overridden method—Rewrite()—which is shown below:

protected override void Rewrite(string requestedPath, 
   System.Web.HttpApplication app)
{
   // get the configuration rules
   RewriterRuleCollection rules = 
     RewriterConfiguration.GetConfig().Rules;

   // iterate through each rule...
   for(int i = 0; i < rules.Count; i++)
   {
      // get the pattern to look for, and 
      // Resolve the Url (convert ~ into the appropriate directory)
      string lookFor = "^" + 
        RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, 
        rules[i].LookFor) + "$";

      // Create a regex (note that IgnoreCase is set...)
      Regex re = new Regex(lookFor, RegexOptions.IgnoreCase);

      // See if a match is found
      if (re.IsMatch(requestedPath))
      {
         // match found - do any replacement needed
         string sendToUrl = 
RewriterUtils.ResolveUrl(app.Context.Request.ApplicationPath, 
            re.Replace(requestedPath, rules[i].SendTo));

         // Rewrite the URL
         RewriterUtils.RewriteUrl(app.Context, sendToUrl);
         break;      // exit the for loop
      }
   }
}

The Rewrite() method starts with getting the set of rewriting rules from the Web.config file. It then iterates through the rewrite rules one at a time, and for each rule, it grabs its LookFor property and uses a regular expression to determine if a match is found in the requested URL.

If a match is found, a regular expression replace is performed on the requested path with the value of the SendTo property. This replaced URL is then passed into the RewriterUtils.RewriteUrl() method. RewriterUtils is a helper class that provides a couple of static methods used by both the URL rewriting HTTP module and HTTP handler. The RewriterUrl() method simply calls the HttpContext object's RewriteUrl() method.

Note   You may have noticed that when performing the regular expression match and replacement, a call to RewriterUtils.ResolveUrl() is made. This helper method simply replaces any instances of ~ in the string with the value of the application's path.

The entire code for the URL rewriting engine is available for download with this article. We've examined the most germane pieces, but there are other components as well, such as classes for deserializing the XML-formatted rewriting rules in the Web.config file into an object, as well as the HTTP handler factory for URL rewriting. The remaining three sections of this article examine real-world uses of URL rewriting.

Performing Simple URL Rewriting with the URL Rewriting Engine

To demonstrate the URL rewriting engine in action, let's build an ASP.NET Web application that utilizes simple URL rewriting. Imagine that we work for a company that sells assorted products online. These products are broken down into the following categories:

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

Assume we already have created an ASP.NET Web page called ListProductsByCategory.aspx that accepts a Category ID value in the querystring and displays all of the products belonging to that category. So, users who wanted to view our Beverages for sale would visit ListProductsByCategory.aspx?CategoryID=1, while users who wanted to view our Dairy Products would visit ListProductsByCategory.aspx?CategoryID=4. Also assume we have a page called ListCategories.aspx, which lists the categories of products for sale.

Clearly this is a case for URL rewriting, as the URLs a user is presented with do not carry any significance for the user, nor do they provide any "hackability." Rather, let's employ URL rewriting so that when a user visits /Products/Beverages.aspx, their URL will be rewritten to ListProductsByCategory.aspx?CategoryID=1. We can accomplish this with the following URL rewriting rule in the Web.config file:

<RewriterConfig>
   <Rules>
      <!-- Rules for Product Lister -->
      <RewriterRule>
         <LookFor>~/Products/Beverages\.aspx</LookFor>
         <SendTo>~/ListProductsByCategory.aspx?CategoryID=1</SendTo>
      </RewriterRule>
      <RewriterRule>
   </Rules>
</RewriterConfig>

As you can see, this rule searches to see if the path requested by the user was /Products/Beverages.aspx. If it was, it rewrites the URL as /ListProductsByCategory.aspx?CategoryID=1.

Note   Notice that the <LookFor> element escapes the period in Beverages.aspx. This is because the <LookFor> value is used in a regular expression pattern, and period is a special character in regular expressions meaning "match any character," meaning a URL of /Products/BeveragesQaspx, for example, would match. By escaping the period (using \.) we are indicating that we want to match a literal period, and not any old character.

With this rule in place, when a user visits /Products/Beverages.aspx, they will be shown the beverages for sale. Figure 3 shows a screenshot of a browser visiting /Products/Beverages.aspx. Notice that in the browser's Address bar the URL reads /Products/Beverages.aspx, but the user is actually seeing the contents of ListProductsByCategory.aspx?CategoryID=1. (In fact, there doesn't even exist a /Products/Beverages.aspx file on the Web server at all!)

Figure 3. Requesting category after rewriting URL

Similar to /Products/Beverages.aspx, we'd next add rewriting rules for the other product categories. This simply involves adding additional <RewriterRule> elements within the <Rules> element in the Web.config file. Consult the Web.config file in the download for the complete set of rewriting rules for the demo.

To make the URL more "hackable," it would be nice if a user could simply hack off the Beverages.aspx from /Products/Beverages.aspx and be shown a listing of the product categories. At first glance, this may appear a trivial task—just add a rewriting rule that maps /Products/ to /ListCategories.aspx. However, there is a fine subtlety—you must first create a /Products/ directory and add an empty Default.aspx file in the /Products/ directory.

To understand why these extra steps need to be performed, recall that the URL rewriting engine is at the ASP.NET level. That is, if the ASP.NET engine is never given the opportunity to process the request, there's no way the URL rewriting engine can inspect the incoming URL. Furthermore, remember that IIS hands off incoming requests to the ASP.NET engine only if the requested file has an appropriate extension. So if a user visits /Products/, IIS doesn't see any file extension, so it checks the directory to see if there exists a file with one of the default filenames. (Default.aspx, Default.htm, Default.asp, and so on. These default filenames are defined in the Documents tab of the Web Server Properties dialog box in the IIS Administration dialog box.) Of course, if the /Products/ directory doesn't exist, IIS will return an HTTP 404 error.

So, we need to create the /Products/ directory. Additionally, we need to create a single file in this directory, Default.aspx. This way, when a user visits /Products/, IIS will inspect the directory, see that there exists a file named Default.aspx, and then hand off processing to the ASP.NET engine. Our URL rewriter, then, will get a crack at rewriting the URL.

After creating the directory and Default.aspx file, go ahead and add the following rewriting rule to the <Rules> element:

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

With this rule in place, when a user visits /Products/ or /Products/Default.aspx, they will see the listing of product categories, shown in Figure 4.

Figure 4. Adding "hackability" to the URL

Handling Postbacks

If the URLs you are rewriting contain a server-side Web Form and perform postbacks, when the form posts back, the underlying URL will be used. That is, if our user enters into their browser, /Products/Beverages.aspx, they will still see in their browser's Address bar, /Products/Beverages.aspx, but they will be shown the content for ListProductsByCategory.aspx?CategoryID=1. If ListProductsByCategory.aspx performs a postback, the user will be posted back to ListProductsByCategory.aspx?CategoryID=1, not /Products/Beverages.aspx. This won't break anything, but it can be disconcerting from the user's perspective to see the URL change suddenly upon clicking a button.

The reason this behavior happens is because when the Web Form is rendered, it explicitly sets its action attribute to the value of the file path in the Request object. Of course, by the time the Web Form is rendered, the URL has been rewritten from /Products/Beverages.aspx to ListProductsByCategory.aspx?CategoryID=1, meaning the Request object is reporting that the user is visiting ListProductsByCategory.aspx?CategoryID=1. This problem can be fixed by having the server-side form simply not render an action attribute. (Browsers, by default, will postback if the form doesn't contain an action attribute.)

Unfortunately, the Web Form does not allow you to explicitly specify an action attribute, nor does it allow you to set some property to disable the rendering of the action attribute. Rather, we'll have to extend the System.Web.HtmlControls.HtmlForm class ourselves, overriding the RenderAttribute() method and explicitly indicating that it not render the action attribute.

Thanks to the power of inheritance, we can gain all of the functionality of the HtmlForm class and only have to add a scant few lines of code to achieve the desired behavior. The complete code for the custom class is shown below:

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);
     }
  }
}

The code for the overridden RenderAttributes() method simply contains the exact code from the HtmlForm class's RenderAttributes() method, but without setting the action attribute. (I used Lutz Roeder's Reflector to view the source code of the HtmlForm class.)

Once you have created this class and compiled it, to use it in an ASP.NET Web application, start by adding it to the Web application's References folder. Then, to use it in place of the HtmlForm class, simply add the following to the top of your ASP.NET Web page:

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

Then, where you have <form runat="server">, replace that with:

<skm:Form id="Form1" method="post" runat="server">

and replace the closing </form> tag with:

</skm:Form>

You can see this custom Web Form class in action in ListProductsByCategory.aspx, which is included in this article's download. Also included in the download is a Visual Studio .NET project for the action-less Web Form.

Note   If the URL you are rewriting to does not perform a postback, there's no need to use this custom Web Form class.

Creating Truly "Hackable" URLs

The simple URL rewriting demonstrated in the previous section showed how easily the URL rewriting engine can be configured with new rewriting rules. The true power of the rewriting rules, though, shines when using regular expressions, as we'll see in this section.

Blogs are becoming more and more popular these days, and it seems everyone has their own blog. If you are not familiar with blogs, they are often-updated personal pages that typically serve as an online journal. Most bloggers simply write about their day-to-day happenings, others focus on blogging about a specific theme, such as movie reviews, a sports team, or a computer technology.

Depending on the author, blogs are updated anywhere from several times a day to once every week or two. Typically the blog homepage shows the most recent 10 entries, but virtually all blogging software provides an archive through which visitors can read older postings. Blogs are a great application for "hackable" URLs. Imagine while searching through the archives of a blog you found yourself at the URL /2004/02/14.aspx. Would you be terribly surprised if you found yourself reading the posts made on February 14th, 2004? Furthermore, you might want to view all posts for February 2004, in which case you might try hacking the URL to /2004/02/. To view all 2004 posts, you might try visiting /2004/.

When maintaining a blog, it would be nice to provide this level of URL "hackability" to your visitors. While many blog engines provide this functionality, let's look at how it can be accomplished using URL rewriting.

First, we need a single ASP.NET Web page that will show blog entries by day, month, or year. Assume we have such a page, ShowBlogContent.aspx, that takes in querystring parameters year, month, and day. To view the posts for February 14th, 2004, we could visit ShowBlogContent.aspx?year=2004&month=2&day=14. To view all posts for February 2004, we'd visit ShowBlogContent.aspx?year=2004&month=2. Finally, to see all posts for the year 2004, we'd navigate to ShowBlogContent.aspx?year=2004. (The code for ShowBlogContent.aspx can be found in this article's download.)

So, if a user visits /2004/02/14.aspx, we need to rewrite the URL to ShowBlogContent.aspx?year=2004&month=2&day=14. All three cases—when the URL specifies a year, month, and day; when the URL specifies just the year and month; and when the URL specifies only the yea—can be handled with three rewrite rules:

<RewriterConfig>
   <Rules>
      <!-- Rules for Blog Content Displayer -->
      <RewriterRule>
         <LookFor>~/(\d{4})/(\d{2})/(\d{2})\.aspx</LookFor>
         <SendTo>~/ShowBlogContent.aspx?year=$1&amp;month=$2&amp;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>

These rewriting rules demonstrate the power of regular expressions. In the first rule, we look for a URL with the pattern (\d{4})/(\d{2})/(\d{2})\.aspx. In plain English, this matches a string that has four digits followed by a forward slash followed by two digits followed by a forward slash, followed by two digits followed by .aspx. The parenthesis around each digit grouping is vital—it allows us to refer to the matched characters inside those parentheses in the corresponding <SendTo> property. Specifically, we can refer back to the matched parenthetical groupings using $1, $2, and $3 for the first, second, and third parenthesis grouping, respectively.

Note   Since the Web.config file is XML-formatted, characters like &, <, and > in the text portion of an element must be escaped. In the first rule's <SendTo> element, & is escaped to &amp;. In the second rule's <SendTo>, an alternative technique is used—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

Note   The URL rewriting engine expects a regular expression pattern in the <LookFor> elements. If you are unfamiliar with regular expressions, consider reading an earlier article of mine, An Introduction to Regular Expressions. Also, a great place to get your hands on commonly used regular expressions, as well as a repository for sharing your own crafted regular expressions, is RegExLib.com.

Building the Requisite Directory Structure

When a request comes in for /2004/03/19.aspx, IIS notes the .aspx extension and routes the request to the ASP.NET engine. As the request moves through the ASP.NET engine's pipeline, the URL will get rewritten to ShowBlogContent.aspx?year=2004&month=03&day=19 and the visitor will see those blog entries for March 19, 2004. But what happens when the user navigates to /2004/03/? Unless there is a directory /2004/03/, IIS will return a 404 error. Furthermore, there needs to be a Default.aspx page in this directory so that the request is handed off to the ASP.NET engine.

So with this approach, you have to manually create a directory for each year in which there are blog entries, with a Default.aspx page in the directory. Additionally, in each year directory you need to manually create twelve more directories—01, 02, …, 12—each with a Default.aspx file. (Recall that we had to do the same thing—add a /Products/ directory with a Default.aspx file—in the previous demo so that visiting /Products/ correctly displayed ListCategories.aspx.)

Clearly, adding such a directory structure can be a pain. A workaround to this problem is to have all incoming IIS requests map to the ASP.NET engine. This way, even if when visiting the URL /2004/03/, IIS will faithfully hand off the request to the ASP.NET engine even if there does not exist a /2004/03/ directory. Using this approach, however, makes the ASP.NET engine responsible for handling all types of incoming requests to the Web server, including images, CSS files, external JavaScript files, Macromedia Flash files, and so on.

A thorough discussion of handling all file types is far beyond the scope of this article. For an example of an ASP.NET Web application that uses this technique, though, look into .Text, an open-source blog engine. .Text can be configured to have all requests mapped to the ASP.NET engine. It can handle serving all file types by using a custom HTTP handler that knows how to serve up typical static file types (images, CSS files, and so on).

Conclusion

In this article we examined how to perform URL rewriting at the ASP.NET-level through the HttpContext class's RewriteUrl() method. As we saw, RewriteUrl() updates the particular HttpContext's Request property, updating what file and path is being requested. The net effect is that, from the user's perspective, they are visiting a particular URL, but actually a different URL is being requested on the Web server side.

URLs can be rewritten either in an HTTP module or an HTTP handler. In this article we examined using an HTTP module to perform the rewriting, and looked at the consequences of performing the rewriting at different stages in the pipeline.

Of course, with ASP.NET-level rewriting, the URL rewriting can only happen if the request is successfully handed off from IIS to the ASP.NET engine. This naturally occurs when the user requests a page with a .aspx extension. However, if you want the person to be able to enter a URL that might not actually exist, but would rather rewrite to an existing ASP.NET page, you have to either create mock directories and Default.aspx pages, or configure IIS so that all incoming requests are blindly routed to the ASP.NET engine.

Related Books

ASP.NET: Tips, Tutorials, and Code

Microsoft ASP.NET Coding Strategies with the Microsoft ASP.NET Team

Essential ASP.NET with Examples in C#

Works consulted

URL rewriting is a topic that has received a lot of attention both for ASP.NET and competing server-side Web technologies. The Apache Web server, for instance, provides a module for URL rewriting called mod_rewrite. mod_rewrite is a robust rewriting engine, providing rewriting rules based on conditions such as HTTP headers and server variables, as well as rewriting rules that utilize regular expressions. For more information on mod_rewrite, check out A User's Guide to URL Rewriting with the Apache Web Server.

There are a number of articles on URL rewriting with ASP.NET. Rewrite.NET - A URL Rewriting Engine for .NET examines creating a URL rewriting engine that mimics mod_rewrite's regular expression rules. URL Rewriting With ASP.NET also gives a good overview of ASP.NET's URL rewriting capabilities. Ian Griffiths has a blog entry on some of the caveats associated with URL rewriting with ASP.NET, such as the postback issue discussed in this article. Both Fabrice Marguerie (read more) and Jason Salas (read more) have blog entires on using URL rewriting to boost search engine placement.


About the author

Scott Mitchell, author of five books and founder of 4GuysFromRolla.com, has been working with Microsoft Web technologies for the past five years. Scott works as an independent consultant, trainer, and writer. He can be reached at mitchell@4guysfromrolla.com or through his blog, which can be found at http://ScottOnWriting.NET.




이건 또 관련된 사이트구


http://duraboys.tistory.com/398


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