development

Asp.net MVC ModelState.Clear

big-blog 2020. 7. 24. 07:15
반응형

Asp.net MVC ModelState.Clear


누구든지 Asp.net MVC에서 ModelState의 역할에 대한 간결한 정의를 줄 수 있습니까? 특히 어떤 상황에서 전화가 필요하거나 바람직한 지 알아야합니다 ModelState.Clear().

조금 열었습니다 . 죄송합니다. 제가하던 일을 말하면 도움이 될 것 같습니다.

"Page"라는 컨트롤러에서 편집 작업이 있습니다. 페이지의 세부 정보를 변경하는 양식을 처음 볼 때 모든 것이 제대로로드됩니다 ( "MyCmsPage"개체에 바인딩). 그런 다음 MyCmsPage 개체의 필드 중 하나에 대한 값을 생성하는 단추 ( MyCmsPage.SeoTitle)를 클릭합니다. 그것은 잘 생성하고 객체를 업데이트 한 다음 새로 수정 된 페이지 객체로 작업 결과를 반환하고 관련 텍스트 상자 (를 사용하여 렌더링 <%= Html.TextBox("seoTitle", page.SeoTitle)%>)가 업데이트 될 것으로 예상 하지만 ... 아직로드 된 이전 모델의 값을 표시합니다.

나는 그것을 사용하여 문제를 해결 ModelState.Clear()했지만 왜 / 어떻게 작동했는지 알아야하므로 맹목적으로하지 않습니다.

PageController :

[AcceptVerbs("POST")]
public ActionResult Edit(MyCmsPage page, string submitButton)
{
    // add the seoTitle to the current page object
    page.GenerateSeoTitle();

    // why must I do this?
    ModelState.Clear();

    // return the modified page object
     return View(page);
 }

Aspx :

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyCmsPage>" %>
....
        <div class="c">
            <label for="seoTitle">
                Seo Title</label>
            <%= Html.TextBox("seoTitle", page.SeoTitle)%>
            <input type="submit" value="Generate Seo Title" name="submitButton" />
        </div>

MVC의 버그라고 생각합니다. 나는 오늘 몇 시간 동안이 문제로 어려움을 겪었습니다.

이것을 감안할 때 :

public ViewResult SomeAction(SomeModel model) 
{
    model.SomeString = "some value";
    return View(model); 
}

뷰는 변경 사항을 무시하고 원래 모델로 렌더링됩니다. 그래서 같은 모델을 사용하는 것이 마음에 들지 않을 것이라고 생각했습니다.

public ViewResult SomeAction(SomeModel model) 
{
    var newModel = new SomeModel { SomeString = "some value" };
    return View(newModel); 
}

그리고 여전히 뷰는 원래 모델로 렌더링됩니다. 이상한 점은 뷰에 중단 점을 넣고 모델을 검사하면 값이 변경된다는 것입니다. 그러나 응답 스트림에는 이전 값이 있습니다.

결국 나는 당신이했던 것과 같은 일을 발견했습니다.

public ViewResult SomeAction(SomeModel model) 
{
    var newModel = new SomeModel { SomeString = "some value" };
    ModelState.Clear();
    return View(newModel); 
}

예상대로 작동합니다.

나는 이것이 "기능"이라고 생각하지 않습니까?


최신 정보:

  • 이것은 버그가 아닙니다.
  • View()POST 작업에서 돌아 오는 것을 중지하십시오 . 대신 PRG사용 하고 작업이 성공하면 GET으로 리디렉션 하십시오 .
  • 당신이하면 된다 을 반환 View()후 조치에서 양식 유효성 검사를 수행하고 그에게 길을 어떻게 MVC가 설계 헬퍼 내장 사용합니다. 이 방법으로 사용하면 사용할 필요가 없습니다..Clear()
  • 이 조치를 사용하여 SPA에 대한 아약스를 리턴 하는 경우 웹 API 컨트롤러를 ModelState사용하고 어쨌든 사용하지 않아야하므로 잊어 버리 십시오.

이전 답변 :

MVC의 ModelState는 주로 해당 객체가 유효한지 여부와 관련하여 모델 객체의 상태를 설명하는 데 사용됩니다. 이 튜토리얼 은 많은 설명이 필요합니다.

일반적으로 MVC 엔진에서 유지 관리하는 ModelState를 지우지 않아도됩니다. 수동으로 지우면 MVC 유효성 검사 모범 사례를 준수하려고 할 때 원하지 않는 결과가 발생할 수 있습니다.

제목의 기본값을 설정하려는 것 같습니다. 이 작업은 모델 객체가 인스턴스화 될 때 (어딘가 또는 객체 자체의 매개 변수가없는 ctor) get 조치에서 페이지로 처음으로 돌아가거나 완전히 클라이언트에서 (아약스 등을 통해) 수행되어야합니다. 마치 사용자가 입력 한 것처럼 보이고 게시 된 양식 컬렉션과 함께 다시 나타납니다. 양식 콜렉션을 수신 할 때 (POST 조치 // 편집에서)이 값을 추가하는 방법으로이 기괴한 동작이 발생 하여 작동 하는 .Clear() 것처럼 보일 수 있습니다. 날 믿어-당신은 명확한 것을 사용하고 싶지 않습니다. 다른 아이디어 중 하나를 시도하십시오.


개별 필드의 값을 지우려면 다음 기술이 유용하다는 것을 알았습니다.

ModelState.SetModelValue("Key", new ValueProviderResult(null, string.Empty, CultureInfo.InvariantCulture));

참고 : "키"를 재설정하려는 필드 이름으로 변경하십시오.


ModelState는 기본적으로 유효성 검사 측면에서 모델의 현재 상태를 유지합니다.

ModelErrorCollection : 모델이 값을 바인딩하려고 할 때 오류를 나타냅니다. 전의.

TryUpdateModel();
UpdateModel();

또는 ActionResult의 매개 변수처럼

public ActionResult Create(Person person)

ValueProviderResult : 모델에 대한 시도 된 바인드에 대한 세부 사항을 보유하십시오. 전의. AttemptedValue, Culture, RawValue .

Clear () 메소드는 신중하지 않은 결과를 초래할 수 있으므로주의해서 사용해야합니다. 그리고 AttemptedValue와 같은 ModelState의 멋진 속성을 잃어 버릴 것입니다. 이는 MVC가 백그라운드에서 사용하여 오류가 발생하면 양식 값을 다시 채 웁니다.

ModelState["a"].Value.AttemptedValue

중단 된 양식의 모델을 업데이트하고 성능 분석을 위해 '작업으로 리디렉션'을 원하지 않는 인스턴스가 있습니다. 숨겨진 필드의 이전 값이 업데이트 된 모델에서 유지되어 모든 종류의 문제가 발생했습니다!.

몇 줄의 코드가 곧 ModelState 내에서 제거하려는 (유효성 검사 후) 요소를 식별하여 새로운 값이 다음 형식으로 사용되었습니다.

while (ModelState.FirstOrDefault(ms => ms.Key.ToString().StartsWith("SearchResult")).Value != null)
{
    ModelState.Remove(ModelState.FirstOrDefault(ms => ms.Key.ToString().StartsWith("SearchResult")));
}

우리 중 많은 사람들이 이것에 물린 것처럼 보이며, 이것이 일어나는 이유는 이해가되지만 ModelState가 아닌 Model의 값이 표시되도록하는 방법이 필요했습니다.

일부는을 제안 ModelState.Remove(string key)했지만 key특히 중첩 모델의 경우 무엇인지 명확하지 않습니다 . 여기에 도움이되는 몇 가지 방법이 있습니다.

RemoveStateFor메소드는 ModelStateDictionary, 모델 및 원하는 특성에 대한 표현식을 가져 와서 제거합니다. HiddenForModelView에서 ModelState 항목을 먼저 제거하여 Model의 값만 사용하여 숨겨진 입력 필드를 만드는 데 사용할 수 있습니다. (이것은 다른 헬퍼 확장 메소드를 위해 쉽게 확장 될 수 있습니다).

/// <summary>
/// Returns a hidden input field for the specified property. The corresponding value will first be removed from
/// the ModelState to ensure that the current Model value is shown.
/// </summary>
public static MvcHtmlString HiddenForModel<TModel, TProperty>(this HtmlHelper<TModel> helper,
    Expression<Func<TModel, TProperty>> expression)
{
    RemoveStateFor(helper.ViewData.ModelState, helper.ViewData.Model, expression);
    return helper.HiddenFor(expression);
}

/// <summary>
/// Removes the ModelState entry corresponding to the specified property on the model. 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 ModelStateDictionary modelState, TModel model,
    Expression<Func<TModel, TProperty>> expression)
{
    var key = ExpressionHelper.GetExpressionText(expression);

    modelState.Remove(key);
}

다음과 같이 컨트롤러에서 호출하십시오.

ModelState.RemoveStateFor(model, m => m.MySubProperty.MySubValue);

또는 다음과 같은 관점에서 :

@Html.HiddenForModel(m => m.MySubProperty.MySubValue)

System.Web.Mvc.ExpressionHelperModelState 속성의 이름을 얻는 데 사용 됩니다.


유효성이 충분하지 않으면 값을 업데이트하거나 재설정하고 싶었고이 문제가 발생했습니다.

쉬운 대답은 ModelState.Remove입니다. 문제가 있습니다. 헬퍼를 사용하는 경우 이름 지정 규칙을 따르지 않는 한 실제로 이름을 알 수 없기 때문입니다. 아마도 사용자 지정 도우미와 컨트롤러 가 모두 이름을 얻는 데 사용할 수 있는 함수를 만들지 않는 한 .

이 기능은 도우미에서 옵션으로 구현되어야합니다. 기본적 으로이 작업을 수행 하지 않지만 허용되지 않는 입력을 다시 표시하려면 그렇게 말할 수 있습니다.

그러나 적어도 나는 지금 이슈를 이해한다;).


결국 알았습니다. 등록되지 않은 내 Custom ModelBinder가 다음을 수행합니다.

var mymsPage = new MyCmsPage();

NameValueCollection frm = controllerContext.HttpContext.Request.Form;

myCmsPage.SeoTitle = (!String.IsNullOrEmpty(frm["seoTitle"])) ? frm["seoTitle"] : null;

따라서 기본 모델 바인딩에서 문제가 발생했을 수 있습니다. 확실하지 않지만 사용자 정의 모델 바인더가 등록되면 문제가 해결됩니다.


일반적으로 프레임 워크 표준 관행에 맞서 싸우는 경우 접근 방식을 재고해야합니다. 이 경우 ModelState의 동작입니다. 예를 들어 POST 후 모델 상태를 원하지 않으면 get으로의 리디렉션을 고려하십시오.

[HttpPost]
public ActionResult Edit(MyCmsPage page, string submitButton)
{
    if (ModelState.IsValid) {
        SomeRepository.SaveChanges(page);
        return RedirectToAction("GenerateSeoTitle",new { page.Id });
    }
    return View(page);
}

public ActionResult GenerateSeoTitle(int id) {
     var page = SomeRepository.Find(id);
     page.GenerateSeoTitle();
     return View("Edit",page);
}

문화 의견에 대한 답변 편집 :

다음은 다문화 MVC 응용 프로그램을 처리하는 데 사용하는 것입니다. 먼저 경로 핸들러 서브 클래스 :

public class SingleCultureMvcRouteHandler : MvcRouteHandler {
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        var culture = requestContext.RouteData.Values["culture"].ToString();
        if (string.IsNullOrWhiteSpace(culture))
        {
            culture = "en";
        }
        var ci = new CultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = ci;
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
        return base.GetHttpHandler(requestContext);
    }
}

public class MultiCultureMvcRouteHandler : MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        var culture = requestContext.RouteData.Values["culture"].ToString();
        if (string.IsNullOrWhiteSpace(culture))
        {
            culture = "en";
        }
        var ci = new CultureInfo(culture);
        Thread.CurrentThread.CurrentUICulture = ci;
        Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name);
        return base.GetHttpHandler(requestContext);
    }
}

public class CultureConstraint : IRouteConstraint
{
    private string[] _values;
    public CultureConstraint(params string[] values)
    {
        this._values = values;
    }

    public bool Match(HttpContextBase httpContext,Route route,string parameterName,
                        RouteValueDictionary values, RouteDirection routeDirection)
    {

        // Get the value called "parameterName" from the 
        // RouteValueDictionary called "value"
        string value = values[parameterName].ToString();
        // Return true is the list of allowed values contains 
        // this value.
        return _values.Contains(value);

    }

}

public enum Culture
{
    es = 2,
    en = 1
}

그리고 여기에 경로를 연결하는 방법이 있습니다. 경로를 만든 후 하위 에이전트 (example.com/subagent1, example.com/subagent2 등)에 문화권 코드를 추가합니다. 배양이 필요한 경우 라우트 핸들러 및 라우트에서 서브 에이전트를 제거하십시오.

    public static void RegisterRoutes(RouteCollection routes)
    {

        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.IgnoreRoute("Content/{*pathInfo}");
        routes.IgnoreRoute("Cache/{*pathInfo}");
        routes.IgnoreRoute("Scripts/{pathInfo}.js");
        routes.IgnoreRoute("favicon.ico");
        routes.IgnoreRoute("apple-touch-icon.png");
        routes.IgnoreRoute("apple-touch-icon-precomposed.png");

        /* Dynamically generated robots.txt */
        routes.MapRoute(
            "Robots.txt", "robots.txt",
            new { controller = "Robots", action = "Index", id = UrlParameter.Optional }
        );

        routes.MapRoute(
             "Sitemap", // Route name
             "{subagent}/sitemap.xml", // URL with parameters
             new { subagent = "aq", controller = "Default", action = "Sitemap"},  new[] { "aq3.Controllers" } // Parameter defaults
        );

        routes.MapRoute(
             "Rss Feed", // Route name
             "{subagent}/rss", // URL with parameters
             new { subagent = "aq", controller = "Default", action = "RSS"},  new[] { "aq3.Controllers" } // Parameter defaults
        );

        /* remap wordpress tags to mvc blog posts */
        routes.MapRoute(
            "Tag", "tag/{title}",
            new { subagent = "aq", controller = "Default", action = "ThreeOhOne", id = UrlParameter.Optional},  new[] { "aq3.Controllers" }
        ).RouteHandler = new MultiCultureMvcRouteHandler(); ;

        routes.MapRoute(
            "Custom Errors", "Error/{*errorType}",
            new { controller = "Error", action = "Index", id = UrlParameter.Optional},  new[] { "aq3.Controllers" }
        );

        /* dynamic images not loaded from content folder */
        routes.MapRoute(
            "Stock Images",
            "{subagent}/Images/{*filename}",
            new { subagent = "aq", controller = "Image", action = "Show", id = UrlParameter.Optional, culture = "en"},  new[] { "aq3.Controllers" }
        );

        /* localized routes follow */
        routes.MapRoute(
            "Localized Images",
            "Images/{*filename}",
            new { subagent = "aq", controller = "Image", action = "Show", id = UrlParameter.Optional},  new[] { "aq3.Controllers" }
        ).RouteHandler = new MultiCultureMvcRouteHandler();

        routes.MapRoute(
            "Blog Posts",
            "Blog/{*postname}",
            new { subagent = "aq", controller = "Blog", action = "Index", id = UrlParameter.Optional},  new[] { "aq3.Controllers" }
        ).RouteHandler = new MultiCultureMvcRouteHandler();

        routes.MapRoute(
            "Office Posts",
            "Office/{*address}",
            new { subagent = "aq", controller = "Offices", action = "Address", id = UrlParameter.Optional }, new[] { "aq3.Controllers" }
        ).RouteHandler = new MultiCultureMvcRouteHandler();

        routes.MapRoute(
             "Default", // Route name
             "{controller}/{action}/{id}", // URL with parameters
             new { subagent = "aq", controller = "Home", action = "Index", id = UrlParameter.Optional }, new[] { "aq3.Controllers" } // Parameter defaults
        ).RouteHandler = new MultiCultureMvcRouteHandler();

        foreach (System.Web.Routing.Route r in routes)
        {
            if (r.RouteHandler is MultiCultureMvcRouteHandler)
            {
                r.Url = "{subagent}/{culture}/" + r.Url;
                //Adding default culture 
                if (r.Defaults == null)
                {
                    r.Defaults = new RouteValueDictionary();
                }
                r.Defaults.Add("culture", Culture.en.ToString());

                //Adding constraint for culture param
                if (r.Constraints == null)
                {
                    r.Constraints = new RouteValueDictionary();
                }
                r.Constraints.Add("culture", new CultureConstraint(Culture.en.ToString(), Culture.es.ToString()));
            }
        }

    }

글쎄, 이것은 내 면도기 페이지에서 작동하는 것처럼 보였으며 .cs 파일로의 왕복 여행조차하지 않았습니다. 이것은 오래된 HTML 방식입니다. 유용 할 수 있습니다.

<input type="reset" value="Reset">

참고URL : https://stackoverflow.com/questions/1775170/asp-net-mvc-modelstate-clear

반응형