WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

二重スラッシュを含む URL 書換

by WebSurfer 23. January 2015 14:10

IIS の URL Rewrite モジュールを使用して aaaa/1/2/3 という URL を test.aspx?x=1&y=2&z=3 というように書き換えることを考えます。

URL の書き換え

この時、たとえば上の画像のように 2 つ目の値をスキップした aaaa/1//3 という URL を test.aspx?x=1&y=&z=3(y に値がないところに注目)に書き換えたい場合はどうすればいいでしょうか?

IIS7 の URL Rewrite モジュールを使って、普通に(というか、あまり深く考えずに)書換ルールを作ると以下のようにすると思います。

<rewrite>
  <rules>
    <rule name="DoubleSlash">
      <match url="aaaa/(.*)/(.*)/(.*)" />
      <action 
        type="Rewrite" 
        url="test.aspx?x={R:1}&y={R:2}&z={R:3}" />
    </rule>
  </rules>
</rewrite> 

IIS マネージャーの「テストパターン」ダイアログ上で、[テスト対象データの入力(D):]テキストボックスに aaaa/1/2/3 とか aaaa/1//3 などのテストパターンを入力してテストすると、 {R:1}, {R:2}, {R:3} には期待通りの結果が得られます。

なので、ブラウザから aaaa/1/2/3 とか aaaa/1//3 などで呼んでも同様に aaaa/(.*)/(.*)/(.*) にマッチして、test.aspx?x=1&y=2&z=3 とか test.aspx?x=1&y=&z=3 に書き換えられると期待すると思います。

しかし、ブラウザから aaaa/1//3 で呼ぶとダメです。ダメな理由は、IIS が aaaa/1//3 を aaaa/1/3 に書き換えてしまうからです。詳しく書くと以下の通りです。

  1. ブラウザからは aaaa/1//3 で要求が出る。
  2. それを受けた IIS は aaaa/1//3 を aaaa/1/3 に書き換えてしまう。
  3. aaaa/1/3 が URL Rewrite モジュールに渡される。
  4. aaaa/1/3 は aaaa/(.*)/(.*)/(.*) にはマッチしない。マッチしないので書き換えは行われない。
  5. 書き換えが行われないので、IIS は aaaa/1/3 で指定されるリソースを探す。
  6. そのようなリソースは存在しないので HTTP エラー 404.0 - Not Found となる。

ちなみに、aaaa/1/2/3 であれば期待通り test.aspx?x=1&y=2&z=3 に書き換えられ、test.aspx ページ内でクエリ文字列は正しく取得できます。

上記 2 がどうして分かったかと言うと、6 で表示されるエラー画面で「要求された URL http://aspnet4site:80/aaaa/1/3」となっていたからです。(aspnet4site は自分の開発マシンで使っているホスト名です)

というわけで、上記 2 を何とかしないと URL Rewrite モジュールでは対処できないということになります。

それを何とかする手段が IIS Server Variables の UNENCODED_URL を使用して IIS が処理する前の URL を取得して、それに書換ルールを適用することです。

詳しい手順は Microsoft IIS.net の記事 URL Rewrite Module Configuration Reference の中の「Using server variables in rewrite rules」および「Using back-references in rewrite rules」というセクションが参考になると思います。

具体的な書換ルールのコードは、今回のケースでは以下のようになります。

<rewrite>
  <rules>
    <rule name="DoubleSlash">
      <match url="^aaaa/" />
      <action 
        type="Rewrite"
        url="test.aspx?x={C:1}&y={C:2}&z={C:3}" />
      <conditions>
        <add 
          input="{UNENCODED_URL}" 
          pattern="^/aaaa/(.*)/(.*)/(.*)" />
      </conditions>
    </rule>
  </rules>
</rewrite>

上記のルールで、aaaa/1/2/3 でも、aaaa/1//3 でも、aaaa//2/3 でも、 aaaa/// でも、その他全てのパターンで期待通り書き換えられるのを確認できました。

