문제

ASP.NET MVC에 문제가 있는 것 같습니다. 페이지에 각각 동일한 이름을 사용하지만 다른 유형(라디오/숨김/등)을 사용하는 양식이 두 개 이상 있는 경우 첫 번째 양식 게시물(예를 들어 '날짜' 라디오 버튼을 선택함), 양식이 다시 렌더링되면(예: 결과 페이지의 일부로) 다른 양식에서 SearchType의 숨겨진 값이 표시되는 문제가 있는 것 같습니다. 마지막 라디오 버튼 값(이 경우 SearchType.Name)으로 변경됩니다.

다음은 축소 목적을 위한 예시 양식입니다.

<% Html.BeginForm("Search", "Search", FormMethod.Post); %>
  <%= Html.RadioButton("SearchType", SearchType.Date, true) %>
  <%= Html.RadioButton("SearchType", SearchType.Name) %>
  <input type="submit" name="submitForm" value="Submit" />
<% Html.EndForm(); %>

<% Html.BeginForm("Search", "Search", FormMethod.Post); %>
  <%= Html.Hidden("SearchType", SearchType.Colour) %>
  <input type="submit" name="submitForm" value="Submit" />
<% Html.EndForm(); %>

<% Html.BeginForm("Search", "Search", FormMethod.Post); %>
  <%= Html.Hidden("SearchType", SearchType.Reference) %>
  <input type="submit" name="submitForm" value="Submit" />
<% Html.EndForm(); %>

결과 페이지 소스(결과 페이지의 일부임)

<form action="/Search/Search" method="post">
  <input type="radio" name="SearchType" value="Date" />
  <input type="radio" name="SearchType" value="Name" />
  <input type="submit" name="submitForm" value="Submit" />
</form>

<form action="/Search/Search" method="post">
  <input type="hidden" name="SearchType" value="Name" /> <!-- Should be Colour -->
  <input type="submit" name="submitForm" value="Submit" />
</form>

<form action="/Search/Search" method="post">
  <input type="hidden" name="SearchType" value="Name" /> <!-- Should be Reference -->
  <input type="submit" name="submitForm" value="Submit" />
</form>

RC1을 사용하는 다른 사람이 이를 확인할 수 있습니까?

어쩌면 열거형을 사용하고 있기 때문일 수도 있습니다.모르겠습니다.숨겨진 필드에 '수동' 입력() 태그를 사용하면 이 문제를 피할 수 있지만 MVC 태그(<%= Html.Hidden(...) %>)를 사용하면 .NET MVC가 이를 대체한다는 점을 덧붙여야 합니다. 매번.

많은 감사를 드립니다.

업데이트:

오늘 또 이런 버그를 봤습니다.게시된 페이지를 반환하고 Html 도우미와 함께 MVC 설정된 숨겨진 양식 태그를 사용하면 머리가 잘리는 것 같습니다.내가 연락했어 필 핵 왜냐하면 나는 어디로 향해야 할지 모르기 때문이고, 이것이 David가 지정한 대로 예상되는 동작이어야 한다고 믿지 않기 때문입니다.

도움이 되었습니까?

해결책

예,이 동작은 현재 디자인에 의한 것입니다. 값을 명시 적으로 설정하더라도 동일한 URL에 다시 게시하면 모델 상태를보고 값을 사용합니다. 일반적으로 이것은 원래 값이 아닌 Postback에서 제출 한 값을 표시 할 수 있습니다.

가능한 두 가지 해결책이 있습니다.

해결책 1

각 필드에 고유 한 이름을 사용하십시오. 기본적으로 HTML 요소의 ID로 지정된 이름을 사용합니다. 여러 요소가 동일한 ID를 갖는 것이 유효하지 않은 HTML입니다. 따라서 독특한 이름을 사용하는 것이 좋습니다.

해결책 2

숨겨진 도우미를 사용하지 마십시오. 당신이 정말로 그것을 필요로하지 않는 것 같습니다. 대신, 당신은 이것을 할 수 있습니다 :

<input type="hidden" name="the-name" 
  value="<%= Html.AttributeEncode(Model.Value) %>" />

