ASP.NETでより詳細に制御するにはどうすればよいですか?


124

私は、非常にシンプルな「マイクロWebアプリ」を構築しようとしています。これを実現できれば、いくつかのスタックオーバーフローユーザーにとって興味があると思います。私はそれをバニラASP.NET 3.5(つまりMVCではない)の深さサイトの私のC#でホストしています。

フローは非常に簡単です。

  • ユーザーがすべてのパラメーターを指定していない(またはそれらのいずれかが無効である)URLでアプリを入力した場合、ユーザー入力コントロールを表示するだけです。(2つしかありません。)
  • ユーザーがURLでアプリを入力すると、すべての必要なパラメータを持って、私は結果を表示したい入力コントロールを(彼らはパラメータを変更できるように)

これは、自分で課した要件(設計と実装の混合)です。

  • 投稿でPOSTではなくGETを使用したいので、ほとんどの場合、ユーザーは簡単にページをブックマークできます。
  • 私はしていない URLがその上に余分なこまごまとし、提出後愚か探して終わるしたいです。メインURLと実際のパラメータのみを入力してください。
  • 理想的には、JavaScriptをまったく必要としないようにしたいと思います。このアプリにはそれのための正当な理由はありません。
  • レンダリング中にコントロールにアクセスして値を設定できるようにしたい。特に、ASP.NETがこれを自動的に実行できない場合は、渡されたパラメーター値にコントロールのデフォルト値を設定できるようにしたい私にとって(他の制限内で)。
  • 私はすべてのパラメーター検証を自分で行うことができて嬉しく、サーバー側のイベントの方法にそれほど必要ありません。ボタンなどにイベントをアタッチする代わりに、ページの読み込み時にすべてを設定するのは本当に簡単です。

これのほとんどは大丈夫ですが、ビューステートを完全に削除し、残りの便利な機能を維持する方法は見つかりませんでした。このブログ投稿の投稿を使用して、viewstateの実際のを取得しないようにしましたが、最終的にはURLのパラメーターとして表示されるため、見苦しいようです。

ASP.NETフォームではなくプレーンHTMLフォームにした場合(つまり、テイクアウトrunat="server")、マジックビューステートを取得できませんが、プログラムからコントロールにアクセスできません。

私は可能性が ASP.NETのほとんどを無視し、XMLにLINQを使用してXML文書を構築し、実施することにより、このすべてを行いますIHttpHandler。しかし、それは少し低いレベルに感じます。

私の問題は、制約を緩和する(たとえば、POSTを使用し、余剰パラメーターを気にしない)かASP.NET MVCを使用することで解決できることに気付きましたが、私の要件は本当に無理ですか?

たぶん、ASP.NETはこの種のアプリにスケールダウンしないのでしょうか?ただし、非常に可能性の高い代替案があります。私はただ愚かであり、それを実行するための完全に単純な方法があり、私がまだ見つけていないのです。

何か考えはありますか?(強大さがどのように落ちたのかについてのキューコメントなど。それは結構です-私はASP.NETの専門家であると主張したことがないことを願っています。真実はまったく反対です...)


16
「強大さがどのように倒れたかについてのキューのコメント」-私たちは皆、無知であり、異なることについてだけです。私は最近ここに参加し始めたばかりですが、すべてのポイントよりも質問に感心します。あなたはまだまだ考え、学んでいます。あなたへの称賛。
duffymo 2009年

15
学習をあきらめた誰かに注意を払うことはないと思います:)
Jon Skeet

1
一般的には真です。コンピュータサイエンスでは非常に真実です。
Mehrdad Afshari、

3
次の本は「ASP.NET in Depth」になりますか?:-P
チャクリット

20
はい、2025年に発行予定です;)
Jon Skeet、

回答:


76

このソリューションにより、コントロールのすべての属性を含むコントロール全体にプログラムからアクセスできるようになります。また、送信時にテキストボックスの値のみがURLに表示されるため、GETリクエストURLはより「意味のある」ものになります

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="JonSkeetForm.aspx.cs" Inherits="JonSkeetForm" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Jon Skeet's Form Page</title>
</head>
<body>
    <form action="JonSkeetForm.aspx" method="get">
    <div>
        <input type="text" ID="text1" runat="server" />
        <input type="text" ID="text2" runat="server" />
        <button type="submit">Submit</button>
        <asp:Repeater ID="Repeater1" runat="server">
            <ItemTemplate>
                <div>Some text</div>
            </ItemTemplate>
        </asp:Repeater>
    </div>
    </form>
</body>
</html>

次に、コードビハインドで、PageLoadで必要なすべてを実行できます。

public partial class JonSkeetForm : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        text1.Value = Request.QueryString[text1.ClientID];
        text2.Value = Request.QueryString[text2.ClientID];
    }
}

を含むフォームがrunat="server"不要な場合は、HTMLコントロールを使用する必要があります。目的に合わせて作業する方が簡単です。通常のHTMLタグを使用runat="server"して、IDを付けてください。その後、プログラム使用してそれらにアクセスしなしコードを記述できますViewState

唯一の欠点は、GridViews などの「役立つ」ASP.NETサーバーコントロールの多くにアクセスできないことです。Repeater結果と同じページにフィールドが必要であり、(私の知る限り)a Repeaterrunat="server"Formタグの属性なしで実行される唯一のDataBoundコントロールであると想定しているため、私の例にを含めました。


1
フィールドが非常に少ないため、手動で実行するのは非常に簡単です。重要なのは、通常のHTMLコントロールでrunat = serverを使用できることを知らなかったことです。まだ結果を実装していませんが、それは簡単なことです。もうすぐです!
Jon Skeet、

実際、<form runat = "server">は、ページレベルでEnableViewState = "False"を設定した場合でも、__ VIEWSTATE(およびその他の)非表示フィールドを追加します。これは、ページのViewStateを失いたい場合に使用する方法です。URLの親しみやすさに関しては、urlrewritingがオプションかもしれません。
セルジュダミアン

1
書き直す必要はありません。この回答は正常に機能します(ただし、IDが "user"のコントロールがあることを意味します。何らかの理由で、テキストボックスコントロールの名前をIDと別に変更することはできません)。
Jon Skeet、

1
確認のために、これは確かに非常にうまくいきました。どうもありがとう!
Jon Skeet、

14
あなたはそれを古典的なaspで書いたはずです!
ScottE 2009

12

FORMタグでrunat = "server"を使用しないことで、間違いなく(私見)正しい方向に進んでいます。これは、この例のように、Request.QueryStringから直接値を抽出する必要があることを意味します。

.aspxページ自体:

<%@ Page Language="C#" AutoEventWireup="true" 
     CodeFile="FormPage.aspx.cs" Inherits="FormPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>ASP.NET with GET requests and no viewstate</title>
</head>
<body>
    <asp:Panel ID="ResultsPanel" runat="server">
      <h1>Results:</h1>
      <asp:Literal ID="ResultLiteral" runat="server" />
      <hr />
    </asp:Panel>
    <h1>Parameters</h1>
    <form action="FormPage.aspx" method="get">
    <label for="parameter1TextBox">
      Parameter 1:</label>
    <input type="text" name="param1" id="param1TextBox" value='<asp:Literal id="Param1ValueLiteral" runat="server" />'/>
    <label for="parameter1TextBox">
      Parameter 2:</label>
    <input type="text" name="param2" id="param2TextBox"  value='<asp:Literal id="Param2ValueLiteral" runat="server" />'/>
    <input type="submit" name="verb" value="Submit" />
    </form>
</body>
</html>

そしてコードビハインドでは:

using System;

public partial class FormPage : System.Web.UI.Page {

        private string param1;
        private string param2;

        protected void Page_Load(object sender, EventArgs e) {

            param1 = Request.QueryString["param1"];
            param2 = Request.QueryString["param2"];

            string result = GetResult(param1, param2);
            ResultsPanel.Visible = (!String.IsNullOrEmpty(result));

            Param1ValueLiteral.Text = Server.HtmlEncode(param1);
            Param2ValueLiteral.Text = Server.HtmlEncode(param2);
            ResultLiteral.Text = Server.HtmlEncode(result);
        }

