使用不同情况

时间:2017-06-23 11:35:07

标签: c# asp.net-mvc razor

对于有想法的人来说,有点奇怪......我在特定页面上渲染一个隐藏的布尔字段。但是,对于同一个字段,我会得到两个略有不同的标记,具体取决于特定事件是否在流程之前发生。生成的两个字段是;

<input id="HasPreviouslyIssuedCard" name="HasPreviouslyIssuedCard" type="hidden" value="false" />

<input id="HasPreviouslyIssuedCard" name="HasPreviouslyIssuedCard" type="hidden" value="False" />

问题是“值”属性中的文本的情况,您会注意到它是不同的,后来影响JS条件。产生这个的剃刀标记是;

@Html.Hidden("HasPreviouslyIssuedCard", Model.HasPreviouslyIssuedCard?.ToString(), new { id = nameof(Model.HasPreviouslyIssuedCard) })

但是,我也尝试使用以下变体,在渲染隐藏字段时具有相同的差异;

@Html.HiddenFor(m => m.HasPreviouslyIssuedCard)

我该怎么做才能获得这种差异?要获得大写变体,我在进入相关页面之前点击浏览器后退按钮。两种方法都以相同的方式加载数据,并以相同的方式将相同的值传递给渲染器。两种不同的输出。

请记住,这是一个呈现的布尔值类型字段。应该没有太大的修补空间。有很多方法可以解决这个问题,但由于我们在积压工作中有几个与布尔字段和后退按钮有关的项目,我想解释一下而不是解决它。

我最好的猜测是,按下后退按钮会以某种方式改变渲染器的状态,或者模型中的其他一些标志是不同的(因为它是70多个字段的向导)正在改变渲染器的方式解释如何设置布尔值。值相同,页面相同,数据以相同的方式读取。

基于此页面(Why does Boolean.ToString output "True" and not "true"),我们应假设始终获得大写变体,但这不是结果。

任何接受者/想法/想法?

编辑1

HiddenFor()方法中挖掘MVC的渲染逻辑,最终调用Convert.ToString(value, CultureInfo.CurrentCulture)。直接调用时,我不能让它产生一个小写的布尔值,但它显然是这样做的。我当前的文化代码设置为en-IE,但我直接调用它时会看到大写的布尔值。

编辑2

我已经对我的应用程序进行了更多的修补和跟踪,并且可以提供更详细的信息,但我还没有能够在更简单的应用程序中重现这一点

MVC 5应用程序:它有;

  1. 通过HTTP /检索到的根网域GET上的初始目标网页。布尔input标记呈现为True / False
  2. 通过HTTP /Apply检索到的网址GET的向导中的第一页。布尔input标记呈现为True / False
  3. 在步骤2中用户提交页面后,向导中的第二页在同一URL处。通过HTTP POST检索。 input代码的情况现在呈现为true / false
  4. 点击浏览器的后退按钮并点击陷阱页面(我们将浏览器历史记录设置为始终点击Back上的陷阱页面,因为它与向导一起玩得很快乐。)
  5. 用户点击陷阱页面上的按钮,将它们带回到他们中断的应用程序中。 input标签现在以大写形式返回呈现(原始报告的问题)。
  6. 我已经使用ILSpy深入研究MVC库以尝试扫描和MVC(如果我正确地读取代码)实际上使用IConverter的实现来编写布尔值,不像我原先想的那样Convert.ToString(value, CultureInfo.CurrentCulture)

    HiddenFor()的调用中追踪的代码堆栈(我认为);

    1. System.Web.Mvc.InputExtentions.HiddenFor()(公共职能)
    2. System.Web.Mvc.InputExtentions.HiddenHelper()(私有函数,这里有一些关于数组的逻辑,但在我们的情况下并不适用)
    3. System.Web.Mvc.InputExtentions.InputHelper()(私人功能,魔法发生在这里)
    4. System.Web.Mvc.InputExtentions.InputHelper();

      的反编译代码
      private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes)
      {
          string fullHtmlFieldName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
          if (string.IsNullOrEmpty(fullHtmlFieldName))
          {
              throw new ArgumentException(MvcResources.Common_NullOrEmpty, "name");
          }
          TagBuilder tagBuilder = new TagBuilder("input");
          tagBuilder.MergeAttributes<string, object>(htmlAttributes);
          tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
          tagBuilder.MergeAttribute("name", fullHtmlFieldName, true);
          string text = htmlHelper.FormatValue(value, format);
          bool flag = false;
          switch (inputType)
          {
          case InputType.CheckBox:
          {
              bool? flag2 = htmlHelper.GetModelStateValue(fullHtmlFieldName, typeof(bool)) as bool?;
              if (flag2.HasValue)
              {
                  isChecked = flag2.Value;
                  flag = true;
              }
              break;
          }
          case InputType.Hidden:
              goto IL_131;
          case InputType.Password:
              if (value != null)
              {
                  tagBuilder.MergeAttribute("value", text, isExplicitValue);
                  goto IL_16C;
              }
              goto IL_16C;
          case InputType.Radio:
              break;
          default:
              goto IL_131;
          }
          if (!flag)
          {
              string text2 = htmlHelper.GetModelStateValue(fullHtmlFieldName, typeof(string)) as string;
              if (text2 != null)
              {
                  isChecked = string.Equals(text2, text, StringComparison.Ordinal);
                  flag = true;
              }
          }
          if (!flag && useViewData)
          {
              isChecked = htmlHelper.EvalBoolean(fullHtmlFieldName);
          }
          if (isChecked)
          {
              tagBuilder.MergeAttribute("checked", "checked");
          }
          tagBuilder.MergeAttribute("value", text, isExplicitValue);
          goto IL_16C;
          IL_131:
          string text3 = (string)htmlHelper.GetModelStateValue(fullHtmlFieldName, typeof(string));
          tagBuilder.MergeAttribute("value", text3 ?? (useViewData ? htmlHelper.EvalString(fullHtmlFieldName, format) : text), isExplicitValue);
          IL_16C:
          if (setId)
          {
              tagBuilder.GenerateId(fullHtmlFieldName);
          }
          ModelState modelState;
          if (htmlHelper.ViewData.ModelState.TryGetValue(fullHtmlFieldName, out modelState) && modelState.Errors.Count > 0)
          {
              tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
          }
          tagBuilder.MergeAttributes<string, object>(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));
          if (inputType == InputType.CheckBox)
          {
              StringBuilder stringBuilder = new StringBuilder();
              stringBuilder.Append(tagBuilder.ToString(TagRenderMode.SelfClosing));
              TagBuilder tagBuilder2 = new TagBuilder("input");
              tagBuilder2.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden));
              tagBuilder2.MergeAttribute("name", fullHtmlFieldName);
              tagBuilder2.MergeAttribute("value", "false");
              stringBuilder.Append(tagBuilder2.ToString(TagRenderMode.SelfClosing));
              return MvcHtmlString.Create(stringBuilder.ToString());
          }
          return tagBuilder.ToMvcHtmlString(TagRenderMode.SelfClosing);
      }
      

      编辑3

      只是重述,因为有一些关于javascript方面的评论。我在尝试诊断问题时就考虑过这个问题。为了排除任何潜在的JS干扰/操纵,我使用Fiddler来捕获传输中的HTML。 MVC正在生成的HTML正在改变案例 - 我可以在Fiddler中看到这一点,在JS甚至加载之前的某个时刻,从不介意运行。这不是JS问题。

2 个答案:

答案 0 :(得分:3)

似乎我错了,毕竟 JS,虽然不是我试图排除它的时候。重新产生这一事件的事件顺序;

  1. 使用布尔隐藏字段+非提交按钮
  2. 创建HTML form
  3. 在按钮的onclick事件中,有一段JS将布尔字段设置为true(小写)
  4. 在控制器的POST操作中,渲染相同的视图
  5. 在POST之后,布尔字段将具有小写值而不是大写。

    发生了什么?好吧,如果HiddenFor()中存在密钥,ViewData.ModelState(及其变体)将呈现ToString()的布尔值,而不是模型属性的ModelState值采集。看起来很直观,但是可以(并且对我而言)抛出的是所有其他数据类型,模型绑定器非常具体 - ModelState值和模型的属性值将是相同的。除了布尔值的情况 - 模型绑定器足够聪明,可以在转换POST时将Truetrue视为相同,但这会留下ModelState值和字符串属性值如果它是通过JS布尔设置的那么重要。 JS会将ModelState值设置为true,而模型属性的ToString()值则为True。在JS中,"true" !== "True"

    至于为什么在点击浏览器的后退按钮后重置为我 - 我们所拥有的陷阱页面没有表格值,并通过HTTP GET链接回应用程序,导致{{1调用模型属性HiddenFor而不是将其从.ToString()中拉出来,因为它不在那个时间点。同样地,在我的测试中,用户在通过JS设置之后很久就会处于向导中的某个位置,因此在它们继续通过向导时它将保持大写。

    我的假设是JS在页面加载后开始使用。这实际上是由于JS在开始POST之前设置了一个值,并且小写值在页面生命周期中通过ModelState持续存在。去图。

    修改

    要重现的代码;

    模型;

    ModelState

    Razor标记;

    public class Test
    {
        public bool Sample { get; set; }
    }
    

    控制器;

    @model TestModelValueCase.Models.Test
    @{
        ViewBag.Title = "Test Page";
    }
    
    @using (Html.BeginForm())
    {
        @Html.HiddenFor(m => m.Sample)
        <div>
            <label>Hidden Value:</label>
            <span id="_uiValue"></span>
        </div>
        <button type="submit">Try Post</button>
    }
    @section scripts
    {
        <script type="text/javascript" language="javascript">
            $(document).ready(function() {
                var source = $('#@nameof(Model.Sample)');
                $('#_uiValue').html(source.val());
                source.val(true);
            });
        </script>
    }
    

答案 1 :(得分:0)

来自控制器的boolean comming的情况非常棘手,因为大部分时间被解释为字符串

@Hidden 似乎就是这种情况。

对我来说,实现它的更简单方法是创建一个复选框并将显示设置为“none”

@html.CheckBoxFor(m=> m.HasPreviouslyIssuedCard,  new { style ="display:none"}))

使用此解决方案,您将在两种语言(C#和JavaScript)中使用更多类型的变量。