124

私は非常に単純な「マイクロ Web アプリケーション」を構築しようとしています。私は、バニラ ASP.NET 3.5 (つまり、MVC ではない) である C# in Depth サイトでホストしています。

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

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

これが私の自主的な要件です(設計と実装の混合):

  • 主に、ユーザーがページを簡単にブックマークできるように、投稿で POST ではなく GET を使用する必要があります。
  • 送信後に URL がばかげたものに見えて、余分な部分が追加されてしまうのは望ましくありません。メインの URL と実際のパラメータだけをお願いします。
  • 理想的には、JavaScript をまったく必要としないようにしたいと考えています。このアプリには正当な理由はありません。
  • レンダリング時にコントロールにアクセスし、値などを設定できるようにしたいです。特に、ASP.NET がこれを自動的に実行できない場合、渡されたパラメーター値にコントロールの既定値を設定できるようにしたいと考えています。私にとって(他の制限内で)。
  • すべてのパラメーターの検証を自分で行うことができて満足しており、サーバー側のイベントの方法はあまり必要ありません。ボタンなどにイベントを添付する代わりに、ページの読み込み時にすべてを設定するのは非常に簡単です。

これのほとんどは問題ありませんが、viewstateを完全に削除して残りの便利な機能を維持する方法は見つかりませんでした。このブログ投稿の投稿を使用して、ビューステートの実際のを取得することをなんとか回避できましたが、それでも最終的には URL のパラメーターになってしまい、見栄えが悪くなります。

ASP.NET フォームではなくプレーンな HTML フォーム (つまり、取り出しrunat="server") にすると、魔法のビューステートは得られませんが、プログラムでコントロールにアクセスできません。

ASP.NET の大部分を無視し、LINQ to XML を使用して XML ドキュメントを構築しIHttpHandler . ちょっとレベル低い気がするけど。

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

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

何か考えはありますか?(強大な人がどのように倒されたかなどについてのキュー コメント。それで問題ありません。真実はまったく逆であるため、私が ASP.NET の専門家であると主張したことがないことを願っています...)

4

7 に答える 7

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

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

于 2009-01-10T17:18:36.200 に答える
12

FORM タグで runat="server" を使用しないことで、間違いなく (IMHO) 正しい軌道に乗っています。これは、次の例のように、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 プロパティが設定されます。

于 2009-01-10T17:02:23.660 に答える
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...
}

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

編集:ページのビューステートメソッドを忘れました:

したがって、カスタムフォーム: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;
}

この場合、私はメソッドを封印します。これは、ページを封印できないためです(抽象的でなくても、Scott Guthrieが別のページにラップします:P)が、フォームを封印することはできます。

于 2009-07-09T19:05:11.097 に答える
1

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

利点:

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

残念:

  1. 単純なものは、単一のテキストボックスが複数の場所にコードを必要とするという点で少し時間がかかりますが、それは本当にうまくスケールアップします
  2. ビューステート(urgh)が表示されるまで、ページビューでそれを実行したいのですが、その後、現実に戻ります。

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

于 2009-01-10T17:18:32.847 に答える
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フォームはある程度問題ありません。VisualStudioから適切なサポートがありますが、このrunat = "server"のことは、まったく間違っています。ViewStateから。

ASP.NET MVCが非常に優れている理由を確認することをお勧めします。これは、ASP.NETFormsアプローチからすべてを捨てることなく移行するものです。

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

于 2009-02-23T12:39:45.227 に答える
1

POST を排除するのではなく、フォームが POST されたときに適切な GET URL にリダイレクトすることを考えたことがありますか。つまり、GET と POST の両方を受け入れますが、POST では GET 要求を作成してリダイレクトします。これは、ページに依存しないようにする場合は、ページ上または HttpModule を介して処理できます。これにより、物事がはるかに簡単になると思います。

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

于 2009-01-10T16:52:24.053 に答える
1

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

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

于 2009-01-10T16:55:07.370 に答える