WebSurfer's Home

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

Firefox と PostBackUrl

by WebSurfer 2019年3月9日 15:51

ASP.NET Web Forms アプリで、クロスページポストバックにより別のページに遷移し、その後ブラウザの戻るボタンで元のページに戻った場合、ブラウザによってはページングなどの機能が働かなくなる(ページングしたいのにクロスページポストバックされてしまう)という話を書きます。元の話は Teratail のスレッド「DataPagerの挙動について」のものです。

form 要素の action 属性

ASP.NET Web Forms アプリでは form 要素の action 属性に自身のページの url が設定されますので、ボタンクリックなどの操作で form が submit されると自身のページに POST されます。それが ASP.NET での既定の動きで「ポストバック」と呼ばれています。

クロスページポストバックとは、自身のページではなく、他のページに POST することです。それには、Button などに用意されている PostBackUrl プロパティにポスト先のページの url を設定します。それにより、Button が html の input 要素に変換された時、その onclick 属性にクロスページポストバックを行うためのクライアントスクリプトが設定されます。

その Button をクリックすると、まず onclick 要素に設定されたクライアントスクリプトが動いて form 要素の action 属性が PostBackUrl プロパティに設定された url に書き換えられ、その後 form が submit されます。結果、クロスページポストバックが行われるという仕組みになっています。(詳しい説明は上に紹介した Teratail のスレッドにありますのでそちらを見れください)

予期せぬ動きと言うのは何かと言うと、ブラウザによっては、クロスページポストバックで別ページに遷移後、戻るボタンをクリックして元のページを表示した場合、PostBackUrl が設定されてないボタンをクリックしてもクロスページポストバックされてしまうことです。

上に紹介した Teratail のスレッドの話はページャーの話でしたが、ページャーに限らす、ポストバックすることにより動くソート、編集などのボタンクリックでも同様にクロスページポストバックされてしまいます。

この記事を書いた時点で自分が試した限りですが、Firefox 65.0.2 と Safari 5.1.7 がそういう動きをするブラウザでした。IE11, Edge 44.17763.1.0, Chrome 72.0.3626.121, Opera 58.0.3135.90 は期待通り自身のページにポストバックされました。

何故ブラウザによってそういう予期せぬ動きになるかと言うと、ページをキャッシュに保存するタイミングの違いです。

ASP.NET Web Forms アプリのデフォルトのキャッシュコントロール設定では、ブラウザは別のページに遷移する前に元のページをキャッシュに保存します。遷移後、ブラウザの戻るボタンをクリックすると、ブラウザは元のページをキャッシュから取得します。そこのところは Firefox に限らず IE でも Chrome でも同じです。

Firefox, Safari はクロスページポストバックを行うためのクライアントスクリプトが動いて form 要素の action 属性が別ページの url に書き換えられた後キャッシュに保存するのに対し、IE, Edge, Chrome, Opera は書き換える前(即ち、action 属性が自身のページの url の時)にキャッシュするという違いがあります。(ブラウザとしてどちらが正解かはいろいろ議論があるところと思いますが、その話はちょっと置いときます)

この記事の上の画像は Firefox でクロスページポストバックで別ページに遷移後、戻るボタンで元のページを表示し、開発ツールで html ソースの form 要素を表示したものです。action 属性の url は別ページに書き換えられています。

この状態では、PostBackUrl の設定有無にかかわらず、form を submit する操作をすれば、action 属性に設定されたページにクロスページポストバックされてしまうという訳です。

先の記事「PostBackUrl と Page.PreviousPage」にも書きましたが、PostBackUrl(クロスページポストバック)はいろいろ問題が多く、どうしても必要というケースもなさそうですので、使わないようにすべきと思っています。

Tags: ,

ASP.NET

PostBackUrl と Page.PreviousPage

by WebSurfer 2010年8月27日 17:36

マスターページに Button を配置して Click イベントで MenuPage にリダイレクトするようにしているとします。

あるページ(SourcePage とします)では、Button クリックで MenuPage ではなく別のページ(TargetPage とします)に遷移させたいので、SourcePage の Page_Load で Button.PostBackUrl に TargetPage を設定しました。

それで期待通りに、SourcePage で Button をクリックした場合は TargetPage に遷移していました。

ところが、マスターページの Page_Load に Page.PreviousPage を取得するコードを追加したところ、SourcePage で Button をクリックしても、TargetPage に遷移せず、MenuPage にリダイレクトされるようになってしまいました。

PostBackUrl の設定が無視されて Click イベントが発生しているようです。何故、Page.PreviousPage を取得すると、そうなってしまうのでしょうか?

サンプルのソースコードをアップしておきます。

MasterPage
<%@ Master Language="C#" ClassName="MasterExample" %>

<!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)
  {
    // Page.PreviousPage を取得すると Button1.Click イ
    // ベントが発生する。
    if (Page.PreviousPage != null)
    {
      Button btn2 = 
        (Button)Page.PreviousPage.Master.FindControl("Button1");
    }
  }

  // Button1 の PostBackUrl, Text をコンテンツから設定
  // するためのプロパティ。
  public string SetPostBackUrl
  {
    set { Button1.PostBackUrl = value; }
  }

  public string SetButtonText
  {
    set { Button1.Text = value; }
  }

  // 通常は Button1 クリックで MenuPage へリダイレクト
  protected void Button1_Click(object sender, EventArgs e)
  {
    Response.Redirect("MenuPage.aspx");
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title></title>
  <asp:ContentPlaceHolder id="head" runat="server">
  </asp:ContentPlaceHolder>
</head>
<body>
  <form id="form1" runat="server">
  <asp:Button ID="Button1" 
    runat="server" 
    Text="Go To MenuPage" 
    OnClick="Button1_Click" />
  <hr />
  <div>
    <asp:ContentPlaceHolder id="ContentPlaceHolder1" 
      runat="server">        
    </asp:ContentPlaceHolder>
  </div>
  </form>
</body>
</html>
SourcePage
<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" %>

<script runat="server">
  // このページの時は、Button1 クリックで、Menu ページでは
  // なく TargetPage にクロスページポストバックで遷移する。
  // そのため、以下のように Button1 の設定を変更する。
  // SetPostBackUrl, SetButtonText プロパティはマスターペー
  // ジで定義している。
  protected void Page_Load(object sender, EventArgs e)
  {
    MasterExample m = (MasterExample)Page.Master;
    m.SetPostBackUrl = "TargetPage.aspx";
    m.SetButtonText = "Go To TargetPage";
  }
</script>

<asp:Content ID="Content1" 
    ContentPlaceHolderID="head" 
    Runat="Server">
</asp:Content>

<asp:Content ID="Content2" 
    ContentPlaceHolderID="ContentPlaceHolder1" 
    Runat="Server">
    <h1>This is the Source Page.</h1>
</asp:Content>

ASP.NET 内部でどのような処理がされているか見えないので多分に推測が入っていますが、以下のようなことではないかと思います。

  1. SourcePage を要求すると、Page_Load で Button1.PostBackUrl に TargetPage が設定される。ブラウザ上で Button1 をクリックすると、クライアント側のスクリプトによって TargetPage が要求される。
  2. その際、サーバーには、SourcePage の ViewState と共に、Button1 がクリックされたという情報も送られる。
  3. サーバー側に制御が戻り、TargetPage がロードされ実行される。
  4. そこで Page.PreviousPage が参照されていると、SourcePage の状態を取得するため、SourcePage がロードされ実行される。
  5. その時、通常発生する Page.Init, Page.Load 等のイベントのみならず Button1.Click イベントも発生し(すべてのイベントを発生させて処理しないと正しく状態が取得できないはず)、そのハンドラで遷移先に設定してある MenuPage にリダイレクトされてしまう。

上記を裏付ける Microsoft のライブラリなどを探したのですが、残念ながら見つかるませんでした。見つけた中で一番信頼の置けそうなのが以下のサイトです。

Solve Postback Hassles with Cross-Page Postbacks in ASP.NET 2.0

というわけで、このような問題を避けるため、ASP.NET でクロスページポストバックを使うのはできるだけ避けた方がよさそうです。

今回のケースでは、対症療法的に以下のようにして対応できますが、予期しない副作用があるかもしれませんし。

protected void Button1_Click(object sender, EventArgs e)
{
  if (!Page.IsCrossPagePostBack)
  {
    Response.Redirect("MenuPage.aspx");
  }
}

Tags:

ASP.NET

About this blog

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

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar