.NET Core Web API:你需要知道的一切(第一部分,共两部分)
一篇快速文章,带你了解如何构建 .NET Web API 并通过 AJAX(使用 JSON)向其发送数据。
背景
最近,在尝试使用 .NET Core Web API 时,我发现自己不确定通过 XMLHttpRequest
(XHR) 对象将数据发布到 Web API 的正确方法。
我不确定
- 如何将数据添加到 XHR 对象中,使其显示在 post body 中。
- 如何确保 Web API 能自动将发布的 JSON 转换为我目标域对象。
我收到了各种错误,而且无法判断是客户端出了问题,还是 Web API 端出了问题,或者两者都有。我发现两者都有问题,但我还发现,即使你认为这是一个常见的组合(纯 JavaScript XHR 发布到 Web API),也很难找到一个确切的答案。
本文涵盖的内容
- 创建 .NET Core Web API 项目(通过命令行)- 我在 Ubuntu Linux 上使用 Visual Studio Code。
- 添加一个可以被发布的 Web API 方法。
- 修改 Web API 应用程序,使其能够提供一个 *index.htm* 文件(用于测试我们的 Web API)。
- 设置一个简单的 *index.htm* 和 *main.js* 文件,用于向 Web API 发送数据。
- 配置
XMLHttpRequest
(XHR) 以将 JSON 发布到 Web API。
引言
我假设您已经在机器上安装了最新版本的 Visual Studio 或 Visual Studio Code。
当然,您还需要确保已安装最新版本的 .NET Core。如果您需要,可以访问 https://dotnet.microsoft.com/download^。
我在 Ubuntu Linux 上运行 Visual Studio Code,所以一切都通过命令行操作,但如果您在 Windows 上使用 Visual Studio,则可以使用向导来创建 .NET Core Web API。不过,使用 .NET Core 进行命令行操作并不困难,而且您可能会觉得相当令人满意。
创建新项目
开始之前,请执行以下步骤:
- 打开控制台(或终端)。
- 导航到您想要创建新项目的目录。
- 键入以下命令:
dotnet new webapi --name MainWebAPI
将创建一个新文件夹
当您键入该命令并按 **<ENTER>** 后,它将创建一个名为 `MainWebAPI` 的新子文件夹,并将所有项目文件添加到该目录中。
如果成功,您将看到类似以下内容:
如果您收到“命令未识别”之类的错误,那么您可能尚未安装 .NET Core SDK。要确定已安装的 .NET 版本,可以尝试以下命令:
dotnet --version
我的回复是 3.1.202。
如果一切顺利,那么您就拥有了一个基于 .NET Core 的非常基本的 Web API。
运行 Web API 项目
让我们确保项目可以编译并运行。这非常简单。
首先,只需切换目录到新创建的项目目录:cd MainWebAPI
接下来,键入:dotnet run
.NET 将构建并启动 Web API。
它看起来将与以下内容类似:
仅构建项目
顺便说一句,如果您只想构建项目,可以键入:dotnet build
查看 Web API 的运行效果
要查看 Web API 的运行效果,您可以按住 **CTRL** 键并单击控制台窗口中显示的链接 (https://:5001)。这样做后,您的默认浏览器将打开该链接。
但这并不怎么神奇,因为您还没有调用任何 Web API 方法。
您只会看到一个空白网页。
通过 GET 请求调用一个 Web API 方法
然而,有一个方法可以通过 `GET` 请求调用。请尝试以下 URL:https://:5001/WeatherForecast/^。
这会调用 `WeatherForecastController`**\* ** 类中的默认 `Get` 方法。该方法仅生成一些随机的 `WeatherForecast`**\* ** 域对象,然后将它们作为 JSON 发送给客户端。
**\* ** 在我们查看代码时,我将很快向您展示这两个类。
您将在浏览器中看到以下两张图片中的一张(取决于您的浏览器如何渲染 JSON)。
现在您已经看到了代码的运行情况,让我们来看看代码,并开始修改 `WeatherForecastController`,以便我们可以向其发布 JSON,并让它自动将 JSON 转换为域对象。
Visual Studio Code
我将在本文中全程使用 Visual Studio Code (VSC),但如果您愿意,仍然可以使用 Visual Studio。
请启动 Visual Studio Code (VSC),然后我们将打开项目。
打开文件夹
要在 VSC 中加载项目,您需要:
- 转到主菜单
- 选择 [File...] 菜单项
- 选择 [Open Folder...]
- 导航到创建项目时创建的文件夹,然后选择它。
完成这些步骤后,您将看到类似以下内容:
高亮区域
我在 VSC 中高亮显示了几个区域,以便我们可以讨论它们。
工具栏
第一部分是工具栏,其中包含几个图标。
文件视图
当前选中的图标以白色高亮显示,表示文件视图已显示。您可以看到第二部分中有一个文件列表。但是,如果您选择其他图标,这一部分将会改变。
调试视图
在本文中,我们还将使用调试项向您展示如何在 VSC 中逐步调试 Web API 代码。调试项由 表示。
但是,要在 VSC 中使用调试器,您必须确保已安装并运行 C# 扩展插件。
插件视图
插件视图由 表示。
您可以看到,我在上面提供的 VSC 概览图中,第四部分实际上是 VSC 警告我需要将 C# 扩展插件添加到项目中才能调试代码。如果您看到此提示并单击 **[Yes]** 按钮,它将将其添加到项目中。但是,如果该扩展尚未添加到您的系统中,您可能不会看到此提示。
另外,该弹出窗口通常会很快消失。不过没关系,因为您可以随时单击插件图标将插件添加到您的系统和项目中。
安装并激活后,它的外观如下:
主编辑器区域
最后,您可以看到主编辑器区域(上面 VSC 图片中的区域 3)当前显示有关 VSC 最新版本的信息。这是当您从文件查看器区域选择文件时,每个文件将在此处显示的区域。
现在您对 VSC 更加熟悉了,让我们开始查看 `WeatherForecastController`。
检查 WeatherForecastController
打开子目录中的文件
请确保您处于文件视图。接下来,在文件视图中,找到 `Controllers` 目录。它会有一个指向 Controllers 名称的向下右箭头(大于号)。这表示一个当前折叠的文件夹。单击 _Controllers_ 文件夹并选择出现的 _WeatherForecaseController.cs_,它将在编辑器区域中显示。
Controller:应用程序逻辑层
我不会解释 Controller 代码的每一个方面。您可以将 Controller 总体看作是应用程序逻辑层。以下是基本工作原理。
用户通过向服务器上的命名 Controller(由 URL 描述)发送 HTTP 命令(GET、POST、PUT 等^)来请求 Controller 上的操作(函数或方法)。
**注意**:在本文中,我们只处理 `POST` 命令。
作为开发人员,我们可以在 `Controller` 方法中编写代码来运行应用程序逻辑,从而提供特定的功能。就这么简单。
项目模板包含在 `Controller` 中的 `Get` 方法如下所示:
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
Console.WriteLine("in get...");
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
这可能很明显,但 `[HttpGet]` 方法装饰器告诉编译器通过 HTTP `Get` 使此方法可用。
此方法仅生成五个 `WeatherForecast` 对象(域对象)的数组并返回它们。它们将被自动序列化为 JSON,这就是我们之前在浏览器中看到的。
我们的主要挑战
这里的挑战是如何将数据发布到 `WeatherForecast` Controller,以便它能自动反序列化为 `WeatherForecast` 对象。
首先,让我们在 Controller 中添加最基本的 `Post` 方法。
[HttpPost]
public String Post(){
Console.WriteLine("in post...");
return "It worked!";
}
首先,请注意我们将装饰器更改为 `[HttpPost]`,这样当用户发布到 URL https://:5001/WeatherForecast 时,此方法就会触发。
默认 Post 方法
这个内部方法名是 `Post()`,但外部没有显示动作(方法名),这意味着当用户直接发布到 `Controller` 时,这是默认的动作(方法)将会发生。
有一种方法可以提供一个外部名称,然后该名称可以包含在 `WeatherForecast` Controller URL 中,我稍后会向您展示。
次要挑战:客户端 Post 创建
现在,我们面临着测试 `WeatherForecast` Controller 上的 `Post()` 方法的挑战。由于我们不能再仅通过浏览器访问 URL(简单的 HTTP `Get`),我们需要其他方法来从客户端调用 HTTP `Post`。
第一个最简单的 Post 测试
执行 `Post` 到 Controller 的第一个也是可能是最简单的方法是下载 `PostMan` 并进行设置。还有其他工具,但 PostMan 可能是最简单的,而且是免费的。
访问 https://www.postman.com/downloads/^,获取免费工具并为您当前的系统安装它。
完成安装并启动后,我们可以尝试发布到我们的 Controller。
PostMan 请求演练
PostMan 的用户界面有点混乱,但创建新请求很容易,我将引导您完成。
创建新请求
您要做的第一件事是创建一个新请求。为此,您可以选择 [**New**] 按钮,然后在菜单出现时选择 [**Request**]。或者,您可以单击 [**Create a Request**] 选项。这两者都在下一张图片中以红色高亮显示。
选择其中一个选项后,您将看到一个新表单。在图片中,您可以看到我也展开了 HTTP Actions 列表,向您展示了在哪里可以选择在提交请求时使用的操作。首先,我们将尝试简单的 `Get`。
PostMan:配置更改
但是,在发送第一个请求之前,您需要对 PostMan 的默认设置进行一项更改。如果您不这样做,那么您所有的请求都将失败,因为 PostMan 不支持自签名证书。
如果您不更改设置,您将看到类似以下的错误(请注意错误中的蓝色高亮文本)。
HTTPS:不支持自签名证书
要解决此问题,只需在 PostMan 中转到 **File**...**Settings**... 并关闭此处高亮显示的选项(此处仍显示为开启)- SSL certificate verification。
完成此操作并关闭 **Settings** 窗口后,您可以继续操作,它将正常工作。
继续从下拉列表中选择 `Get` 命令。
接下来,添加以下 URL,然后按 **<ENTER>** 或单击 [**Send**] 按钮。
完成此操作后,您将看到与之前在浏览器中看到的结果类似的结果,只是格式化方式不同,符合 PostMan 的格式。
请记住,这是调用 `WeatherForecast` Controller 上默认 `Get()` 操作的结果。我提到这一点是为了提醒您,我们没有在 URL 中添加任何(特定的)动作名称。
Post 到 WeatherForecastController
现在,让我们在 PostMan 中将 HTTP Verb 更改为 `Post`,然后再次尝试。
这是新的、非常基础的结果:
这都很好,但我们想要做的是将一些 JSON 发布到 Controller,并让它自动将 JSON 转换为我们的域对象(`WeatherForecast`)。
发布 JSON:尝试 1
首先,我们需要了解目标域对象是什么样子的。它有哪些属性?
我们可以在 VSC 中查看 `WeatherForecast` 类。
WeatherForecast 类分析
这是一个非常简单的类。这是该类的完整代码列表。
using System;
namespace MainWebAPI
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
}
它基本上只由四个属性组成(尽管其中一个属性会运行一个函数来获取其值)。
日期
TemperatureC
// (摄氏温度)TemperatureF
// (计算出的华氏温度)Summary
// 天气文字摘要
JSON:名称/值对
既然我们知道 JSON(JavaScript Object Notation)只是定义对象的名称值对,我们就可以非常轻松地创建自己的 `WeatherForecast`(带数据)。以下是我们将在测试中使用的:
{"Date":"2015-05-03", "TemperatureC":37,"Summary":"Our weather data"}
请注意,由于 `TemperatureF` 是从 `TemperatureC` 计算出来的,我甚至没有在我们的 JSON 中包含该属性。
括号:代表对象 - 快速 JSON 摘要
以下是一种快速思考 JSON 内容的方式:花括号表示对象的开始和结束。冒号前面的每个值表示属性名,冒号后面的每个值表示属性值。每个名称/值对都用逗号分隔。每个属性名必须与目标类中的属性匹配。
就是这样,让我们在 PostMan 中尝试一下。
更改 WeatherForecastController Post 方法
我们必须对 `WeatherForecastController` 中的默认 `Post()` 方法进行一项更改。我们必须确保它能够接收一个 `WeatherForecast` 对象。
让我们在 VSC 中打开 `WeatherForecastController` 并进行更改。
我们还将方法更改为返回一个 `Int32`,并返回 `TemperatureF`,这样您就可以看到该值已生成。这也为我们提供了一个稍微可用的 Web API 方法(尽管有点牵强),因为您可以发送一个 `WeatherForecast` 对象,其中您知道摄氏温度,该方法将返回华氏温度。
以下是修改后的默认 `Post()` 方法:
[HttpPost]
public Int32 Post(WeatherForecast wf){
Console.WriteLine("in post...");
return wf.TemperatureF;
}
进行更改并运行
进行这些更改后,请返回控制台并:
- 如果程序已在运行,请关闭它(按 **CTRL-C** 结束会话)。
- 再次构建并运行应用程序 -- `dotnet run` 将重新构建并运行应用程序。
- 复制上面创建的 JSON。
- 回到 PostMan。
- 单击 [**body**] 选项卡(参见下一张图片)。
- 选择 [**raw**](下一张图片)单选按钮。
- 将 JSON 粘贴到编辑器区域。
- 单击 [**Send**] 按钮。
完成所有这些步骤后,您将看到以下结果(错误)。
**注意**:我使用了一张图片来显示多个步骤和结果。
错误解释
有点令人困惑,但底部部分是将 JSON 发布到 Controller 后的结果。我们没有收到预期的响应(`TemperatureF`),而是收到了一些描述问题的 JSON。
很难判断是否发生了错误以及错误发生在何处。
Post() 方法的 Console.WriteLine 没有运行
请记住,我们的默认 `WeatherForecastController` 有一个 `Console.WriteLine()` 调用,它会尝试输出一条消息。这应该会在运行应用程序的控制台窗口中输出一些文本,但如果您查看,却没有输出。这为我们提供了一个线索,说明我们的方法从未运行。
不支持的媒体类型
真正提示我们发生了什么的是错误 415 和消息“unsupported media type”。但这仍然有些晦涩,如果您刚开始学习,您会wonder这到底意味着什么。经过大量搜索和阅读后,您会发现这意味着服务器不知道我们正在向它发布什么类型的数据,它不喜欢这样。
通过 HTTP Header 修复问题
我们可以通过向我们的 `Post` 操作添加一个 HTTP Header 来告诉服务器我们发送的数据类型。
服务器希望知道您正在发送的数据类型,以便它可以正确使用这些数据。让我们将正确的 HTTP Header 添加到我们的 PostMan Post 操作中,然后再次尝试。
HTTP Headers:名称/值对
HTTP Headers 是元数据(与实际数据一起发送的数据,用于描述数据),它们与数据一起发送。要在 PostMan 的 headers 中添加一个名称/值对,我们只需要:
- 选择 Headers 选项卡(参见下一张图片)。
- 添加一个新的 header,名为:`Content-Type`(在 Key(与 Name 相同)下键入此内容)。
- 为添加的 Key 添加一个新值:`application/json`
这个 Key 和 Value 都是 HTTP 规范预定义的,服务器将能够理解。
现在,当您再次发布时,服务器将理解传入的内容是 JSON,并应将其作为 JSON 处理。
进行这些更改后,请再次单击 PostMan 中的 [**Send**] 按钮,这次您将获得有效的结果。98 华氏度与 37 摄氏度(我们在 JSON 中发送到 `WeatherForecastController` 的值)是相同的温度。
您还可以看到 `WeatherForecastController` 的 `Post()` 方法确实运行了,因为在您的控制台窗口中,有来自 `Console.WriteLine()` 调用的输出。
自动转换为域对象:太棒了!
另外,请思考一下 .NET Core Web API 自动将我们的 JSON 转换为我们的域对象(`WeatherForecast`)是多么的神奇。
您可以通过以下事实来判断它是自动完成的:我们的默认 `Post()` 方法只是接受一个 `WeatherForecast` 类型的参数,并且我们在 `return` 语句中引用了该对象的属性。
return wf.TemperatureF;
第一部分总结
我希望您觉得本系列文章的第一部分很有启发性,并以清晰的方式揭示了一些棘手的元素(HTTP Headers、HTTP Post 和使用 PostMan)。
但是,由于本文已经很长了,我决定将文章的其余部分放在 第二部分。
现在我们明白了我们需要:
- JSON 数据添加到 `Post` body 中。
- 特殊的 `Content-Type` header 添加到 `Post` 操作中。
......这将使我们更容易理解如何使用 JavaScript 通过 AJAX(`XMLHttpRequest` 对象)将数据发布到我们的 Web API Controller。
Using the Code
- 下载 zip 文件。
- 将文件解压到您的 _Dev_ 目录(有一个名为 _MainWebAPI_ 的外层文件夹,其中包含所有文件和子文件夹)。
- 打开控制台并将目录更改为 _\MainWebAPI_。
- 运行命令 `/> dotnet run`。
- 应用程序将构建并启动 Web 服务器。
历史
- 2020年5月20日:提交第一部分