ASP.NET MVC 2의 모델에 대한 개인 속성에 대해 ModelState가 유효하지 않은 것으로 보고되었습니다.

StackOverflow https://stackoverflow.com/questions/3229971

문제

저는 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은 개체가 생성될 때만 설정할 수 있습니다. 나에게는 이것이 OO로 이해되지만 이것이 내 문제의 핵심이므로 여기서 강조하고 싶었습니다.

그런 다음 내 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; }
    }
}

}

그런 다음 내 웹 레이어에서 사용자가 이 개체를 편집할 수 있도록 허용하고 싶습니다.그래서 내 프로필 컨트롤러에는 다음 두 가지 작업 방법이 있습니다.

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 속성에 개인 설정자가 있기 때문입니다.그러나 이것은 의도적으로 설계된 것입니다. 보안 및 의미론을 위해 사용자 이름을 변경하는 것을 원하지 않으므로 설정자는 비공개입니다.하지만 속성에 필수 속성을 추가했는데 설정되지 않아 실패합니다!

문제는 모델 바인딩이 이를 유효성 검사 오류로 보고해야 하는지 아닌지입니다.속성이 비공개이므로 설정되지 않도록 설계했습니다. 따라서 설계상 모델 바인더가 속성을 설정할 것으로 예상하지 않지만 유효성 검사 오류는 원하지 않습니다.내 생각에는 설정할 수 있는 속성에 대해서만 유효성 검사 오류가 발생해야 한다고 생각합니다.

좋아, 지금까지 내가 생각해낸 가능한 해결책은..

속성을 공개로 설정합니다.

이렇게 하면 기존 사용자의 사용자 이름을 변경할 수 있게 됩니다.나는 이것을 잡기 위해 어딘가에 추가 논리를 추가해야 할 것입니다. 별로 좋지는 않습니다.또한 게시물을 통해 설정하려는 장난스러운 사람들을 막기 위해 작업 메서드에 Bind Exclude를 추가해야 합니다.

오류 제거

나는 ModelState 사전에서 오류를 제거할 수 있다고 믿습니다. 이 경우에는 괜찮을 것입니다. 그러나 개인 설정자가 있는 모든 개체에 대해 이 오류를 추가해야 하므로 코드 냄새가 발생할 것이라고 생각합니다.아마 잊어버릴 것 같아요!!

인터페이스에 대해 내 보기를 강력하게 입력하세요.

어떤 사람들은 자신의 뷰를 모델의 인터페이스에 바인딩한다는 것을 읽었습니다. 이는 비즈니스 모델 개체에 대한 ModelView 인터페이스의 왕입니다.이 아이디어는 마음에 들지만 자동 바인딩이 느슨해져서 모델 객체를 웹 레이어의 생성자와 복제해야 할 것입니다. 잘 모르겠나요?!여기에 대한 정보가 있습니다. http://www.codethinked.com/post/2010/04/12/Easy-And-Safe-Model-Binding-In-ASPNET-MVC.aspx.

모델 뷰 사용

이것은 나에게 건조하지 않은 것 같습니까?!적합한 기존 모델 개체가 없는 경우에도 기꺼이 이를 사용합니다(예: 가입 모델 보기를 사용합니다).

CustomModelBinder

내가 선호하는 옵션이지만 내가 무엇을 하고 있는지 잘 모르겠습니다!!바인더가 설정할 수 있는 속성에만 바인딩하도록 할 수 있다면 정말 웃을 것입니다!!

사람들은 어떻게 생각하나요?위 옵션, 다른 솔루션에 대한 의견이 있습니까? 내 아키텍처가 제대로 맞지 않습니까?!

감사해요 :)

도움이 되었습니까?

해결책 3

jfar는 Brad가 댓글을 남긴 Brad Wilson의 게시물에 대한 좋은 링크를 게시했습니다.

여전히 부분 편집을 할 수 있지만 더 이상 부분적으로 검증 할 수는 없습니다.따라서 [필수] 속성으로 무언가를 바인딩하는 것을 제외하면 유효성 검사가 실패합니다.이 문제를 해결하기위한 몇 가지 선택이 있습니다.

  • 양식 데이터를 정확하게 미러링하는 뷰 모델을 사용하세요.

  • 호출하기 전에 데이터가 포함 된 [필수]이지만 부정확 한 필드를 미리 채우기 위해 (시도) Updatemodel을 확인하여 유효성 검사가 성공하도록합니다 (해당 데이터로 아무것도하지 않더라도)

  • 유효성 검사 오류가 발생한 다음 부적절한 오류가 있으므로 유효성 검사가 완료된 후 ModelSTate에서 제거하십시오.

내 사례는 특정 필드가 업데이트되는 것을 원하지 않는 '부분 편집' 사례에 적합한 것 같습니다.

나는 이것을 해결책으로 살펴볼 것입니다.

다른 팁

"나는 이것이 설정되지 않도록 설계했으므로 설계상으로는 모델 바인더가 이를 설정할 것으로 기대하지 않지만 유효성 검사 오류는 원하지 않습니다.내 생각에는 설정할 수 있는 속성에 대해서만 유효성 검사 오류가 발생해야 한다고 생각합니다."

이 디자인 결정에 대한 자세한 내용은 여기를 참조하세요.

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

흥미롭게도 대부분의 사람들은 귀하가 불평하는 것과 정반대로 불평했습니다.;)

기본적으로 설정할 수 없는 항목은 항상 설정해야 한다고 시스템에 알리는 것입니다.따라서 MVC가 잘못 작동하거나 그와 유사한 것은 없다고 말하고 싶습니다.불가능한 시나리오를 코딩하는 것뿐입니다.


전반적으로 Metadatabuddy 기술의 문제점에 도달했습니다.주로 새로운 시나리오와 편집 시나리오에 대해 서로 다른 검증이 필요합니다.

"이렇게 하면 기존 사용자의 사용자 이름을 변경할 수 있게 됩니다.나는 이것을 잡기 위해 어딘가에 추가 논리를 추가해야 할 것입니다. 별로 좋지는 않습니다.또한 게시물을 통해 설정하려는 장난스러운 사람들을 막기 위해 작업 메서드에 Bind Exclude를 추가해야 합니다."

IMHO 이러한 코드 변경으로 인해 과식이 발생했습니다.단일 메서드 호출에 간단한 문자열을 추가하게 됩니다.무슨 큰일이야?나는 여기서 실용적인 접근 방식을 취하겠습니다.

나는 작업에 가장 적합하기 때문에 뷰 모델을 사용하겠습니다.DRY가 두 개체의 속성을 반복할 수 없다는 의미가 아니라 "논리를 복제하지 않거나 두 위치에 동일한 데이터를 유지하지 않음"이라고 생각하세요.이 경우 모델 바인딩을 처리하는 의미 체계가 도메인 모델과 일치하지 않으므로 이를 변환할 방법이 필요합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top