Подтвердить что ты не робот

Asp.net MVC ModelState.Clear

Может ли кто-нибудь дать мне краткое определение роли ModelState в MVC Asp.net(или ссылку на один). В частности, мне нужно знать, в каких ситуациях необходимо или желательно называть ModelState.Clear().

Бит с открытым концом huh... извините, я думаю, что это может помочь, если вы скажете, что я делаю:

У меня есть действие редактирования на контроллере под названием "Страница". Когда я впервые вижу форму для изменения данных страницы, все загружается отлично (привязка к объекту "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>
4b9b3361

Ответ 1

Я думаю, что это ошибка в 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); 
}

Работает так, как ожидалось.

Я не думаю, что это "функция", не так ли?

Ответ 2

Update:

  • Это не ошибка.
  • Пожалуйста, прекратите возвращать View() из действия POST. Используйте PRG вместо этого и перенаправляйте GET, если действие выполнено успешно.
  • Если вы возвращаете View() из действия POST, сделайте это для проверки формы и сделайте так, как MVC создан, используя встроенные помощники. Если вы сделаете это так, вам не нужно будет использовать .Clear()
  • Если вы используете это действие для возврата ajax для SPA, используйте веб-контроллер api и забудьте о ModelState, так как вы должны 't использовать его в любом случае.

Старый ответ:

ModelState в MVC используется, прежде всего, для описания состояния модельного объекта в значительной степени в зависимости от того, действителен ли этот объект или нет. Этот учебник должен многое объяснить.

Как правило, вам не нужно очищать ModelState, поскольку он поддерживается двигателем MVC для вас. Очистка вручную может привести к нежелательным результатам при попытке придерживаться лучших методов проверки MVC.

Кажется, вы пытаетесь установить значение по умолчанию для заголовка. Это должно быть сделано, когда экземпляр объекта модели (домен где-то или в самом объекте - без параметров ctor), на действие get, так что он переходит на страницу в первый раз или полностью на клиенте (через ajax или что-то еще) так что кажется, что пользователь ввел его, и он возвращается с опубликованной коллекцией форм. Некоторые, как ваш подход добавления этого значения при получении коллекции форм (в действии POST//Edit) вызывает это странное поведение, которое может привести к появлению .Clear() для вас. Поверьте мне - вы не хотите использовать ясность. Попробуйте одну из других идей.

Ответ 3

Если вы хотите очистить значение для отдельного поля, я нашел следующий способ полезным.

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

Примечание: Измените "Ключ" на имя поля, которое вы хотите reset.

Ответ 4

Ну ModelState в основном поддерживает текущее состояние модели с точки зрения проверки, она содержит

ModelErrorCollection: Представьте ошибки, когда модель попытается связать значения. ех.

TryUpdateModel();
UpdateModel();

или как параметр в ActionResult

public ActionResult Create(Person person)

ValueProviderResult. Сохраните сведения о попытке привязки к модели. ех. ПопыткаValue, культура, RawValue.

Метод Clear() следует использовать с осторожностью, поскольку он может привести к непредсказуемым результатам. И вы потеряете некоторые приятные свойства ModelState, такие как AttemptedValue, это используется MVC в фоновом режиме для повторного заполнения значений формы в случае ошибки.

ModelState["a"].Value.AttemptedValue

Ответ 5

У меня был экземпляр, в котором я хотел обновить модель суммируемой формы и не хотел "перенаправлять на действие" для аргумента useanace. Предыдущие значения скрытых полей сохранялись на моей обновленной модели, что вызывало проблемы.!

Несколько строк кода вскоре идентифицировали элементы в ModelState, которые я хотел удалить (после проверки), поэтому новые значения использовались в форме: -

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

Ответ 6

Ну, многие из нас, похоже, были укушены этим, и хотя причина, по которой это происходит, имеет смысл, мне нужен был способ убедиться, что значение на моей модели было показано, а не ModelState.

Некоторые предложили ModelState.Remove(string key), но не очевидно, что key должно быть, особенно для вложенных моделей. Вот несколько методов, которые я придумал, чтобы помочь с этим.

Метод RemoveStateFor примет ModelStateDictionary, модель и выражение для требуемого свойства и удалит его. HiddenForModel может использоваться в вашем представлении для создания скрытого поля ввода, используя только значение из Модели, сначала удалив его запись ModelState. (Это можно легко развернуть для других методов вспомогательного расширения).

/// <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.ExpressionHelper для получения имени свойства ModelState.

Ответ 7

Я хотел обновить или reset значение, если оно не полностью подтвердилось и столкнулось с этой проблемой.

Легкий ответ ModelState.Remove является.. проблематичным.. потому что, если вы используете помощники, вы действительно не знаете имя (если вы не придерживаетесь соглашения об именах). Если, возможно, вы не создадите функцию, которую могут использовать как пользовательский помощник, так и ваш контроллер, чтобы получить имя.

Эта функция должна была быть реализована как опция в помощнике, где по умолчанию это не так, но если вы хотите, чтобы неприемлемый вход повторно отображался, вы могли бы просто так сказать.

Но, по крайней мере, я сейчас понимаю проблему;).

Ответ 8

Получил это в конце. Мой пользовательский ModelBinder, который не регистрировался и делает это:

var mymsPage = new MyCmsPage();

NameValueCollection frm = controllerContext.HttpContext.Request.Form;

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

Итак, что-то, что делает привязка модели по умолчанию, должно было вызвать проблему. Не уверен, что, но моя проблема, по крайней мере, исправлена, когда регистрируется мое настраиваемое связующее устройство.

Ответ 9

Как правило, когда вы оказываетесь в борьбе с стандартными стандартами, пришло время пересмотреть ваш подход. В этом случае поведение 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);
}

EDITED ответит на комментарий культуры:

Вот что я использую для обработки многокультурного приложения 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()));
            }
        }

    }

Ответ 10

Что ж, похоже, это сработало на моей странице Razor и даже не пошло туда и обратно в файл .cs. Это старый способ HTML. Это может быть полезно.

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