Сериализация .NET объекта в JavaScript variable на HTML странице внутрь Script-блока

в 17:59, , рубрики: .net, ASP.NET, asp.net mvc 4, Веб-разработка, метки: ,

Старые добрые hidden inputs

Часто приходится передавать в HTML страницу данные, которые необходимо потом использовать из JavaScript. Издавна для этого используется самый простой способ: hidden inputs. То есть, если нам нужно передать Uri адрес веб сервиса, мы на странице рендерим что-то вроде

  <input type="hidden" name="webServiceUri" value="URI we need"/>

и можем при помощи jQuery или plain old JavaScript найти этот input по имени и прочитать переданное значение.

Но когда нужно передавать много параметров или даже массивы, то этот способ становится не удобным. Можно конечно сделать REST сервис, который отдает все данные по AJAX-запросу со страницы (сессионные данные), но в большенстве случаев это излишне.

В настоящее время часто используется другой способ — на стороне сервера отрендерить в HTML все необходимые данные в виде JavaScript переменной внутри Script-блока.

Рассмотрим как это сделать в ASP.NET MVC.


Сценарий использования будет таким:

  • у нас есть .NET класс который инкапсулирует в себе требуемые настройки и мы можем использовать любой класс для этого
  • у нас есть Extension метод для MVC HtmlHelper, который принимает имя JavaScript переменной и объект, который нужно сериализовать
  • сериализуемый класс передается во View внутри ViewModel или ViewBag
  • имя JavaScript переменной может передаватся во View из контроллера или хард-кодится во View

После рендеринга страницы мы получаем код аналогичный следующему:

<script type="text/javascript">
   var settings = {"WebServiceUri":"URI we need","UserId":"1212"};
</script>

Extension метод для MVC HtmlHelper получился таким:

  /// <summary>
  /// This class provides the extension methods to ASP.NET MVC HtmlHelper.
  /// </summary>
  public static class HtmlHelperExtensions
  {
    /// <summary>
    /// This method renders the given model class as JavaScript variable within Html Script tag.
    /// </summary>
    /// <param name="htmlHelper">HtmlHelper instance we extend by this method.</param>
    /// <param name="variableName">The name of JavaScript variable the rendered Json object will be assigned to.</param>
    /// <param name="model">The model class to be rendered to Json and assigned to the variable.</param>
    /// <returns>Html representation of Html Script tag.</returns>
    public static MvcHtmlString RenderJsToScriptBlock(this HtmlHelper htmlHelper, string variableName, object model)
    {
      #region Pre-conditions
      if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentNullException("variableName"); }
      #endregion Pre-conditions

      if (null == model) { return MvcHtmlString.Empty; }

      TagBuilder tagBuilder = new TagBuilder(@"script");
      tagBuilder.MergeAttribute(@"type", @"text/javascript");

      // use InnerHtml because it doesn't encode characters
      tagBuilder.InnerHtml = string.Format("var {0} = {1};", variableName, htmlHelper.Raw(Json.Encode(model)));

      return new MvcHtmlString(tagBuilder.ToString());
    }
  }

Немного Unit-тестов с использованием moq фреймворка и MSTest:

    [TestMethod, TestCategory("Unit")]
    [ExpectedException(typeof(ArgumentNullException))]
    public void Should_Not_Accept_Null_VariableName()
    {
      HtmlHelper helper = new HtmlHelper(new Mock<ViewContext>().Object, new Mock<IViewDataContainer>().Object);
      helper.RenderJsToScriptBlock(null, null);
    }

    [TestMethod, TestCategory("Unit")]
    [ExpectedException(typeof(ArgumentNullException))]
    public void Should_Not_Accept_Empty_VariableName()
    {
      HtmlHelper helper = new HtmlHelper(new Mock<ViewContext>().Object, new Mock<IViewDataContainer>().Object);
      helper.RenderJsToScriptBlock(string.Empty, null);
    }

    [TestMethod, TestCategory("Unit")]
    public void Should_Return_Empty_MvcHtmlString_For_NullModel()
    {
      HtmlHelper helper = new HtmlHelper(new Mock<ViewContext>().Object, new Mock<IViewDataContainer>().Object);
      MvcHtmlString mvcHtmlString = helper.RenderJsToScriptBlock("variableName", null);
      Assert.AreEqual(mvcHtmlString, MvcHtmlString.Empty);
    }

    [TestMethod, TestCategory("Unit")]
    public void Should_Return_Correct_Result_For_Correct_Input()
    {
      const string expectedHtmlString = "<script type="text/javascript">var variableName = {"UserName":"Alex"};</script>";

      HtmlHelper helper = new HtmlHelper(new Mock<ViewContext>().Object, new Mock<IViewDataContainer>().Object);
      var model = new { UserName = "Alex" };
      MvcHtmlString mvcHtmlString = helper.RenderJsToScriptBlock("variableName", model);

      Assert.AreEqual(mvcHtmlString.ToHtmlString(), expectedHtmlString);
    }

Автор: shirmanov

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js