1

私は ASP.NET MVC 2.0 を使用しており、コントローラーのモデル バインディングとモデル状態の検証を利用しようとしています。しかし、私は問題に直面したので、ここにいる人々と共有して、あなたの考えを知りたいと思いました.

わかりました、モデル クラス ライブラリにきれいなユーザー poco があります...

namespace Model
{    
    public partial class User
    {
        public virtual int Id { get; private set; }
        public virtual string UserName { get; private set; }
        public virtual string DisplayName { get; set; }
        public virtual string Email { get; set; }

        public User(string displayName, string userName)
            : this()
        {
            DisplayName = displayName;
            UserName = userName;
        }
    }
}

私が目指した設計では、オブジェクトが構築された後、特定のプロパティのみを編集できます。たとえば、 UserName は、オブジェクトが構築されたときにのみ設定できます。これは私にとっては理にかなっていますが、私の問題の鍵であるため、ここで強調したいと思いました。

次に、 User クラスの検証メタデータを定義する「仲間クラス」を作成します...

namespace Model
{
[MetadataType(typeof(UserMetadata))]
public partial class User
{
    class UserMetadata
    {
        [Required]
        public virtual int Id { get; set; }

        [Required]
        public virtual string UserName { get; set; }

        [Required]
        public virtual string DisplayName { get; set; }

        [RegularExpression(@"^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$", ErrorMessage = "Invalid address")]
        public virtual string Email { get; set; }
    }
}

}

次に、Web レイヤーで、ユーザーがこのオブジェクトを編集できるようにします。したがって、プロファイル コントローラーには次の 2 つのアクション メソッドがあります。

namespace Web.Controllers
{
    public class ProfileController : Controller
    {
        [Authorize]
        public ActionResult Edit()
        {
            var user = _session.Single<User>(x => x.UserName == HttpContext.User.Identity.Name );
            return View(user);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        [Authorize]
        [TransactionFilter]
        public ActionResult Edit(User updatedUser)
        {
            // Get the current user to update.
            var user = _session.Single<User>(x => x.UserName == HttpContext.User.Identity.Name);

            if (ModelState.IsValid)
            {
                TryUpdateModel(user);
                // Update store...                
            }
            return View(updatedUser);
        }
    }
}

これには、強く型付けされたビューがあります...

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Model.User>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Edit
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <%=Html.Script("jquery.validate.js")%>
    <%=Html.Script("MicrosoftMvcJQueryValidation.js")%>
    <%=Html.Script("MvcFoolproofJQueryValidation.js")%>
    <div class="container">
        <div class="column span-14">
        <% using (Html.BeginForm()) {%>
            <%= Html.AntiForgeryToken() %>
            <fieldset>
                <%: Html.DisplayFor(model => model.UserName) %>
                <%= Html.Label("Display Name") %>
                <%= Html.HiddenFor(model => model.DisplayName)%>
                <%= Html.ValidationMessageFor(model => model.DisplayName)%>
                <%= Html.Label("Email address") %>
                <%= Html.EditorFor(model => model.Email)%>
                <%= Html.ValidationMessageFor(model => model.Email)%>
                <%= Html.HiddenFor(model => model.UserName)%>
                <p>
                    <input type="submit" value="Save" />
                </p>
            </fieldset>
        </div>
        <div class="clear"></div>
        <% } %>
    </div>
</asp:Content>

これですべてのコードが終わりました!!

ここに問題があります。ビューは、最初の get 要求の後に正常にレンダリングされます。しかし、ユーザーが表示名を編集した後などにフォームを投稿すると、ModelState は無効になります。これは、UserName プロパティにプライベート セッターがあるためです。ただし、これは設計によるものであり、セキュリティとセマンティクスのために、ユーザー名を変更したくないため、セッターはプライベートです。ただし、プロパティに Required 属性を追加したため、設定されていないため失敗しています。

問題は、モデルバインディングがこれを検証エラーとして報告するかどうかです! プロパティはプライベートであるため、設定されないように設計されているため、設計上、モデル バインダーが設定することは想定していませんが、検証エラーは発生しません。設定できるプロパティの検証エラーのみを生成する必要があると思います。

わかりましたので、これまでに思いついた可能な解決策..

プロパティを公開します。

これを行うと、既存のユーザーのユーザー名を変更できるようになります。これをキャッチするには、どこかに追加のロジックを追加する必要がありますが、あまり良くありません。また、アクション メソッドに Bind Exclude を追加して、悪意のあるユーザーが投稿を介して設定しようとするのを阻止する必要があります。

エラーを取り除く

ModelState ディクショナリからエラーを削除できると思います。この場合はこれで問題ありませんが、プライベート セッターを持つすべてのオブジェクトにこれを追加する必要があるため、コードの臭いが発生すると思います。多分忘れます!!

インターフェイスに対して自分のビューを強く型付けする

ビューをモデルのインターフェースにバインドする人もいると読んだことがあります。これは、ビジネスモデルオブジェクトへの ModelView インターフェースの王様です。このアイデアは気に入っていますが、自動バインドが失われ、Web レイヤーでモデル オブジェクトとそのコンストラクターを複製する必要があります。これに関する情報はhttp://www.codethinked.com/post/2010/04/12/Easy-And-Safe-Model-Binding-In-ASPNET-MVC.aspxにあります。

モデル ビューを使用する

これは私にはDRYに見えませんか?! 適合する既存のモデル オブジェクトがない場合は、喜んでこれらを使用します (たとえば、サインアップ モデル ビューを使用します)。

CustomModelBinder

私の好みのオプションですが、自分が何をしているのかわかりません!! 設定できるプロパティにのみバインドするようにバインダーを取得できたら、笑ってしまいます!!

人々はどう思いますか?上記のオプションに関するコメント、その他の解決策はありますか?私のアーキテクチャとは的外れですか?!

ありがとう :)