물론, 이것에 대해 더 생각하면, 포스트 백을 기반으로 값을 변경하는 것은 텍스트 상자에 적합하지만 숨겨진 입력에는 적합하지 않습니다. v1.0에 대해서는 이것을 변경할 수 없지만 v2에 대해서는 고려할 것입니다. 그러나 우리는 그러한 변화의 의미를 신중하게 생각해야합니다.

다른 팁

다른 사람들과 마찬가지로 나는 ModelState가 모델을 채우는 데 사용될 것으로 예상했을 것입니다. 우리는보기에서 표현식에서 모델을 명시 적으로 사용하므로 Modelstate가 아닌 모델을 사용해야합니다.

디자인 선택이며 이유를 얻습니다. 유효성 검사가 실패하면 입력 값이 모델의 데이터 유형에 구문 분석 할 수 없으며 사용자가 입력 한 잘못된 값을 렌더링하려면 쉽게 수정할 수 있습니다.

내가 이해하지 못하는 유일한 것은 다음과 같습니다. 디자인으로 모델이 사용되는 이유는 무엇입니까?

나는 많은 사람들이 같은 해결 방법을 사용하는 것을 보았다

  • ModelState.Clear () : 모든 ModelState 값을 지우지 만 기본적으로 MVC에서 기본 유효성 검사 사용을 비활성화합니다.
  • ModelState.remove ( "Nodey") : ModelState.Clear ()와 동일하지만 ModelState 키의 미세 관리가 필요합니다. 이는 너무 많은 작업이며 MVC의 자동 바인딩 기능으로는 적절하지 않습니다. 우리가 양식 및 쿼리 스트링 키를 관리 할 때 20 년 전의 느낌이 듭니다.
  • HTMLThemeselves 렌더링 : 너무 많은 작업, 세부 사항 및 추가 기능으로 HTML 헬퍼 방법을 버립니다. 예 : M.Name) "id =" @html.idfor (m => m.name) "value =" @html.attributeencode (model.name) "또는 @html을 바꾸십시오. dropdownlistfor by ...
  • 기본 MVC HTML 도우미를 교체하여 신규식 문제를 피하기 위해 사용자 정의 HTML 도우미를 만들어냅니다. 이는 HTML을 렌더링하는보다 일반적인 접근 방식이지만 여전히 다른 모든 기능을 유지하지만 모델보다 모델 상태 우선 순위를 비활성화하려면 더 많은 HTML+MVC 지식 또는 디 컴파일 시스템이 필요합니다.
  • 후 빨정 사후 평가 패턴을 적용하십시오. 일부 환경에서는 쉽지만 더 많은 상호 작용/복잡성이있는 환경에서는 더 어렵습니다. 이 패턴에는 장단점이 있으며 ModelState를 통한 ModelSTate의 선택된 선택으로 인해이 패턴을 적용해서는 안됩니다.

문제

따라서 문제는 모델이 ModelState에서 채워졌으며보기에서 모델을 사용하도록 명시 적으로 설정 한 것입니다. 모든 사람은 유효성 검사 오류가없는 한 모델 값 (변경된 경우)을 사용하기를 기대합니다. 그런 다음 ModelState를 사용할 수 있습니다.

현재 MVC 헬퍼 확장에서 ModelSTate 값은 모델 값보다 우선합니다.

해결책

따라서이 문제에 대한 실제 수정은 다음과 같습니다. 각 표현식이 모델 값을 가져 오려면 해당 값에 대한 유효성 검사 오류가 없으면 ModelSTATE 값을 제거해야합니다. 해당 입력 제어에 대한 유효성 검사 오류가 있으면 ModelSTATE 값을 제거하지 않아야하며 정상처럼 사용됩니다. 나는 이것이 문제를 정확하게 해결한다고 생각합니다. 이는 대부분의 해결 방법보다 낫습니다.

코드는 다음과 같습니다.

    /// <summary>
    /// Removes the ModelState entry corresponding to the specified property on the model if no validation errors exist. 
    /// Call this when changing Model values on the server after a postback, 
    /// to prevent ModelState entries from taking precedence.
    /// </summary>
    public static void RemoveStateFor<TModel, TProperty>(this HtmlHelper helper,  
        Expression<Func<TModel, TProperty>> expression)
    {
        //First get the expected name value. This is equivalent to helper.NameFor(expression)
        string name = ExpressionHelper.GetExpressionText(expression);
        string fullHtmlFieldName = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);

        //Now check whether modelstate errors exist for this input control
        ModelState modelState;
        if (!helper.ViewData.ModelState.TryGetValue(fullHtmlFieldName, out modelState) ||
            modelState.Errors.Count == 0)
        {
            //Only remove ModelState value if no modelstate error exists,
            //so the ModelState will not be used over the Model
            helper.ViewData.ModelState.Remove(name);
        }
    }

그런 다음 MVC 확장을 호출하기 전에 우리 자신의 HTML 헬퍼 확장을 만들어냅니다.

    public static MvcHtmlString TextBoxForModel<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression,
        string format = "",
        Dictionary<string, object> htmlAttributes = null)
    {
        RemoveStateFor(htmlHelper, expression);
        return htmlHelper.TextBoxFor(expression, format, htmlAttributes);
    }

    public static IHtmlString HiddenForModel<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
        Expression<Func<TModel, TProperty>> expression)
    {
        RemoveStateFor(htmlHelper, expression);
        return htmlHelper.HiddenFor(expression);
    }

이 솔루션은 문제를 제거하지만 MVC가 정상적으로 제공하는 모든 것을 디 컴파일, 분석 및 재건 할 필요는 없습니다 (정시에 변경, 브라우저 차이 등을 관리하는 것도 잊지 마십시오).

나는 논리를 생각한다 "유효성 검사 오류가 아니라면 Model value, modelstate" 부정확해야했을 것입니다. 만약 그렇다면, 그것은 많은 사람들을 물지 않았을 것이지만, 여전히 MVC가 의도 된 것을 다루었습니다.

방금 같은 문제를 겪었습니다. 통과 된 값에 대한 TextBox () 우선 순위와 같은 HTML 도우미 선적 서류 비치 말하는 곳 :

텍스트 입력 요소의 값. 이 값이 NULL 참조 (Visual Basic에서는 없음) 인 경우 요소 값은 ViewDatAdictionary 객체에서 검색됩니다. 값이 없으면 ModelStatedictionary 객체에서 값이 검색됩니다.

나에게, 나는 통과하면 값이 사용된다는 것을 읽었다. 그러나 TextBox () 소스를 읽는다 :

string attemptedValue = (string)htmlHelper.GetModelStateValue(name, typeof(string));
tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(name) : valueParameter), isExplicitValue);

실제 순서가 문서화 된 것과 정반대임을 나타냅니다. 실제 주문은 다음과 같습니다.

  1. Modelstate
  2. ViewData
  3. 값 (발신자에 의해 TextBox ()에 전달됨)

헤드 업 -이 버그는 여전히 MVC 3에 존재합니다. 저는 Razor Markup Syntax를 사용하고 있지만 (실제로 중요합니다), 매번 객체 속성에 대해 동일한 값을 생성하는 Foreach 루프와 동일한 버그를 만났습니다.

이것은 예상되는 행동입니다 -MVC는 viewstate 또는 뒷모습 뒤의 다른 기타를 사용하여 추가 정보를 양식으로 전달하지 않으므로 제출 한 양식 (양식 이름은 제출 된 데이터의 일부가 아닙니다. 이름/값 쌍의 목록).

MVC가 양식을 다시 렌더링하면 단순히 동일한 이름을 가진 제출 된 값이 존재하는지 확인하는 것입니다. 다시 한 번, 지명 된 값이 어떤 형태로 왔는지 또는 심지어 어떤 유형의 제어인지 알 수있는 방법이 없습니다 (귀하의 여부에 관계없이 라디오, 텍스트 또는 숨겨진 것을 사용하십시오. HTTP를 통해 제출 될 때 모두 이름 = 값입니다).

foreach (var s in ModelState.Keys.ToList())
                if (s.StartsWith("detalleProductos"))
                    ModelState.Remove(s);

ModelState.Remove("TimeStamp");
ModelState.Remove("OtherOfendingHiddenFieldNamePostedToSamePage1");
ModelState.Remove("OtherOfendingHiddenFieldNamePostedToSamePage2");

return View(model);

"설계 문제"를 재현하는 예 및 가능한 해결 방법.하지만 "버그"를 찾으려고 3시간을 허비한 것에 대한 해결 방법은 없습니다...이 "디자인"은 여전히 ​​ASP.NET MVC 2.0 RTM에 있습니다.

    [HttpPost]
    public ActionResult ProductEditSave(ProductModel product)
    {
        //Change product name from what was submitted by the form
        product.Name += " (user set)";

        //MVC Helpers are using, to find the value to render, these dictionnaries in this order: 
        //1) ModelState 2) ViewData 3) Value
        //This means MVC won't render values modified by this code, but the original values posted to this controller.
        //Here we simply don't want to render ModelState values.
        ModelState.Clear(); //Possible workaround which works. You loose binding errors information though...  => Instead you could replace HtmlHelpers by HTML input for the specific inputs you are modifying in this method.
        return View("ProductEditForm", product);
    }

양식에 원래 다음 내용이 포함된 경우: <%= Html.HiddenFor( m => m.ProductId ) %>

"이름"(양식이 렌더링될 때)의 원래 값이 "dummy"인 경우 양식이 제출된 후 "dummy(사용자 세트)"가 렌더링되는 것을 볼 수 있습니다.없이 ModelState.Clear() 여전히 "더미"가 표시됩니다 !!!!!!

올바른 해결 방법:

<input type="hidden" name="Name" value="<%= Html.AttributeEncode(Model.Name) %>" />

모든 mvc 양식 개발자가 이를 염두에 두어야 하기 때문에 이것이 전혀 좋은 디자인이 아니라고 생각합니다.

이 문제는 여전히 MVC 5에 존재하며 분명히 괜찮은 버그로 간주되지 않습니다.

우리는 디자인에 의해 이것이 우리에게 예상되는 행동이 아니라는 것을 알게되었습니다. 오히려 우리는 항상 숨겨진 필드의 가치가 다른 유형의 필드와 유사하게 작동하기를 원하며 특별한 취급되지 않거나 모호한 컬렉션에서 그 가치를 끌어 당기지 않기를 원합니다 (ViewState를 상기시켜줍니다!).

몇 가지 결과 (우리에게 올바른 값은 모델 값이며, 잘못된 것은 ModelSTATE 값입니다) :

  • Html.DisplayFor() 올바른 값을 표시합니다 (모델에서 가져옵니다)
  • Html.ValueFor 그렇지 않습니다 (ModelState에서 가져옵니다)
  • ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model 올바른 값을 가져옵니다

우리의 해결책은 단순히 우리 자신의 확장을 구현하는 것입니다.

        /// <summary>
        /// Custom HiddenFor that addresses the issues noted here:
        /// http://stackoverflow.com/questions/594600/possible-bug-in-asp-net-mvc-with-form-values-being-replaced
        /// We will only ever want values pulled from the model passed to the page instead of 
        /// pulling from modelstate.  
        /// Note, do not use 'ValueFor' in this method for these reasons.
        /// </summary>
        public static IHtmlString HiddenTheWayWeWantItFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
                                                    Expression<Func<TModel, TProperty>> expression,
                                                    object value = null,
                                                    bool withValidation = false)
        {
            if (value == null)
            {
                value = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model;
            }

            return new HtmlString(String.Format("<input type='hidden' id='{0}' name='{1}' value='{2}' />",
                                    htmlHelper.IdFor(expression),
                                    htmlHelper.NameFor(expression),
                                    value));
        }

이것은 '디자인 별'일 수 있지만 문서화 된 것은 아닙니다.

Public Shared Function Hidden(  

  ByVal htmlHelper As System.Web.Mvc.HtmlHelper,  
  ByVal name As String, ByVal value As Object)  
As String  

System.web.mvc.html.inputextensions 회원

요약 : 숨겨진 입력 태그를 반환합니다.

매개 변수 :
htmlhelper : HTML 도우미.
이름 : 값을 찾는 데 사용되는 양식 필드 이름 및 System.web.mvc.ViewDatAdictionary 키입니다.
값 : 숨겨진 입력의 값. NULL 인 경우 System.Web.MVC.ViewDatAdictionary 및 System.Web.mvc.ModelStatedictionary를 값을보십시오.

이것은 값 매개 변수가 NULL (또는 지정되지 않은) 일 때만 HTMLHELPER가 다른 곳에서 값을 찾을 것이라고 제안하는 것 같습니다.

내 앱에는 다음과 같은 양식이 있습니다. html.hidden ( "remote", true)<input id="remote" name="remote" type="hidden" value="False" />

참고 viewData.ModelState Dictionary에있는 값이 과도하게 발생합니다.

아니면 내가 뭔가를 놓치고 있습니까?

따라서 MVC 4에서는 "디자인 문제"가 여전히 있습니다. 컨트롤러에서하는 일에 관계없이 컬렉션에 올바른 숨겨진 값을 설정하기 위해 사용해야 할 코드는 다음과 같습니다.보기는 항상 잘못된 값을 나타 냈습니다.

오래된 코드

for (int i = 0; i < Model.MyCollection.Count; i++)
{
    @Html.HiddenFor(m => Model.MyCollection[i].Name) //It doesn't work. Ignores what I changed in the controller
}

업데이트 된 코드

for (int i = 0; i < Model.MyCollection.Count; i++)
{
    <input type="hidden" name="MyCollection[@(i)].Name" value="@Html.AttributeEncode(Model.MyCollection[i].Name)" /> // Takes the recent value changed in the controller!
}

MVC 5에서 이것을 고정 했습니까?

해결 방법이 있습니다.

    public static class HtmlExtensions
    {
        private static readonly String hiddenFomat = @"<input id=""{0}"" type=""hidden"" value=""{1}"" name=""{2}"">";
        public static MvcHtmlString HiddenEx<T>(this HtmlHelper htmlHelper, string name, T[] values)
        {
            var builder = new StringBuilder(values.Length * 100);
            for (Int32 i = 0; i < values.Length; 
                builder.AppendFormat(hiddenFomat,
                                        htmlHelper.Id(name), 
                                        values[i++].ToString(), 
                                        htmlHelper.Name(name)));
            return MvcHtmlString.Create(builder.ToString());
        }
    }

다른 사람들이 제안한 것처럼 HTMLHELPERS (TextBoxFor, CheckBoxFor, HiddenFor 등)를 사용하는 대신 직접 HTML 코드를 사용했습니다.

이 접근법의 문제는 이름과 ID 속성을 문자열로 넣어야한다는 것입니다. 모델 속성을 강하게 유지하고 싶었으므로 이름과 idfor htmlhelpers를 사용했습니다.

<input type="hidden" name="@Html.NameFor(m => m.Name)" id="@Html.IdFor(m=>m.Name)" value="@Html.AttributeEncode(Model.Name)">

업데이트:편리한 htmlhelper 확장증이 있습니다

    public static MvcHtmlString MyHiddenFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, object htmlAttributes = null)
    {
        return new MvcHtmlString(
            string.Format(
                @"<input id=""{0}"" type=""hidden"" value=""{1}"" name=""{2}"">",
                helper.IdFor(expression),
                helper.NameFor(expression),
                GetValueFor(helper, expression)
            ));
    }

    /// <summary>
    /// Retrieves value from expression
    /// </summary>
    private static string GetValueFor<TModel, TValue>(HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
    {
        object obj = expression.Compile().Invoke(helper.ViewData.Model);
        string val = string.Empty;
        if (obj != null)
            val = obj.ToString();
        return val;
    }

그런 다음처럼 사용할 수 있습니다

@Html.MyHiddenFor(m => m.Name)
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top