创建 .NET Core API
先决条件
项目设置
运行 API
将项目添加到解决方案
向库中添加类
创建我们的第一个控制器
在浏览器中进行测试
通过代码进行测试
结束
虽然这篇文章严格来说是我正在进行的一个有趣的轻量级游戏开发副项目系列的一部分,但我今晚在该项目上的活动为我提供了一个很好的机会来分享如何创建一个新的 ASP.NET Core Web API。
本文将引导您完成创建、运行和测试新的 ASP.NET Core Web API 的一些简单步骤。
先决条件
我将使用 .NET Core 2.1,因为这是我的机器上安装的版本,尽管今天.NET Core 3 的 Release Candidate 1 已经发布了。
开始使用:
-
下载并安装Visual Studio 2019 社区版。您还需要安装 .NET Core 跨平台开发工具。

-
下载并安装.NET Core 2.1 SDK
项目设置
打开 Visual Studio 2019 并创建一个新项目。
出现提示时,选择ASP.NET Core Web 应用程序,然后单击“下一步”。
请为您的项目取一个有意义的名称。解决方案名称将自动生成。
接下来,Visual Studio 会询问您要选择哪个初始模板。这些选择并不妨碍您以后选择其他方案。现在,为了创建一个简单的演示应用程序,我们将选择 API 模板,并取消选中右侧的所有复选框。
点击“创建”,您的项目应该就会被创建并打开。
运行 API
要验证一切是否正常工作,请转到屏幕顶部的“调试”菜单,然后单击“启动但不调试”。这将启动一个网页浏览器,并显示一个空白网页,其中包含文本 ["value1", "value2"]。
信不信由你,这意味着一切正常。你的浏览器已导航到该类ValuesController并访问了其 HTTP GET 路由,该路由返回了该内容。
ValuesController.cs以下是文件夹中的一段代码片段Controllers:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
// Other code omitted...
}
浏览器导航到与名称前缀/api/values匹配的页面(参见类的属性)。在这个控制器中,我们映射到上面列出的方法,因为使用的方法是 GET(浏览器导航会执行 GET 请求),而且我们没有进一步研究。ValuesControllerRouteValuesControllerGetapi/values
因此,ASP.NET Core 运行了该Get()方法,并返回了 200 OK 结果,其内容在上面的列表中的字符串数组中定义。
太棒了!我们的代码运行正常。现在是时候进行更深入的开发了。
将项目添加到解决方案
添加新项目时,我首先会创建两个新的库项目并将它们添加到解决方案中。第一个库将用于存放所有应用程序逻辑,第二个库将用于存放单元测试。
在解决方案资源管理器中,右键单击您的解决方案(包含项目的最顶层项),然后选择“添加” ,再选择“新建项目”。
选择类库 (.NET Standard),单击“下一步”,给它起一个有意义的名字(我将其命名为 MattEland.Starship.Logic),然后单击“创建”。
库创建完成后,我们将在解决方案资源管理器中右键单击主 Web API 项目,选择“添加”,然后选择“引用”。在这里,我们将检查添加的库的名称,然后单击“确定”。
这样一来,主 API 项目就可以使用库中定义的代码,这有助于将 API 特定的逻辑与领域逻辑分离,并在需要时更容易地将应用程序逻辑移植到控制台、桌面或移动应用程序。
现在,点击解决方案资源管理器,添加另一个新项目。这次我们将选择新建一个 XUnit 测试项目或一个 NUnit 测试项目。在本教程中,我将演示使用NUnit 测试项目 (.NET Core)模板。
你可以随意给项目命名(我的名字是 MattEland.Starship.Tests),然后点击“创建”。
接下来,我们将右键单击测试项目,并像之前一样添加依赖项。这次,我们将同时添加对库和 Web 应用程序的依赖。这样,我们的测试就可以直接调用控制器上的方法来进行集成测试。
向库中添加类
接下来,我们创建一些示例领域类,并将它们添加到逻辑库中。选中项目后,右键单击,然后单击“添加”,再单击“类...”
接下来,将选择项保留为类(Class),但要给它起一个有意义的名字。我的名字将用来GameState.cs表示回合制游戏的状态。
在这个类中编写一些简单的代码——足以测试一个简单的对象结构。
我的数据如下:
namespace MattEland.Starship.Logic
{
public class GameState
{
public GameState(int id)
{
Id = id;
}
public int Id { get; }
public int ClosedCount { get; set; }
}
}
我还会创建一个类GameRepository来存储 GameState 实例。我们的控制器将与这个类进行交互。
下面列出了一个非常简单的演示型代码库:
using System.Collections.Generic;
using System.Linq;
namespace MattEland.Starship.Logic
{
public class GameRepository
{
private readonly IList<GameState> _games = new List<GameState>();
public GameRepository()
{
// Start with some sample data
CreateNewGame();
}
public IEnumerable<GameState> Games => _games;
public GameState GetGame(int id) => _games.FirstOrDefault(g => g.Id == id);
public GameState CreateNewGame()
{
int id = _games.Count + 1;
var game = new GameState(id);
_games.Add(game);
return game;
}
public bool DeleteGame(int id)
{
var game = _games.FirstOrDefault(g => g.Id == id);
return game != null && _games.Remove(game);
}
}
}
现在我们有了一些基本逻辑和一个用于管理操作的存储库类,让我们看看它如何与控制器集成。
创建我们的第一个控制器
接下来,我们删除该ValuesController.cs文件(或者如果您想保留它作为参考,也可以保留它),并向 Web API 项目添加一个新的控制器。在我的例子中,这个控制器用于GamesController管理各种可用的游戏状态。
此类将保存我们之前创建的存储库类的新实例,并将操作传递给它。
我的控制器信息如下:
using System.Collections.Generic;
using MattEland.Starship.Logic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace MattEland.Starship.ProcessingService.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class GamesController : ControllerBase
{
private readonly GameRepository _repository = new GameRepository();
// GET api/games
[HttpGet]
public ActionResult<IEnumerable<GameState>> LoadGame()
{
return Ok(_repository.Games);
}
// GET api/games/42
[HttpGet("{id}")]
public ActionResult<GameState> LoadGame(int id)
{
var game = _repository.GetGame(id);
if (game != null)
{
return Ok(game);
}
return new NotFoundResult();
}
// POST api/games
[HttpPost]
public CreatedResult NewGame()
{
var game = _repository.CreateNewGame();
return new CreatedResult($"/api/games/{game.Id}", game);
}
// DELETE api/games/42
[HttpDelete("{id}")]
public StatusCodeResult Delete(int id)
{
bool deleted = _repository.DeleteGame(id);
if (deleted)
{
return new StatusCodeResult(StatusCodes.Status204NoContent);
}
return new NotFoundResult();
}
}
}
请注意,我定义了一个标准的 GET 方法来获取所有游戏,以及一个特定的 GET 方法来通过游戏 ID 获取单个游戏。这些方法的区别在于传递给HttpGet属性的参数不同,获取特定游戏的方法会接收一个{id}映射的变量作为int id参数。
另外请注意,我定义了用于创建新游戏和删除现有游戏的方法的动词HttpPost。HttpDelete
请再次注意,这只是非常简化的实现。在实际应用中,我会将请求验证和错误处理等功能集成到 API 层(如果不是由中间件处理的话)。
在浏览器中进行测试
现在逻辑已经准备就绪,你可能会认为我们可以直接运行程序而不进行调试,就能看到新的响应,但请记住,Visual Studio/api/values上次启动时已经导航到了该路径。这是因为项目的调试设置配置为查找该路径。
我们可以通过转到 Web API 项目的属性节点并双击它,然后选择“调试”选项卡,再将“启动浏览器”文本框中的 URL 更改为与新控制器的名称匹配来更改默认路径。
完成所有配置并保存项目(文件 > 全部保存)后,不进行调试运行,并验证是否看到预期的数据。
就我而言,我看到:[{"id":1,"closedCount":0}]这根据我简单的对象定义来看是正确的。
此时,您可以向本地实例发出 HTTP 请求,它将返回相应的响应。
通过代码进行测试
我喜欢在测试应用程序时更加安全可靠,而不是手动测试每一个 API 调用,所以我至少会编写一两个集成测试来模拟对Controller类的直接调用。我的大部分测试都是单元测试,主要针对诸如 ` GameStateon` 或 `off` 之类的功能GameRepository,但测试 API 逻辑是否正常运行也很有帮助。
在(您可以根据需要重命名)中UnitTest1.cs,我将修改现有测试,使其内容如下:
using MattEland.Starship.Logic;
using MattEland.Starship.ProcessingService.Controllers;
using NUnit.Framework;
namespace Tests
{
public class Tests
{
[Test]
public void Test1()
{
// Arrange
var controller = new GamesController();
// Act
var result = controller.NewGame();
// Assert
Assert.IsNotNull(result);
GameState state = (GameState) result.Value;
Assert.AreEqual(2, state.Id);
}
}
}
这会调用我的控制器上的 NewGame 方法(在我的例子中是 HTTP POST 方法),并检查结果,以确认是否已创建并返回了一个新游戏,并且该游戏的 ID 与我预期的一致。
请注意,为了支持直接引用控制器,我必须遵循 Visual Studio 的操作建议并添加对 的引用Microsoft.AspNetCore.Mvc.Core。
我还发现,在添加 NuGet 程序集引用之前,我的测试一直失败Microsoft.AspNetCore.MVC.Abstractions。具体操作方法是:在解决方案资源管理器中右键单击测试项目,选择“管理 NuGet 程序集...”,然后搜索程序集并选中后单击“安装”。
在这里,您可以点击顶部菜单中的“测试” ,然后点击“窗口”,再点击“文件资源管理器”来运行测试。这将打开测试窗格,您可以点击测试用例来运行选定的测试。
注意:您的用户界面可能与我的不同。我使用了ReSharper,它会在用户界面中添加额外的测试工具。
一个简单的单元测试,从GameRepository内部状态开始,可能如下所示:
using System.Linq;
using MattEland.Starship.Logic;
using NUnit.Framework;
namespace Tests
{
public class RepositoryTests
{
[Test]
public void RepositoryShouldStartWithGameState()
{
// Arrange
var repository = new GameRepository();
// Act
var games = repository.Games;
// Assert
Assert.Greater(games.Count(), 0);
}
}
}
结束
在本文中,我们创建了一个新的 API 项目、一个共享逻辑库和一个测试项目,并验证了一切功能正常。
虽然这个例子还有很多非常基础的地方,还有很大的改进空间,但它应该能帮助你入门。敬请期待后续文章,我们将探讨如何优化这个应用程序以及处理常见场景。ASP.NET Core 的知识点很多,但它确实是一个非常棒的 API 开发平台。
文章来源:https://dev.to/integerman/creating-a-net-core-api-3n6d