Tags: ,

Windows Server

URL Rewrite 2.0 と日本語の問題

by WebSurfer 2. December 2013 17:31

Microsoft が提供している IIS 7.x 用の URL Rewrite 2.0 モジュール を使用しての日本語の書き換えがうまくいかないという話です。

なお、この問題は globalization 要素の requestEncoding 属性を Shift_JIS に設定した場合に限られます。デフォルトの UTF-8 の場合は問題ありません。

URL Rewrite 2.0 による書き換え

例えば、以下のように、日本語を使用した URL を書き換えるケースを考えます。

http://host/dir/あ ⇒ http://host/default.aspx?n=あ

その場合、URL Rewrite 2.0 では URL 書き換えルールは以下のようになります。

<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Japanese Query String Test">
          <match url="dir/(.+)$" />
          <action 
            type="Rewrite" 
            url="default.aspx?n={R:1}" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

上記で、{R:1} というのは正規表現の後方参照から取得される文字列で、パターン dir/(.+)$ ではカッコで囲った部分になります。例えば、"http://host/dir/xxx" という入力なら {R:1} に該当するのは "xxx" になります。(ちなみに、{R:0} は "dir/xxx" になります)

ブラウザが要求を出す際、先の記事 ブラウザによる URL のエンコーディング で書きましたように、URL を UTF-8 として URL エンコーディングします。

"あ" の UTF-8 でのバイト列は E3 81 82 ですので、"http://host/dir/あ" は "http://host/dir/%E3%81%82" にエンコーディングされてからサーバーに送信されます。

URL Rewrite モジュールでは、"http://host/dir/%E3%81%82" という入力から、正規表現パターン dir/(.+)$ の後方参照として "%E3%81%82" を取得し、それをデコーディングした文字列が {R:1} に渡される仕組みになっています。(ソースコードを見たわけではなく、検証結果での確認ですが)

問題はその際の Encoding の判定です。

requestEncoding 属性が UTF-8(デフォルト)であれば、使用されている Encoding は UTF-8 と判定され、書き換え後の URL は正しく "default.aspx?n=あ" となります。

しかし、requestEncoding 属性が Shift_JIS に設定されていると、"%E3%81%82" の Encoding は Shift_JIS と判定され、書き換え後の URL は "default.aspx.aspx?n=縺・" とクエリ文字列の部分が文字化けしてしまいます。("縺・" は Shift_JIS のバイト列で E3 81 81 45 になります)

requestEncoding 属性が Shift_JIS でこの問題を避ける方法はあるでしょうか?

試しに、"http://host/dir/あ" で "あ" の部分を Shift_JIS の URL エンコード、即ち "http://host/dir/%82%A0" としてブラウザのアドレスバーに入力してみました。

その結果が一番上の画像なのですが、結果は同じように文字化けしてしまい、やっぱりダメでした。

ブラウザからサーバーに送信される URL は "http://host/dir/%82%A0" となりますが(Fiddler2 で確認)、サーバーが受信して URL Rewrite モジュールに渡す時に "%82%A0" が "%E3%81%82" になってしまうようです。(上の画像の X-Original-URL 参照)

というわけで、Microsoft の IIS 7.x 用 URL Rewrite 2.0 モジュールを使う際は、requestEncoding 属性を UTF-8(デフォルト)に限定した方がよさそうです。

UTF-8 なら "http://host/dir/%82%A0" という Shift_JIS で URL エンコーディングされた URL も正しく "default.aspx?n=あ" に書き換えられます。

