2018 年 9 月

33 卷,第 9

此文章由机器翻译

Web 开发-使用 Blazor 浏览器中的 C#

通过Jonathan Miller

Blazor 是新的 Microsoft 实验性框架,它通过将 C# 到而无需插件的任何浏览器。它包含现代单页面应用程序,与使用 C# 和它 vast 基类的库中的功能结合使用的承诺。Blazor 将 C# 开发到一个新级别。它是使语言的完整堆栈开发工具所需的最后一个部分。它将具有常见的 JavaScript 框架,但根据熟悉的语言、 Api 和工具的 Microsoft.NET framework 的所有功能。

获得 Blazor 加快如果来自传统的 Microsoft 背景,熟悉 ASP.NET Web 窗体或模型-视图-控制器 (MVC) 时,非常容易,尤其是在与 mountain 相比 Microsoft 开发人员必须攀升获得如 Angular 或 React JavaScript 框架中的等效知识。

请务必了解 Blazor 完全运行在浏览器内。Blazor 应用编译后,它是实质上是一组文件,以便获取已加载到浏览器中运行。与传统的 ASP.NET 应用程序是无需任何特殊的后端为其提供服务。Blazor 站点可由任何平台上的任何 Web 服务器提供服务。客户端,支持 WebAssembly 标准的任何浏览器支持 Blazor。这包括立即传送的所有主要浏览器。

如果运行在浏览器中,应用程序并不真正是特别有用,而无需访问外部数据和服务。就像标准 JavaScript 单页面应用程序一样 Blazor 应用访问 Web 服务使用的 HTTP REST Api。这些 Api 可以使用 Microsoft 工具,如 Web API 或任何技术,它可以提供 HTTP REST 终结点创建。在本文中,我将演示 Blazor 应用能够在 Web 上调用免费提供的 Web 服务。

如何设置

第一步获取 Blazor 安装的当前版本。在撰写本文时,Blazor 仍是实验性框架不受支持。这意味着您不应在生产环境中使用它。Blazor 安装非常低影响,但可能不希望在日常工作计算机上安装它。  请考虑在另一台计算机或虚拟环境上安装它。主要要求是使用 ASP.NET 和 Web 开发工作负载安装、.NET Core SDK 和 Blazor 语言扩展的 Visual Studio 2017。请查看 blazor.net 上的"入门"步骤。Blazor 团队提前相当快速,有时,您需要特定版本的 Visual Studio 或.NET Core 以使用当前的实验性版本。

创建新的 Blazor 应用

我将首先创建示例 Blazor 应用程序,然后修改它以调用一些 Web 服务。首先,让我们在 Visual Studio 中创建新的 ASP.NET Core Web 应用程序。

接下来,选择一个 Blazor 应用程序,并单击确定。如果您看不到 Blazor 的选择列表中,您可能缺少 Blazor 语言服务扩展。

创建的默认 Blazor 应用程序是一个空 canvas 远。它包括基本的网站,它利用 Bootstrap。有几个示例页面,使其轻松启动并正在运行并与 Blazor 中立即开始试用。提取数据选项卡显示嵌入到默认应用程序的 JSON 文件中的一些虚拟天气数据。

这是如何甚至实现?

在浏览器中运行 C# 的概念自.NET Silverlight 推出以来已梦想。它的效果非常好的业务线应用程序,但这一事实,这是必需的插件和新兴的 iOS 型号不允许浏览器插件限制很严格 Silverlight 的未来。

使此所有可能的神奇之处是调用 WebAssembly (WASM) 的新标准。WASM 是可以加载的二进制格式直接在浏览器中运行,目前支持所有主流浏览器。  Mono 团队正在努力努力在 WASM 中运行的.NET 运行时版本。Blazor 本身是一个框架,为 WASM 生成最重要的是 Mono 运行时。编译项目时,则将其编译为获取加载和执行由公共语言运行时 (CLR) 在浏览器内运行的.NET 程序集。请务必了解在浏览器中运行的.NET 代码相同的 JavaScript 安全沙箱中运行。在本期中有关此主题的详细信息,请参阅 Dino Esposito 领先列。

获取实际的天气预报

预测是从项目中嵌入的文件加载的虚设数据。我将在整个提取数据页替换为实际 Web 服务中的实际数据。首先,我需要替换一些简单的提示用户输入邮政编码的 HTML。在 FetchData.cshtml,我的 HTML 代码将替换为以下:

<h1>Weather Forecast</h1>
<div class="input-group col-md-3">
  <input type="text" class="form-control" placeholder="Zip code"
    bind="@zip" maxlength="5" />
  <div class="input-group-append">
    <button class="btn btn-secondary" type="button"
      onclick="@GetWeather">Get Weather</button>
  </div>
</div>
<br /><span style="color:red">@errorMessage</span>

请注意,Razor 语法嵌入到脚本中。@ 符号信号代码和变量。输入的标记捕获 ZIP 代码,并将其绑定到一个名为 zip 变量中。按钮标记具有绑定到 @GetWeather,C\# (而不是 JavaScript) 中调用 GetWeather 方法其 onclick 方法。此外,还有一些 @errorMessage,如果用户输入无效 ZIP,可以使用。同一 FetchData.cshtml @functions 块中定义了这些变量和方法:

@functions {
  String zip = String.Empty;
  String errorMessage = String.Empty;
  private async Task GetWeather()
  {
  }
}

运行应用程序现在使用户能够输入邮政编码,然后单击获取天气信息按钮。GetWeather 方法为空,因此没有任何反应。在下一步部分中,我将添加代码,将调用 Web 服务并检索当前天气状况。

存储从 Web 服务返回的数据

接下来,我在我的页面以存储从 Web 服务调用返回的数据需要一些本地变量。Razor 页面将绑定到这些变量,以便可以显示数据。这些变量添加到 @functions 块在页中,如下所示:

Models.CurrentConditions currentcondition;
Models.Alert alerts;
Models.ZipLookup ziplookup;
String imgurl = "";

添加邮政编码 Web 服务

一旦用户单击获取天气信息按钮时,必须验证邮政编码的输入框中。第一个公共 Web 服务是从 zippopotam.us。Http://api.zippopotam.us/US/<zip> 调用 API URL 时,它返回有关指定 ZIP 代码的信息。来自此 Web 服务所需的信息是城市和州的名称。将在预测中,向用户显示此数据并将在后续的 Web 服务调用中使用的状态缩写。Web 服务代码应该很熟悉,因为它使用熟悉的 HttpClient 类。

调用 ZIP 查找 Web 服务中的脚本图 1从 API 下载 ZIP 代码信息,并将其放到名为 ziplookup 的本地变量。我可以在 Razor 代码中使用此变量,以显示城市名称。API 将返回一个异常,如果 zip 文件无效。如果发生这种情况,被显示一条错误消息。

图 1 获取 ZIP 代码信息

try
{
  errorMessage = "";
  var zipresultStr = await Http.GetStringAsync($"http://api.zippopotam.us/US/{zip}");
  zipresultStr = zipresultStr.Replace("place name", "city").Replace(
    "state abbreviation", "stateabbr");
  ziplookup = JsonUtil.Deserialize<Models.ZipLookup>(zipresultStr);
}
catch
{
  errorMessage = "Invalid zip code";
  return;
}

反序列化 ZIP 查找数据在上一代码段中,我是从 Web 服务中检索数据,并反序列化为 Models.ZipLookup 类。这是我创建了以匹配所返回的 JSON 数据的架构的类:

public class ZipLookup
{
  public Place[] places { get; set; }
}
public class Place
{
  public String city { get; set; }
  public String stateabbr { get; set; }
}

返回更多的数据,但我只创建了属性及我想要使用的数据的类。当前实现具有中的 JSON 字段名称带空格的处理的问题。临时解决方法,我使用 String.Replace 要从中删除空格。

显示城市和州现在,已下载并反序列化数据,我可以在 Web 页中显示它。下面的代码块在页中显示的城市和州缩写形式:

<h1>
  @ziplookup.places[0].city, @ziplookup.places[0].stateabbr<br />
</h1>

添加天气条件 Web 服务

下一步的 Web 服务将检索从邮政编码的当前状况openweathermap.org Web 服务。你将需要创建一个帐户,以接收调用 Web 服务时使用的特殊键。

调用当前条件 Web 服务调用来获取当前条件作用很像在以前的 Web 服务调用。下面的异常是在调用中的 apikey 参数。Openweathermap.org 服务需要一个密钥,以对调用方进行身份验证:

currentcondition = await Http.GetJsonAsync<Models.CurrentConditions>(
  $"http://api.openweathermap.org/data/2.5/
  weather?zip={zip},us&appid=<apikey>");
imgurl = $"http://openweathermap.org/img/w/{currentcondition.weather[0].icon}.png";

当前条件调用的结果存储在名为 currentcondition 的本地变量。该结果也将显示一个图标对应的名称传递给当前条件。映像的名称被编码为 imgurl 变量,以便它可以显示在网页中。

反序列化当前条件数据再次重申,原始的 JSON 数据需要反序列化为一个类,因此可以使用它,如中所示图 2。类定义看起来有点奇怪,但它旨在匹配正在从 Web 服务返回的 JSON 数据的架构。没有要返回比此处所示的更多数据。所需的属性必须实现。

图 2 原始的 JSON 数据反序列化

public class CurrentConditions
{
  public CurrentConditions() { }
  public List<Weather> weather { get; set; }
  public Main main { get; set; }
  public String name { get; set; }
}
public class Weather
{
  public String main { get; set; }
  public String description { get; set; }
  public String icon { get; set; }
}
public class Main
{
  public decimal temp { get; set; }
  public decimal temp_min { get; set; }
  public decimal temp_max { get; set; }
}

转换温度从 Web 服务返回温度为开氏度,因此这些值需要转换为华氏度和舍入。当前温度将舍入到在某种程度上最接近 10。高温和低温将四舍五入为最接近的整个程度。

private decimal ConvertKtoF(decimal kelvin, int decimals)
{
  return Math.Round(kelvin * 9 / 5 - 459.67M, decimals);
}

显示当前条件中的 Razor 脚本图 3现在将更新以包含当前温度、 高和低、 当前状况以及对应的图标说明当前状况。请注意温度传递到前面创建的 ConvertKtoF 函数方式。

图 3 更新 Razor 脚本

<h1>
  @ziplookup.places[0].city, @ziplookup.places[0].stateabbr<br />
  @ConvertKtoF(currentcondition.main.temp, 1) &#176;F
</h1>
<h2>
  @currentcondition.weather[0].main <img src="@imgurl" style="display:inline" />
</h2>
<h3>
  <span style="display:inline;color:red">HI
    @ConvertKtoF(currentcondition.main.temp_max, 0)  &#176;F</span> /
  <span style="color:blue">LO @ConvertKtoF(
    currentcondition.main.temp_min, 0)  &#176;F
    </span><br />
</h3>

添加国家/地区的天气服务警报

最终的 Web 服务将检索警报从国家/地区天气服务 (NWS) 是中的状态。

调用 NWS Web 服务下面的代码块从 NWS 中检索所有当前严重的天气警报:

alerts = await Http.GetJsonAsync<Models.Alert>(
  $"https://api.weather.gov/alerts/active/area/{ziplookup.places[0].stateabbr}");

从 ziplookup Web 服务检索到的状态缩写用于筛选到相同的状态的警报。

反序列化 NWS 警报数据需要反序列化为符合 NWS JSON 架构的一组类的 JSON 数据。中的代码段图 4保存来自于 Web 服务返回的警报。

图 4 反序列化 JSON 数据

public class Alert
{
  public String type { get; set; }
  public String title { get; set; }
  public Feature[] features { get; set; }
}
public class Feature
{
  public String type { get; set; }
  public PropertyInfo properties { get; set; }
}
public class PropertyInfo
{
  public String headline { get; set; }
  public String description { get; set; }
  public DateTime effective { get; set; }
  public DateTime expires { get; set; }
}

显示严重的天气警报现在,已检索,将发出警报中的代码图 5添加到 Razor 视图来显示它们。它创建一个表以显示警报,并使用 @foreach 来循环访问并显示每个警报。对于每个警报显示生效日期、 标题和说明。如果没有该状态的警报,则表将为空。

图 5 显示天气警报

<table class="table">
  <thead>
  <tr>
    <th>Date</th>
    <th>Alert</th>
  </tr>
  </thead>
  <tbody>
    @foreach (var alert in alerts.features)
    {
      <tr>
        <td>@alert.properties.effective.ToString("MM/dd/yyyy hh:mmt")</td>
        <td>
          <span style="font-weight:600">@alert.properties.headline</span><br />
          <span>@alert.properties.description</span>
        </td>
      </tr>
    }
  </tbody>
</table>

综合来讲

应用程序现已完成,如中所示图 6。它完全在浏览器中运行,并调用三个外部的 Web 服务,若要显示的当前天气状况和警报。

完成的应用程序
图 6 完成的应用程序

发布 Blazor 应用程序

发布 Blazor 应用程序是像发布任何其他 ASP.NET 应用程序一样容易。从 Visual Studio 执行此操作将编译的应用程序代码并生成具有最终 HTML、 CSS、 脚本和 Blazor 二进制文件所需的所有的完整 Web 应用程序。使用发布到文件夹选项很好的起点。所有应用程序的文件获取放在那里。如果您查看 Dist 文件夹,您会发现标准 index.htm 页和框架 (_f) 文件夹。框架 (_f) 文件夹包含所有的已编译的程序集,以及 Blazor 和 Mono 运行时组件。

后续步骤

Blazor 仍是发布的一个实验性框架,但团队正在转发速度非常快。Blazor 路线图详细介绍路由、 组件、 布局和的详细信息的完整框架。在 GitHub 上的开放源开发 Blazor 项目。请访问 blazor.net 页后,可以保持最新状态并体验的当前位。


Jonthan Miller是一名高级架构师。十年来,他一直致力于在 Microsoft 堆栈上开发产品,并在 .NET 出现之后,致力于在其上进行编程。Waldman 是前端技术 (Windows 窗体、 Windows Presentation Foundation、 Silverlight、 ASP.NET、 AngularJS/Bootstrap)、 中间件 (Windows 服务,Web API) 和后端 (SQL server、 Azure) 方面的专业知识的完全堆栈产品开发人员。

衷心感谢以下技术专家对本文的审阅:Dino Esposito (BaxEnergy) Daniel Roth (Microsoft)


在 MSDN 杂志论坛讨论这篇文章