4

3 に答える 3

2

設定されないように設計したので、設計上、モデルバインダーが設定することは期待していませんが、検証エラーは発生しません。可能なプロパティの検証エラーのみを生成する必要があると思います。セット。

この設計上の決定について詳しくは、こちらをご覧ください。

http://bradwilson.typepad.com/blog/2010/01/input-validation-vs-model-validation-in-aspnet-mvc.html

興味深いことに、ほとんどの人はあなたの不満とは正反対の不満を言っていました。;)

基本的に、設定できないものは常に設定する必要があることをシステムに伝えます。したがって、MVCが正しく機能していないとは言えません。不可能なシナリオをコーディングするだけです。


全体として、metadatabuddyテクニックの問題点に到達したばかりです。主に、新しいシナリオと編集シナリオで異なる検証を行う必要があります。

これを行うと、既存のユーザーのユーザー名を変更できるようになります。これをキャッチするために、どこかにロジックを追加する必要がありますが、あまり良くありません。アクションメソッドにBindExcludeを追加する必要もあります。いたずらな人が投稿を介してそれを設定しようとするのを防ぐためです。

これらのコードの変更を食べ過ぎている私見。単一のメソッド呼び出しに単純な文字列を追加することになります。大したことは何ですか?ここでは実用的なアプローチを取ります。

于 2010-07-12T17:37:56.360 に答える
2

仕事に最適なので、ビューモデルを使用します。DRY が 2 つのオブジェクトでプロパティを繰り返すことができないことを意味するとは考えないでください。「ロジックを複製したり、2 つの場所で同一のデータを保持したりしないでください」と考えてください。この場合、モデル バインディングを処理するセマンティクスがドメイン モデルと一致しないため、それを変換する方法が必要です。

于 2010-07-12T17:49:51.713 に答える
0

jfar は、Brad がコメントしている Brad Wilson の投稿への適切なリンクを投稿しました...

部分的な編集はできますが、部分的な検証はできなくなりました。したがって、[Required] 属性を使用してバインディングを除外すると、検証は失敗します。これを回避するには、いくつかの選択肢があります。

  • フォーム データを正確に反映するビュー モデルを使用する

  • (Try)UpdateModel を呼び出す前に、[必須] でバインドされていないフィールドにデータを事前入力して、検証が成功するようにします (そのデータで何もするつもりはありませんが)。

  • 検証エラーが発生するのを許可し、検証が完了したら ModelState から削除します。これは不適切なエラーであるためです。

私のケースは、特定のフィールドを更新したくない「部分編集」ケースに当てはまるようです。

これらを解決策として検討します。

于 2010-07-13T18:57:02.053 に答える