参考までに検証に使った aspx ページ(0024-QueryString.aspx)を以下に載せておきます。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
    
  protected void Page_Load(object sender, EventArgs e)
  {
    Encoding enc = Request.ContentEncoding;
        
    Label1.Text = "Request.ContentEncoding.EncodingName: " + 
        enc.EncodingName;
              
    Label2.Text = "X-Original-URL: ";
    string[] values = 
        Request.Headers.GetValues("X-Original-URL");
    if (values != null)
    {
      string url = values[0];
      Label2.Text = Label2.Text + url;
      Label2.Text = Label2.Text + " (Url-decoded: " +
            HttpUtility.UrlDecode(url, enc) + ")";
    }

    Label3.Text = "Request.RawUrl: " + Request.RawUrl;
    Label4.Text = "Request.Url.OriginalString: " + 
        Request.Url.OriginalString;

    string queryString = Request.QueryString["n"];
    Label5.Text = 
        "Request.QueryString[\"n\"]: " + queryString;
                
    if (queryString != null)
    {
      string s = " (byte array: ";
      byte[] b = enc.GetBytes(queryString);
      for (int i = 0; i < b.Length; i++)
      {
        s = s + String.Format("[{0:X2}]", b[i]);
      }
      Label5.Text = Label5.Text + s + ")";
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    <asp:Label ID="Label1" runat="server"></asp:Label>
    <br />
    <asp:Label ID="Label2" runat="server"></asp:Label>
    <br />
    <asp:Label ID="Label3" runat="server"></asp:Label>
    <br />
    <asp:Label ID="Label4" runat="server"></asp:Label>
    <br />
    <asp:Label ID="Label5" runat="server"></asp:Label>
  </div>
  </form>
</body>
</html>

Tags: ,

Windows Server

ASP.NET 開発サーバーと IIS

by WebSurfer 18. November 2011 22:58

ASP.NET ベースの Web アプリケーションを開発する際、Visual Studio に搭載されている開発サーバーを利用されている方は多いと思います。

ASP.NET 開発サーバーの起動

余談ですが、この開発サーバーは、もともと Cassini という名前のサンプル Web サーバープログラムだそうで、今でも Microsoft のサイト ASP.NET Cassini Sample Web Server からソースコードを入手できます。(2012/1/8 追記: Cassini のダウンロードページがリンク切れになっていました。それに代わるものというわけではないですが、CassiniDev - Cassini 3.5/4.0 Developers Edition という CodePlex のサイトで改良版が提供されています)

開発サーバーは手軽で便利ですが、開発マシンで IIS が使えるのであれば、開発サーバーを開発に利用するのは避けたほうがいいと思います。

理由は、開発サーバーでは問題なく動いていても、IIS を使った本番環境に移すと動かないということがあるからです。

自分が認識している問題としては、以下があります。

  1. アクセス権: 開発サーバーの場合は PC にログインしているユーザーアカウントが持つアクセス権を使います。Administrator 権限でログインしていれば、ほぼ無制限に PC 内のリソースにアクセスできます。一方、実環境で IIS 上で動かす場合、リソースへのアクセス権は IIS のワーカープロセスのアカウント(IIS7 の場合、デフォルトで NETWORK SERVICE)が持つものになります。そのアクセス権には制限がありますので、ファイルが開けないとか書き込めないという問題が出てきます。
  2. 統合パイプラインモード: 開発サーバーには IIS7 の統合パイプラインモードの機能がありません(クラッシックモード相当になります)。また、統合パイプラインモードとクラシックモードとでは web.config での HTTP ハンドラ、HTTP モジュールの定義方法が異なりますので、定義の仕方によっては、HTTP ハンドラ、HTTP モジュールが動かない場合があります。
  3. 要求のフィルタリング: IIS7 で導入された 要求のフィルタリング requestFiltering は開発サーバーにはありません。それゆえ、カスタムエラーの表示などエラーハンドリングが異なります。
  4. MIME の種類: ファイルを要求した際、そのファイルの拡張子が IIS に定義済みの MIME の種類ではないと "HTTP エラー 404.0 - Not Found" を返します。よくある例が .flv や .mp4 ファイルで、開発サーバーでは再生できるが、IIS では再生できないという結果になります。(2013/4/20 追記)
  5. SSL: 開発サーバーには SSL 通信の機能がありません。したがって、SSL 通信下における試験ができません。(2012/1/8 追記)
  6. Session 0 分離: Windows Vista, Windows Server 2008 から Session 0 分離というセキュリティ対策がとられています(詳しくは、Windows でのサービスとドライバーへの Session 0 分離の影響 というページからダウンロードできる文書を参照)。つまり、ワーカープロセスは Session 0 で動いていて、それから起動されるアプリも Session 0 で動く。Session 0 は noninteractive なので、モーダルダイアログやフォームを表示することはできません(InvalidOperationException 例外がスローされます)。一方、ASP.NET 開発サーバーを使っている場合など、ワーカープロセスがログインしたユーザーのアカウントで動いている時は話が違ってきます。(2012/2/1 追記)
  7. 32-bit/64-bit の問題: これは逆に ASP,NET 開発サーバーで動かないケースで、利用しているライブラリが 64-bit 用の場合が問題になります。Visual Studio は 32-bit 版しか提供されていませんので、64-bit OS 上では WOW 上で 32-bit 動作します。その状態で、Visual Studio で Web アプリケーションを開発して ASP.NET 開発サーバで動作させた場合、Web アプリケーションは 32-bit で動作します。従って、x64 版の dll は動きません。例えば、Sharepoint の dll など x64 版 しかないものがあるそうですが、そのような場合は ASP.NET 開発サーバーでは動きません。(2013/9/5 追記)
  8. その他: IIS 用に用意された 拡張モジュール が利用できない。特に使えないと困るのが、SEO 対策に有効な URL Rewrite Module でしょうか。(2012/1/8 追記)

IIS 上で開発すれば、上記のようなトラブルは未然に防ぐことができます。(Visual Studio 2010 SP1 でリリースされた IIS Express を使うという選択肢があるようですが、自分は使ったことがないので、上記の問題の有無はわかりません。)(2012/10/27 追記:今さらながらですが IIS Express を使ってみました。1 項の「アクセス権」の問題は IIS Express では解決できません。その他にも問題があります。詳しくは IIS Express をインストールしました を見てください。)

XP, Vista, 7 など、サーバー OS でなくても、あるグレード以上なら IIS が標準で実装されています。ちょっと設定に時間はかかりますが、IIS をインストールして IIS 上で開発できます。

ただし、OS のグレードによっては IIS がインストールされていても機能に制限があり、デバッグができないものがあるので注意してください。

------ 2013/9/6 追記 ------

ASP.NET 開発サーバー、IIS Express、IIS の比較を書いた Microsoft の公式文書(MSDN ライブラリ)を紹介しておきます(URL 下記)。Visual Studio で IIS を使用して開発したい方は、そのページの下の方の「IIS をインストールして Visual Studio と連動するように構成」というセクションに設定方法のページへのリンクがありますので参考にしてください。

ASP.NET Web プロジェクト用の Visual Studio の Web サーバー

ASP.NET 開発サーバーの実態は何かは、以下のページが参考になると思います。

VS 2005やIISを使用せずにWebアプリケーションを実行するには?[VS 2005のみ]

WebDev.WebServer.EXE がある場所は Visual Studio のバージョンによって異なるので注意してください。上のページに書いてある場所は Visual Stidio 2005 の場合です。

Visual Studio 2008, 2010 の場合は以下のようになります。64-bit OS の場合は Program Files ⇒ Program Files (x86) になるはずです。

VS2008:
C:\Program Files\Common Files\microsoft shared\DevServer\9.0

VS2010:
C:\Program Files\Common Files\microsoft shared\DevServer\10.0

Tags: ,

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。その後 ブログ2 を追加し、ここは ASP.NET 関係のトピックス、ブログ2はそれ以外のトピックスに分けました。

Calendar

<<  September 2021  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar