SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

HTTP404エラーに対応したASP.NETのリダイレクタ作成

サイト来訪者の不快さを軽減するリダイレクトの設定

ダウンロード RedirectorHandler.zip (3.2 KB)

web.configにジェネリックハンドラを結びつけよう

 次に「web.config」ファイルに設定を行います。どのWindowsのバージョンでも同一です。

web.configの設定例
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <system.web>
    <customErrors mode="On"
            defaultRedirect="/testredirect/RedirectorHandler.ashx">
      <error statusCode="404"
            redirect="/testredirect/RedirectorHandler.ashx" />
      <error statusCode="403"
            redirect="/testredirect/RedirectorHandler.ashx" />
    </customErrors>
  </system.web>
</configuration>

 IIS 6までの場合には、アプリケーションのプロパティで[ASP.NET]タブの[構成の編集]ボタンから、[カスタムエラー]の[ローカルのカスタムエラー]でも設定可能です。

 まず、IISマネージャでの設定は静的ファイルへの要求の場合に、ディレクトリ参照の要求で失敗した場合(403)や、ファイルの要求で失敗した場合(404)に処理されるURLを示しています。

 「Web.config」への設定は、拡張子が.aspxや.asmxなど、ASP.NET関連エラーの場合です。

 先ほど書いたような拡張子の要求の場合、IISがASP.NETに処理を渡してきます。ASP.NET側でどのような処理をするかはIIS側では感知しませんので、2か所に分けて記述するのです。

 同様にASPClassicの設定が行われている場合には、.aspの拡張子の場合にASPClassic側に処理が渡ってしまうので、今回のものではフォローできません。このあと説明する処理と同様の処理をASP側で記述する必要があります。

URLを抜き出すコードを実装する

 さて、いよいよ実装していきます。

 IIS側で404エラーが発生した場合と、ASP.NET側で404エラーが発生した場合の2パターンで設定をしました。この2パターンにより、ジェネリックハンドラに渡されてくる情報も微妙に変化します。

 今回の場合、リクエストされたURLでファイルが存在しない場合に、直近のディレクトリや直上のディレクトリへのリダイレクトを促す処理ですので、まずはリクエストされてきたURLを抜き出しましょう。

 IIS側エラーの場合には、context.Request.Url.Queryに"?403;http://localhost:80/testredirect/"という情報が入っています。これはHTTPエラーの種類と、元アクセスURLが混ざった状態で入っています。

 ASP.NET側エラーの場合にはcontext.Request.Url.Queryに"?aspxerrorpath=/testredirect/1.aspx"という情報が入っています。この場合は通常のリクエストと同じ記法で元URLが記述されていますので、context.Request["aspxerrorpath"]とするだけで、"/testredirect/1.aspx"が取得できることになります。

 これらを正規表現などで抜き出しましょう。

元リクエストURLを抜き出す部分(C#)
private  string もともと取得しようとしたパスを抽出(HttpContext context)
{
    //もともと取得しようとしたURLを調べます。
    if (context.Request["aspxerrorpath"] == null)
    {
        //IISからくる場合にはcontext.Request.Url.Queryに
        //元ページパスが格納されている
        string 対象URL = context.Request.Url.Query;
        Regex regex取得対象ページ抽出用
            = new Regex(@"^\?\d+;http://\S+:\d+/(?<TARGET>[\s\S]+)");
        MatchCollection 元取得ページ
            = regex取得対象ページ抽出用.Matches(対象URL);
        if (元取得ページ.Count > 0)
        {
            return 元取得ページ[0].Groups["TARGET"].Value;
        }
        else
        {
            return null;
        }
    }
    else
    {
        //ASPXからくる場合にはaspxerrorpathというパラメータでやってくる
        return context.Request["aspxerrorpath"];
    }
}
元リクエストURLを抜き出す部分(VB.NET)
Private Function もともと取得しようとしたパスを抽出( _
    ByVal context As HttpContext) As String
    'もともと取得しようとしたURLを調べます。
    If (context.Request("aspxerrorpath") Is Nothing) Then
        
        'IISからくる場合にはcontext.Request.Url.Queryに
        '元ページパスが格納されている
        Dim 対象URL As String = context.Request.Url.Query
        Dim regex取得対象ページ抽出用 _
            As New Regex("^\?\d+;http://\S+:\d+/(?<TARGET>[\s\S]+)")
        Dim 元取得ページ As MatchCollection _
            = regex取得対象ページ抽出用.Matches(対象URL)
        If (元取得ページ.Count > 0) Then
            Return 元取得ページ(0).Groups("TARGET").Value
        Else
            Return Nothing
        End If
    Else
        'ASPXからくる場合にはaspxerrorpathというパラメータでやってくる
        Return context.Request("aspxerrorpath")
    End If
End Function

元リクエストパスを分解し、リダイレクト先URIを組み立てる

 前述のURLを抜き出すと/testredirect/1.aspxのようなホスト名などがない完全なパス情報だけが残ります。この情報を/で分解し、一番後ろの情報だけ削除して次のリクエスト先を決定します。

パスを分割し元リクエストURLを抜き出す部分(C#)
//パスをリストに分解する
List<string> 分割後対象パス = this.パスをリストに分解(元ページURL);
//リダイレクト先URIを組みたてる
string リダイレクト先URI
    = this.リダイレクト先URLを組みたてる(分割後対象パス);

private  List<string> パスをリストに分解(string 元ページURL)
{
    //パスの分解を行う
    List<string> 分割後対象パス
        = new List<string>(元ページURL.Split('/'));
    //セキュリティのためと、IIS, ASPXの違いを吸収するためなどで
    // ".." と "" を排除する
    分割後対象パス = 分割後対象パス.FindAll(delegate(string value)
    {
        if (value == null || value == "" || value == "..")
        {
            return false;
        }
        else
        {
            return true;
        }
    });
    return 分割後対象パス;
}
private  string リダイレクト先URLを組みたてる(
    List<string> 分割後対象パス)
{
    //リダイレクト先URLを組みたてる
    StringBuilder sb = new StringBuilder();
    分割後対象パス.ForEach(delegate(string value)
    {
        sb.Append("/");
        sb.Append(value);
    });
    return sb.ToString();
}
パスを分割し元リクエストURLを抜き出す部分(VB.NET)
'パスをリストに分解する
Dim 分割後対象パス As List(Of String) _
    = Me.パスをリストに分解(元ページURL)
'リダイレクト先URIを組みたてる
Dim リダイレクト先URI As String _
    = Me.リダイレクト先URLを組みたてる(分割後対象パス)

Private Function リダイレクト先URLを組みたてる( _
    ByVal 分割後対象パス As List(Of String)) As String
    'リダイレクト先URLを組みたてる
    Dim sb As New StringBuilder()
    For Each value As String In 分割後対象パス
        sb.Append("/")
        sb.Append(value)
    Next
    Return sb.ToString()
End Function

Private Function パスをリストに分解(ByVal 元ページURL As String) _
    As List(Of String)
    'パスの分解を行う
    Dim 分割後対象パス As New List(Of String)(元ページURL.Split("/"c))
    'セキュリティのためと、IIS, ASPXの違いを吸収するためなどで
    ' ".." と "" を排除する
    分割後対象パス _
        = 分割後対象パス.FindAll(AddressOf Me.対象かどうかの判定)
    '要素がある場合には最後の要素を消してしまう
    If (分割後対象パス.Count > 0) Then
        分割後対象パス(分割後対象パス.Count - 1) = ""
    End If
    Return 分割後対象パス
End Function

Private Function 対象かどうかの判定(ByVal value As String) As Boolean
    If (value Is Nothing OrElse value = "" OrElse value = "..") Then
        Return False
    Else
        Return True
    End If
End Function

 最後にはリダイレクトさせれば完了です。

パスを分割し元リクエストURLを抜き出す部分(C#)
context.Response.Redirect(リダイレクト先URI);
パスを分割し元リクエストURLを抜き出す部分(VB.NET)
context.Response.Redirect(リダイレクト先URI)

最後に

 静的なWebサイトの場合、URLは分かりやすい分類によって作成されている場合が多いと思います。手でURLを直接入力してサイトにアクセスしてくるユーザーもいますし、HTMLの記述ミスで404エラーを出してしまう場合もあります。

 しかし、そのような場合においても、ユーザーによりよい情報を提供することで不快な思いをせずにすむならそれに越したことはありません。

 今回紹介したコードの完全版が、このページの上の方にリンクされています。

 是非あなたのサイトに組み込んで、ユーザビリティの向上の一助にしてください。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

中 博俊(ナカ ヒロトシ)

大阪でフリーのソフトウェアエンジニア兼テクニカルライターとして日々新技術で楽しんでいる。MSMVP for C# ブログ:http://blogs.wankuma.com/naka/

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1340 2007/05/30 08:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー