WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

Server.Transfer と fragment 識別子

by WebSurfer 2014年8月3日 13:42

Server.Transfer メソッド を使って別のページに遷移する場合、fragment 識別子を使用して指定する場所にジャンプさせることができるでしょうか?

結論から言うとダメです。でも、それでは記事にならないので、何故ダメなのかの理由と、ジャンプさせるための別の方法を書きます。

(1) Server.Transfer で fragment 識別子を使用できない理由

html の a 要素や、ASP.NET でよく使われる Response.Redirect メソッド を使用する場合は問題ありません。

例えば、Response.Redirect メソッドを使う場合、以下のように fragment 識別子(#positionToMove の部分)を設定すれば、リダイレクト先の default2.aspx ページで html 要素の id または name 属性に positionToMove が指定されていれば(例: id="positionToMove")、その場所にジャンプします。

protected void Button1_Click(object sender, EventArgs e)
{
  Response.Redirect("~/default2.aspx#positionToMove", false);
}

これを Server.Transfer("~/default2.aspx#positionToMove") のようにすると HttpException がスローされ、"要求の種類 'POST' の http ハンドラーが見つかりませんでした。" というエラーメッセージが出ます。これは、.aspx#positionToMove という拡張子のファイルを処理して応答を返す HTTP ハンドラが見つからないということを言ってます。

何故このような違いが出るかは、Response.Redirect と Server.Transfer のサーバー側での処理の違いと、ブラウザでの fragment 識別子の扱いが分かると理解できると思います。

まず、Response.Redirect と Server.Transfer の違いですが、これは @IT の記事 第16回 ASP.NETにおけるページの遷移 (1/4) が参考になると思いますので見てください。

具体的には、上に書いたコードの例で言うと、(a) Response.Redirect はサーバーからブラウザがリダイレクト指示(HTTP 302 応答)を受け、ブラウザがサーバーに default2.aspx を GET 要求に行く、(b) Server.Transfer はサーバー側だけで処理が行われ default2.aspx#positionToMove を返そうとする・・・ところが大きな違いです。

上記 (a) で、HTTP 302 応答のヘッダには Location: /default2.aspx#positionToMove が含まれますが、それを受けたブラウザは #positionToMove を取り除いて GET /default2.aspx という要求をサーバーに出します。サーバーには .aspx ファイルを扱うハンドラはもちろん定義されていますので、HTTP ハンドラが見つからないなどという問題はなく、default2.aspx が処理されて応答が返ってきます。

ブラウザはジャンプ先の情報 #positionToMove を保持していますので、default2.aspx の応答が返ってきた後でその位置にジャンプすると言う仕組みになってます。

というわけで、そもそも Server.Transfer("~/default2.aspx#positionToMove") などどするのは間違いのようです。

と言うよりは、その前に、本当に Server.Transfer を使う必要があるのかということをよく考えるべきと思います。

普通は Server.Transfer を使う必要はないはずです。個人的には使うべきではないと思っています。使うケースがあるとすると、自分が知る限りですが、エラーページに Transfer して、そこで遷移元で起こった例外の情報を取得する場合ぐらいでしょうか。

(2) Server.Transfer で遷移して指定の場所にジャンプする方法

MSDN ライブラリの Transfer メソッドの説明によると "QueryString コレクションおよび Form コレクションを保持します" ということです。

なので、クエリ文字列でジャンプ先のアンカー名を渡し、遷移先のページでアンカー名を取得し、JavaScript を使ってアンカー名に指定された位置にジャンプさせることが可能です。

遷移元のページでは以下のよう Server.Transfer メソッドを記述します。

protected void Button2_Click(object sender, EventArgs e)
{
  // クエリ文字列でジャンプ先のアンカー名を渡す。
  Server.Transfer("~/default2.aspx?anchorName=positionToMove");
}

遷移先のページに移動する位置を示すアンカー(フラグメント)を設定します。例えば、アンカー名が上記のコードのように positionToMove とすると以下のような感じです。html 要素は p 要素でなくても、div でも span でも可です。古いブラウザでは name 属性に指定しないと認識しないものもあるようですので、id と name 属性の両方にアンカー名を指定しておいた方がいいと思います。

<p id="positionToMove" name="positionToMove">
    ジャンプする場所
</p>

さらに、遷移先のページで、クエリ文字列の有無を調べて、アンカー名の指定があればその位置にジャンプさせる JavaScript のコードを登録します。以下のような感じです。

protected void Page_Load(object sender, EventArgs e)
{
  string anchorName = Request.QueryString["anchorName"];

  // null チェックおよび適切性の検証。
  if (anchorName != null && IsAnchorNameValid(anchorName))
  {
    String csname = "FragmentScript";
    Type cstype = this.GetType();

    ClientScriptManager cs = Page.ClientScript;

    if (!cs.IsStartupScriptRegistered(cstype, csname))
    {
      String cstext = 
          "window.location.hash = '" + anchorName + "';";
      cs.RegisterStartupScript(cstype, csname, cstext, true);
    }
  }
}

// anchorName として適切かどうかを検証。
protected bool IsAnchorNameValid(string anchorName)
{
  // パターンは実際に合わせて適宜変更してください。
  string pattern = "^positionToMove$";

  // anchorName が null の場合 ArgumentNullException
  // がスローされるので注意。
  return Regex.IsMatch(anchorName, pattern);
}

上記のコードには、悪意のあるユーザーによるスクリプトの注入に対応するため、クエリ文字列がアンカー名として適切かどうかを検証する例を含めています。アンカー名が固定で良ければ、クエリ文字列でスクリプトを組み立てない方がいいと思います。

Tags: ,

ASP.NET

SyntaxHighlighter とフラグメント識別子

by WebSurfer 2010年6月11日 12:35

IE6 または IE8 で(IE5 以前、IE7 は試してません)、SyntaxHighlighter を使用しているページの URL にフラグメント識別子を追加して要求すると、HTML の title タグに設定したタイトルがフラグメント識別子に書き換えられてしまうという問題が見つかりました。

どうも IE と SyntaxHighlighter の相性の問題のようで、Firefox 3.6.3, Opera 10.53, Safari 4.0.5 では問題なかったです。

このブログで試すには、以下のリンクをクリックしてみてください。

タイトルがフラグメント識別子に書き換えられる例

ブラウザにはまず上記リンクの URL がタイトルとして表示され、次に受信した HTML コードの title タグに設定されたタイトル "SyntaxHighlighter" に書き換えられ、最後にフラグメント識別子 "#fragment" に書き換えられるはずです。

原因が特定できていませんが、クライアント側のみの問題で、SyntaxHighlighter 関係のスクリプトが動くとこの問題が起こるようです。でも、意図的に書き換えているわけではなさそうな感じです。

気にするほどのことでもなさそうですが、原因不明のまま放置するのも気分が良くないし、といって解決策は見つからないしで悩んでいます。

Tags: ,

BlogEngine.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年3月  >>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar