65.9K
CodeProject 正在变化。 阅读更多。
Home

.NET Core Web API:您需要了解的全部(第 2 部分,共 2 部分)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (5投票s)

2020 年 5 月 20 日

CPOL

11分钟阅读

viewsIcon

8952

downloadIcon

134

通过 XmlHttpRequest 将数据发布到您的 Web API - 了解如何设置标头、请求正文数据等。

引言

这是本系列文章两部分中的第二部分,也是最后一部分。

您可以阅读第一部分:.NET Core Web API:您需要了解的全部(第 1 部分,共 2 部分)[^]

在本文中,我们将介绍

  1. 创建用于将数据发布到 WeatherForecastController 的基本 HTML 页面
  2. 修改 dotnet Web API 以返回静态页面(提供 HTML)
  3. 基本的 JavaScript,其中将包含创建 XmlHttpRequest (XHR) 的代码
  4. 配置 XHR 以在请求正文中携带数据和适当的标头进行发布

在第一篇文章中

在第一篇文章中,我们创建了基本的 Web API 模板项目,并学习了如何使用 PostMan^ 向其发布数据。

现在,我们将运用在那里学到的所有知识,创建一个可以向我们的 Web API 发布 JSON 的网页。

让我们直接开始创建我们将用于向我们的 Web 控制器发布数据的 HTML 页面。

添加子文件夹和 index.htm

我首先在项目中添加一个名为 wwwroot 的新子文件夹。我添加这个文件夹名称是因为这是我在托管网站上部署文件的文件夹名称,在那里我可以部署我的 Web API。

您还会发现,dotnet 运行的默认服务器会自动识别 wwwroot 并直接从该目录提供 index.htm 文件。

接下来,我添加一个名为 index.htm 的新文件,并添加我们基本的 HTML,它将提供一个按钮,我们可以点击该按钮将数据发布到 Web API。

这是整个 HTML 文件。它非常简单,请记住,整个示例都使用纯 JavaScript(无需外部 JavaScript 库),因此所有代码都非常简单。

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>XHR Poster</title>
        <meta charset="utf-8">
        <script src="main.js"></script>
        <style>
            body{padding: 5px 15px 5px 15px;}
        </style>
    </head>
    <body>
        <label for="tempC">Temp (C)</label>
        <input id="tempC" type="number" value="20">
        <button onclick="postData()">Post TempC</button><br>
        <label for="tempF">Temp (F)</label>
        <input id="tempF" type="text">
    </body>
</html>

该页面在浏览器中呈现以下内容

simple html test page

来自 HTML 的四个要点

由于它是自明的,我将不解释所有的 HTML,但我将指出四个重要的注意事项

  1. main.js,它在顶部加载,包含我们所有提供所需功能的 JavaScript 代码。
  2. 有一个 number 输入框,允许用户设置要发布到 Web API 的 TempC 值(代表摄氏度值)。
  3. 文本输入框,显示从 Web API 返回的 TemperatureF 的输出值(代表华氏度值)。
  4. 页面功能通过单击按钮触发,该按钮调用 main.js 中的 JavaScript 方法 postData()

如果您正在跟进,请继续在 /wwwroot 文件夹中添加一个 main.js 文件。这是我的 Visual Studio Code (VSC) IDE(集成开发环境)中所有文件外观的快照。

VSC file view

仔细看,您会发现 /wwwroot 目录包含两个文件(index.htmmain.js)。

dotnet run:启动您的 Web 服务器

让我们运行 dotnet,以便它再次编译 Web API 并为我们启动 Web 服务器,这样我们就可以尝试让它提供 index.htm 文件。这会导致错误,但会很有启发性。

要启动 Web API,请转到您的控制台窗口(本系列第 1 部分对此有详细介绍),然后键入命令

$>dotnet run

执行此操作后,服务器将在默认主机和端口(localhost:5001)上启动,以便我们尝试加载 index.htm

客户端代码和服务器代码(Web API)

请记住,现在我们的项目同时包含客户端代码(index.htmmain.js)和服务器代码(用 C# 编写)。但是,dotnet 引擎基本上会忽略我们的 HTML 和 JavaScript。它知道不需要编译这些代码,只需要处理它找到的 C# 代码。但是,dotnet 做的另一件事是为您启动一个不错的 Web 服务器。

尝试加载 index.htm

尝试此 URL,看看 index.htm 是否加载:https://:5001/index.htm

您会看到这个。这是一个完全空白的页面。

blank web page

您的第一反应可能是需要更改 URL 以包含子目录 /wwwroot,例如 https://:5001/wwwroot/index.htm

我在尝试完成此操作时曾想过问题可能出在这里。

您也可能认为里面至少有一些骨架 HTML,但我向您保证,并没有。当您访问该 URL(对您的 Web API 来说是不存在的)时,服务器的响应为空。

如果您是第一次尝试,这可能会非常令人困惑,因为您可能会认为还有其他问题。这个问题也很难搜索,因为没有错误值或错误消息。

问题在于 Web API 未设置为提供像 index.htm 这样的静态页面。让我们改变这一点。

更改 Web API 以提供静态页面

默认情况下,Web API 项目不会提供像 index.htm 这样的静态页面。这可能是合理的,因为我们正在创建一个 Web API。但是,如果您想能够测试您的 Web API 而不会遇到 CORS(跨域资源共享)的各种困难,那么了解如何更改 Web API 会很有用,而且非常简单。

更改 Startup.cs

为此,我们只需打开 Startup.cs 并添加一个新的服务来在 dotnet 应用程序上运行。

当您打开 Startup.cs 并查看 Configure() 方法时,您会看到已经加载了许多服务。在我们更改代码之前,它将如下所示

// This method gets called by the runtime. 
// Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
 
            app.UseHttpsRedirection();
 
            app.UseRouting();
 
            app.UseAuthorization();
 
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

您可以看到项目模板已经向 app 对象添加了许多服务以供运行。这些服务提供了路由(UseRouting())和 HttpsRedirection() 等特定功能。

我们只需要添加一行代码来添加静态页面服务。

让我们在当前包含此行的代码后面添加一行

app.UseAuthorization();

我们的新代码行将是

app.UseStaticFiles();

在 VSC 中看起来是这样的

app.UseStaticFiles()

进行更改后,返回到控制台并停止 Web API(CTRL-C),然后通过 $> dotnet run 再次启动它。

现在,我们可以再次尝试该 URL,页面将正确提供

https://:5001/index.htm^

这下好多了!

static page loads properly

现在,我们已经准备好尝试一些 JavaScript 代码了。但现在,我们需要让我们的 JavaScript 与我们的 XmlHttpRequest (XHR) 正确配合。我们将把所有这些工作都放在 main.js 文件中。

在 JavaScript 中配置 XHR

我们需要做四件事

  1. 实例化内置的(JavaScript/浏览器提供的 XHR 对象)。
  2. 向 XHR 对象添加一个事件监听器,用于在异步请求完成时(load 事件)触发。
  3. 向 XHR 对象添加一个事件监听器,用于在发生错误时运行。
  4. 添加一个变量来保存我们将发布到的 URL。

这是我放在 main.js 开头的四行代码

var xhr = new XMLHttpRequest();
xhr.addEventListener("load", transferComplete);
xhr.addEventListener("error", transferFailed);
var url = "https://:5001/WeatherForecast/"

当 XHR 对象 load 事件发生时,它将调用一个名为 transferComplete() 的方法,该方法将在 JavaScript 代码的更下方定义。

如果 XHR 对象 error 事件发生,它将调用一个名为 transferFailed() 的方法,该方法将在我们的 JavaScript 代码的更下方定义。

用户通过单击按钮启动进程

我稍后会向您展示这两个方法,但首先,让我们看一下当用户单击 [TempC] 按钮时触发的代码。

请记住,我们在 HTML 中将按钮的 onclick="postData()" 连接起来。该方法定义在我们的 main.js JavaScript 中。

这是 VSC 中的整个方法,带有行号,以便我们可以讨论正在发生的事情。

postData() 方法:逐行解析

postData() method

由于我们已经实例化了 xhr 对象并设置了 URL,现在我们只需要调用 xhr 对象的 open() 方法。

我们向 open() 方法提供两个参数

  1. HTTP 动作(Get)作为 string
  2. 我们要发布到的 URL

yyyy-mm-dd 日期格式

在第 8 行,我们设置了一个本地变量来保存日期 string。请注意,此日期格式为 yyyy-mm-dd。

如果您使用其他格式,Web API 可能无法在服务器端将 string 转换为日期,并且可能会失败。

第 9 行

我们调用浏览器方法 document.querySelector() 来获取对数字输入控件的引用并获取其当前值。这样,用户就可以动态更改值以向 Web API 提交不同的值。我们将当前值存储在本地变量中以备后用。

第 10 行只是一个 console.log() 输出到浏览器控制台,以便我们可以看到正在发生的事情。

第 11 行:JSON 对象

在第 11 行,我们创建一个名为 weather 的新变量,并用 JSON 值初始化它。JSON 中的每个名称都将成为本地 weather 对象的一个属性。我这样做是因为它使向 Web API 发布 JSON 更加容易。当然,weather 对象中的每个属性名称都与我们在 Web API 中(在 C# 中定义)的 WeatherForecast 域对象中的属性名称匹配(有关更多信息,请参阅本文的第 1 部分)。

第 12 行:显式将值转换为数字

在第 12 行,我向已经创建的 weather 对象添加了一个名为 TemperatureC 的新属性。JavaScript 是一种动态语言,允许对此类对象进行此类操作。您只需通过引用对象并为其赋值即可动态添加新属性。我添加此属性名称(TemperatureC)是因为它是 Web API 端域对象上预期的名称。

请注意,我还显式地将本地变量 tempC 的值(它是一个 string)强制转换为 JavaScript Number 类型(它是一个 Double - 高精度数值)。这是因为我发现,由于 Web API 期望 TemperatureCInt32,如果该值以 String 的形式传入,它似乎会失败(JSON 对象中的 string 没有自动转换为 Int32)。

第 13 行是另一个 console.log(),只是为了检查 Date 属性的格式。

设置请求标头

在第 14 行,我们必须在 XHR 对象上设置 Content-Type 请求标头,否则 Web API 将以 415 错误失败,就像我们使用 PostMan 将数据发布到 URL 时一样(请参阅第 1 部分)。

XHR 发送

最后,在第 15 行,我们调用 XHR send() 方法,该方法将实际将数据发布到 Web API URL。

但是,我们想发布代表我们 weather 对象的 JSON string,为此,我们调用一个内置的浏览器方法 JSON.stringify(),它将 weather 对象转换为适当的 JSON 字符串。

但是,这只是触发 Web API WeatherForecastController 默认 Post 方法的请求。我们需要在请求完成并且我们的 JavaScript 代码收到 load 事件已触发的通知后做一些工作。这就是我们之前绑定的事件处理程序发挥作用的地方。

transferCompleted() 逐行解析

这是非常简单的 transferCompleted() 方法,我们将其定义并连接起来,以便在 XHR 对象的 load 事件触发时运行。

transferCompleted()

前两行只是输出到浏览器控制台,以便我们看到从 Web API 返回的内容。我们知道 Web API 的默认 Post() 方法返回代表我们发布到方法的摄氏度值的华氏度温度(有关更多信息,请参阅第 1 部分)。

XHR 响应属性

xhr 对象的 response 属性包含 Web API 返回的数据。在我们的例子中,这只是一个表示华氏度温度的整数值。通常,您会返回一个表示比这更复杂的内容的整个 JSON 对象,但对于我们的测试,我们只返回整数值。

在第 21 行,我们仅使用 document.querySelector() 方法获取对 tempF 文本控件的引用,并将其值设置为 Web API 返回的值(xhr.response)。

final output

流程概述

就是这样!现在,当用户在数字控件中选择一个值并单击按钮时,将调用 Web API,它将返回相应的华氏度温度并将其显示在 tempF 文本框中。

这一切都有效,因为我们正确地设置了 XHR 对象,并具有

  1. 正确的 URL
  2. 适当的 Content-Type 标头
  3. 正确的 JSON(表示 WeatherForecast 域对象)

如果您其中任何一项出错,您会在浏览器控制台中看到一个非常基本的错误,告知您出了问题。

获取代码并尝试一下

我已在顶部发布了最终代码。获取代码并自行尝试。现在您可以将您的 Web API 和 JavaScript 请求以此为模型,并开始使用这项技术,所有信息都集中在一个地方。

历史

  • 2020 年 5 月 20 日:文章发布
© . All rights reserved.