        // Do something with parameters and return some result.
        private string GetResult(string param1, string param2) {
            if (String.IsNullOrEmpty(param1) && String.IsNullOrEmpty(param2)) return(String.Empty);
            return (String.Format("You supplied {0} and {1}", param1, param2));
        }
    }

ここでの秘訣は、テキスト入力のvalue = ""属性内でASP.NETリテラルを使用しているため、テキストボックス自体がrunat = "server"である必要がないということです。結果はASP:Panel内にラップされ、結果を表示するかどうかに応じて、ページの読み込み時にVisibleプロパティが設定されます。


これはかなりうまく機能しますが、URLは、たとえばStackOverflowほどフレンドリーではありません。
Mehrdad Afshari、

1
URLはとてもフレンドリーだと思います...これは本当に良い解決策のようです。
Jon Skeet、

ああ、私は以前あなたのつぶやきを読んでそれを調査しました、そして今私はあなたの浴槽のために私のそびれた子供たちを準備するあなたの質問を逃しました... :-)
splattne

2

オーケージョン、まずビューステートの問題:

2.0以降、内部コードの変更の有無を確認していませんが、数年前にビューステートを削除する方法を以下に示します。実際には、その非表示フィールドはHtmlForm内にハードコードされているため、新しいフィールドを派生させて、自分で呼び出してレンダリングを開始する必要があります。プレーンな古い入力コントロールに固執する場合は、__ eventtargetと__eventtargetを省略することもできることに注意してください(クライアントにJSを必要としないので役立つので、これを使用したいと思います)。

protected override void RenderChildren(System.Web.UI.HtmlTextWriter writer)
{
    System.Web.UI.Page page = this.Page;
    if (page != null)
    {
        onFormRender.Invoke(page, null);
        writer.Write("<div><input type=\"hidden\" name=\"__eventtarget\" id=\"__eventtarget\" value=\"\" /><input type=\"hidden\" name=\"__eventargument\" id=\"__eventargument\" value=\"\" /></div>");
    }

    ICollection controls = (this.Controls as ICollection);
    renderChildrenInternal.Invoke(this, new object[] {writer, controls});

    if (page != null)
        onFormPostRender.Invoke(page, null);
}

したがって、これらの3つの静的MethodInfoを取得し、そのビューステートの部分をスキップして呼び出します;)

static MethodInfo onFormRender;
static MethodInfo renderChildrenInternal;
static MethodInfo onFormPostRender;

これがフォームの型コンストラクタです。

static Form()
{
    Type aspNetPageType = typeof(System.Web.UI.Page);

    onFormRender = aspNetPageType.GetMethod("OnFormRender", BindingFlags.Instance | BindingFlags.NonPublic);
    renderChildrenInternal = typeof(System.Web.UI.Control).GetMethod("RenderChildrenInternal", BindingFlags.Instance | BindingFlags.NonPublic);
    onFormPostRender = aspNetPageType.GetMethod("OnFormPostRender", BindingFlags.Instance | BindingFlags.NonPublic);
}

質問が正しい場合は、フォームのアクションとしてPOSTを使用したくないので、次のようにします。

protected override void RenderAttributes(System.Web.UI.HtmlTextWriter writer)
{
    writer.WriteAttribute("method", "get");
    base.Attributes.Remove("method");

    // the rest of it...
}

これはほぼそれだと思います。どうなるか教えてください。

編集:私はページのviewstateメソッドを忘れました:

そのため、カスタムフォーム:HtmlFormは、新しい(またはそうでない)抽象ページを取得します。ページ:System.Web.UI.Page:P

protected override sealed object SaveViewState()
{
    return null;
}

protected override sealed void SavePageStateToPersistenceMedium(object state)
{
}

protected override sealed void LoadViewState(object savedState)
{
}

protected override sealed object LoadPageStateFromPersistenceMedium()
{
    return null;
}

この場合、ページをシールすることができないため、メソッドをシールします(それが抽象的ではない場合でも、スコットガスリーが別のページにラップします:P)フォームをシールすることができます。


これをありがとう-それはかなり多くの仕事のように聞こえますが。ダンの解決策は私にとってはうまくいきましたが、より多くの選択肢があることは常に良いことです。
Jon Skeet、

1

POSTを排除するのではなく、フォームがPOSTされたときに適切なGET URLにリダイレクトすることを考えましたか?つまり、GETとPOSTの両方を受け入れますが、POST時にGETリクエストを作成してリダイレクトします。これは、ページから独立させる場合は、ページ上またはHttpModuleを介して処理できます。これで物事がずっと簡単になると思います。

編集:ページにEnableViewState = "false"が設定されていることを前提としています。


良いアイデア。まあ、それを強制されているという点では恐ろしいアイデアですが、おそらく機能しているという点で素晴らしいです:)試してみます...
Jon Skeet

そして、はい、私は至る所でEnableViewState = falseを試しました。完全に無効化するのではなく、単に削減するだけです。
Jon Skeet、

ジョン:のろわれたサーバーコントロール(runat = "server"なし)を使用せず、<form runat = "server">がまったくない場合、ViewStateは問題ありません。そのため、サーバーコントロールを使用しないと言いました。いつでもRequest.Formコレクションを使用できます。
Mehrdad Afshari、

ただし、コントロールにrunat = serverがないと、レンダリング時に値をコントロールに再度伝達するのが面倒です。さいわい、runat = serverを使用したHTMLコントロールはうまく機能します。
Jon Skeet、

1

ルーティングを処理するHTTPモジュールを作成し(MVCと同様ですが、洗練されていません。いくつかのifステートメントのみ)、それをページaspxまたはashxページに渡します。aspxページテンプレートを変更する方が簡単であるため、ただしWebControls、私は使用しませんaspx。ただResponse.Write

ちなみに、物事を簡素化するために、モジュールでパラメーター検証を行い(おそらくコードとルーティングを共有しているため)、それを保存HttpContext.Itemsしてページにレンダリングできます。これは、MVCとほとんど同じように機能しますが、すべての機能がありません。これは、ASP.NET MVCの日以前に私がよくやったことです。


1

ページクラスを完全に破棄し、URLに基​​づく大きなスイッチケースですべての要求を処理するだけで本当に満足しています。Evey "page"は、htmlテンプレートおよびc#オブジェクトになります。テンプレートクラスは、キーコレクションと比較する一致デリゲートを持つ正規表現を使用します。

利点:

  1. 非常に高速で、再コンパイル後もほとんど遅延がありません(ページクラスは大きくなければなりません)
  2. コントロールは本当にきめ細かい(SEOに最適であり、JSでうまく機能するDOMを作成する)
  3. プレゼンテーションはロジックとは別です
  4. jQueryはHTMLを完全に制御します

困惑:

  1. 単純なものは、1つのテキストボックスが複数の場所でコードを必要とするという点で少し時間がかかりますが、非常にうまくスケールアップします
  2. ビューステートが表示されるまで(ページが表示され、それから現実に戻るまで)、常にページビューを使用するのは魅力的です。

ジョン、土曜日の朝にSOで何をしているのですか:)?


1
ここは土曜日の夕方です。それで大丈夫ですか?(私は自分の投稿時間/日数の散布図を参照したいのですが...)
Jon Skeet

1

asp:Repeaterコントロールは廃止されたと思いました。

ASP.NETテンプレートエンジンは優れていますが、forループを使用して同じように簡単に繰り返すことができます...

<form action="JonSkeetForm.aspx" method="get">
<div>
    <input type="text" ID="text1" runat="server" />
    <input type="text" ID="text2" runat="server" />
    <button type="submit">Submit</button>
    <% foreach( var item in dataSource ) { %>
        <div>Some text</div>   
    <% } %>
</div>
</form>

ASP.NETフォームはまあまあですが、Visual Studioからはまともなサポートがありますが、このrunat = "server"のことは間違っています。ViewStateに。

ASP.NET MVCが非常に優れている理由、つまり、ASP.NETフォームアプローチから離れることなく、ASP.NET MVCから離れることを検討することをお勧めします。

NHamlのようなカスタムビューをコンパイルするための独自のビルドプロバイダーを作成することもできます。HTTPをラップするため、およびCLRホスティング環境として、ASP.NETランタイムに依存して、より詳細に制御するには、ここを参照する必要があると思います。統合モードを実行すると、HTTPリクエスト/レスポンスも操作できるようになります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.