关注爱学习公众号
扫码手机阅读更方便
本文内容 可在两种模式下发布使用 .NET Core 创建的应用程序,模式会影响用户运行应用的方式。将应用作为独立应用,生成的应用程序将包含 .NET Core 运行时和库,以及该应用程序及其依赖项。 应用程序的用户可以在未安装 .NET Core 运行时的
本文内容 作者:Daniel Roth、Rick Anderson 和 Shaun LuttinASP.NET Core 是一个跨平台的高性能开源框架,用于生成启用云且连接 Internet 的新式应用。 使用 ASP.NET Core,您可以:生成 W
备注 某些 shell 将 # 解释为特殊字符。 在这些情况下,请将语言参数值括在引号中。 例如 dotnet new console -lang "F#"。
“数据很宝贵,它的持续时间长于系统本身。” Tim Berners-Lee
本文内容 本教程介绍使用 SignalR 生成实时应用的基础知识。 您将学习如何:创建 Web 项目。添加 SignalR 客户端库。创建 SignalR 中心。配置项目以使用 SignalR。添加可将消息从任何客户端发送到所有连接客户端的代码。
“如果你不喜欢对产品进行单元测试,很可能你的客户也不喜欢这样做。” _- 匿名-
本文内容 本文介绍了 .NET Core 3.1 中的新增功能。 此版本包含对 .NET Core 3.0 的细微改进,重点介绍小型但重要的修复。 .NET Core 3.1 中最重要的特性为,它是长期支持 (LTS) 版本。如果使用的是 Visual Stu
备注 “开发”映像不包含应用程序二进制文件和其他内容,因为“调试”配置使用卷装载提供迭代编辑和调试体验 。 若要创建包含所有内容的生产映像,请使用“版本”配置 。
本文内容 本教程演示如何在 Visual Studio 2019 中创建和运行 .NET Core 控制台应用程序。先决条件安装了具有“.NET Core 跨平台开发”工作负载的 Visual Studio 2019 版本 16.6 或更高版本。 选择此工
本文内容 本教程介绍如何使用 .NET Core CLI 创建并运行 ASP.NET Core Web 应用。你将了解如何:创建 Web 应用项目。信任开发证书。运行应用。编辑 Razor 页面。最后,在本地计算机上运行工作 Web 应用。
作者:Rick Anderson、Kirk Larkin 和 Mike Wasson
本教程介绍使用 ASP.NET Core 构建 Web API 的基础知识。
在本教程中,你将了解:
在结束时,你会获得可以管理存储在数据库中的“待办事项”的 Web API。
本教程将创建以下 API:
API | 描述 | 请求正文 | 响应正文 |
---|---|---|---|
GET /api/TodoItems |
获取所有待办事项 | None | 待办事项的数组 |
GET /api/TodoItems/{id} |
按 ID 获取项 | None | 待办事项 |
POST /api/TodoItems |
添加新项 | 待办事项 | 待办事项 |
PUT /api/TodoItems/{id} |
更新现有项 ? | 待办事项 | None |
DELETE /api/TodoItems/{id} ? ? |
删除项 ?? | None | None |
下图显示了应用的设计。
Visual Studio Code 说明使用用于 ASP.NET Core 的 .NET Core CLI 开发功能,如项目创建。 可在任何平台(macOS、Linux 或 Windows)上或在任何代码编辑器中遵循这些说明。 如果使用 Visual Studio Code 以外的其他内容,则可能需要进行少量更改。 有关在 macOS 上安装 Visual Studio Code 的详细信息,请参阅macOS 上的 Visual Studio Code。
打开集成终端。
将目录 (cd
) 更改为包含项目文件夹的文件夹。
运行以下命令:
dotnet new webapi -o TodoApi
cd TodoApi
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.InMemory
code -r ../TodoApi
当对话框询问是否要将所需资产添加到项目时,选择“是”****。
前面的命令:
选择“文件”>“新建解决方案”**** ****。
在版本 8.6 之前的 Visual Studio for Mac 中,依次选择“.NET Core” > “应用” > “API” > “下一步”**** **** **** ****。 在版本 8.6 或更高版本中,依次选择“Web 和控制台” > “应用” > “API” > “下一步”。**** **** **** ****
确认“目标框架”设置为“.NET Core 3.1”**** ****。 选择“下一步”****。
输入“TodoApi”** 作为“项目名称”****,然后选择“创建”****。
首次访问 Mac 上的命令终端需要以下设置配置:
上述说明以两种方式实现访问命令终端:从 Visual Studio 内访问或通过查找器访问 。
在项目文件夹中打开命令终端并运行以下命令:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.InMemory
项目模板会创建 WeatherForecast
API。 从浏览器调用 Get
方法以测试应用。
按 Ctrl+F5 运行应用。 Visual Studio 启动浏览器并导航到 https://localhost:<port>/WeatherForecast
,其中 <port>
是随机选择的端口号。
如果出现询问是否应信任 IIS Express 证书的对话框,则选择“是”****。 在接下来出现的“安全警告”**** 对话框中,选择“是”****。
按 Ctrl+F5 运行应用。 在浏览器中,转到以下 URL:https://localhost:5001/WeatherForecast
。
选择“运行”**** > “开始调试”**** 以启动应用。 Visual Studio for Mac 会启动浏览器并导航到 https://localhost:<port>
,其中 <port>
是随机选择的端口号。 将返回 HTTP 404(未找到)错误。 将 /WeatherForecast
追加到 URL(将 URL 更改为 https://localhost:<port>/WeatherForecast
)。
返回类似于以下项的 JSON:
[
{
"date": "2019-07-16T19:04:05.7257911-06:00",
"temperatureC": 52,
"temperatureF": 125,
"summary": "Mild"
},
{
"date": "2019-07-17T19:04:05.7258461-06:00",
"temperatureC": 36,
"temperatureF": 96,
"summary": "Warm"
},
{
"date": "2019-07-18T19:04:05.7258467-06:00",
"temperatureC": 39,
"temperatureF": 102,
"summary": "Cool"
},
{
"date": "2019-07-19T19:04:05.7258471-06:00",
"temperatureC": 10,
"temperatureF": 49,
"summary": "Bracing"
},
{
"date": "2019-07-20T19:04:05.7258474-06:00",
"temperatureC": -1,
"temperatureF": 31,
"summary": "Chilly"
}
]
模型** 是一组表示应用管理的数据的类。 此应用的模型是单个 TodoItem
类。
在“解决方案资源管理器”**** 中,右键单击项目。 选择“添加”**** > “新建文件夹”****。 将文件夹命名为“Models”**。
右键单击“Models”** 文件夹,然后选择“添加”**** > “类”****。 将类命名为 TodoItem,然后选择“添加”******。
将模板代码替换为以下代码:
添加名为“Models”的文件夹**。
使用以下代码将 TodoItem
类添加到 Models** 文件夹:
右键单击项目。 选择“添加”**** > “新建文件夹”****。 将文件夹命名为“Models”**。
右键单击“Models”** 文件夹,然后选择“添加”****>“新建文件”****>“常规”****>“空类”****。
将类命名为“TodoItem”,然后单击“新建”******。
将模板代码替换为以下代码:
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
Id
属性用作关系数据库中的唯一键。
模型类可位于项目的任意位置,但按照惯例会使用 Models** 文件夹。
数据库上下文是为数据模型协调 Entity Framework 功能的主类**。 此类由 Microsoft.EntityFrameworkCore.DbContext
类派生而来。
Microsoft.EntityFrameworkCore.InMemory
NuGet 包。TodoContext
类添加到“Models”** 文件夹。输入以下代码:
using Microsoft.EntityFrameworkCore;
namespace TodoApi.Models
{
public class TodoContext : DbContext
{
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
{
}
public DbSet<TodoItem> TodoItems { get; set; }
}
}
在 ASP.NET Core 中,服务(如数据库上下文)必须向依赖关系注入 (DI) 容器进行注册。 该容器向控制器提供服务。
使用以下突出显示的代码更新 Startup.cs**:
// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
namespace TodoApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
前面的代码:
using
声明。右键单击 Controllers** 文件夹。
选择“添加”>“新建构建项”**** ****。
选择“其操作使用实体框架的 API 控制器”,然后选择“添加”**** ****。
在“添加其操作使用实体框架的 API 控制器”对话框中****:
运行以下命令:
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet tool install --global dotnet-aspnet-codegenerator
dotnet aspnet-codegenerator controller -name TodoItemsController -async -api -m TodoItem -dc TodoContext -outDir Controllers
前面的命令:
dotnet-aspnet-codegenerator
)。TodoItemsController
。生成的代码:
[ApiController]
属性标记类。 此属性指示控制器响应 Web API 请求。 有关该属性启用的特定行为的信息,请参阅 使用 ASP.NET Core 创建 Web API。TodoContext
) 注入到控制器中。 数据库上下文将在控制器中的每个 CRUD 方法中使用。ASP.NET Core 模板:
[action]
。[action]
。如果 [action]
标记不在路由模板中,则从路由中排除操作名称。 也就是说,不会在匹配的路由中使用操作的关联方法名称。
替换 PostTodoItem
中的返回语句,以使用 nameof 运算符:
// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
//return CreatedAtAction("GetTodoItem", new { id = todoItem.Id }, todoItem);
return CreatedAtAction(nameof(GetTodoItem), new { id = todoItem.Id }, todoItem);
}
前面的代码是 HTTP POST 方法,如 [HttpPost]
属性所指示。 该方法从 HTTP 请求正文获取待办事项的值。
CreatedAtAction 方法:
Location
标头指定新建的待办事项的 URI。 有关详细信息,请参阅创建的 10.2.2 201。GetTodoItem
操作以创建 Location
标头的 URI。 C# nameof
关键字用于避免在 CreatedAtAction
调用中硬编码操作名称。本教程使用 Postman 测试 Web API。
警告
在测试控制器之后重新启用 SSL 证书验证。
创建新请求。
将 HTTP 方法设置为 POST
。
选择“正文”选项卡****。
选择“原始”单选按钮****。
将类型设置为 JSON (application/json)
在请求正文中,输入待办事项的 JSON:
{
"name":"walk dog",
"isComplete":true
}
选择Send。
在Headers 窗格中选择Response 选项卡。
复制Location 标头值:
将方法设置为“GET”。
粘贴 URI(例如,https://localhost:5001/api/TodoItems/1
)。
选择Send。
这些方法实现两个 GET 终结点:
GET /api/TodoItems
GET /api/TodoItems/{id}
通过从浏览器或 Postman 调用两个终结点来测试应用。 例如:
https://localhost:5001/api/TodoItems
https://localhost:5001/api/TodoItems/1
对 GetTodoItems
的调用生成类似于以下项的响应:
[
{
"id": 1,
"name": "Item1",
"isComplete": false
}
]
https://localhost:<port>/api/TodoItems
。 例如 https://localhost:5001/api/TodoItems
。此应用使用内存中数据库。 如果应用已停止并启动,则前面的 GET 请求将不会返回任何数据。 如果未返回任何数据,将数据 POST 到应用。
[HttpGet]
属性表示响应 HTTP GET 请求的方法。 每个方法的 URL 路径构造如下所示:
在控制器的 Route
属性中以模板字符串开头:
[Route("api/[controller]")]
[ApiController]
public class TodoItemsController : ControllerBase
{
private readonly TodoContext _context;
public TodoItemsController(TodoContext context)
{
_context = context;
}
将 [controller]
替换为控制器的名称,按照惯例,在控制器类名称中去掉“Controller”后缀。 对于此示例,控制器类名称为“TodoItems”控制器,因此控制器名称为“TodoItems”****。 ASP.NET Core 路由不区分大小写。
如果 [HttpGet]
属性具有路由模板(例如 [HttpGet("products")]
),则将它追加到路径。 此示例不使用模板。 有关详细信息,请参阅使用 Http [Verb] 特性的特性路由。
在下面的 GetTodoItem
方法中,"{id}"
是待办事项的唯一标识符的占位符变量。 调用 GetTodoItem
时,URL 中 "{id}"
的值会在 id
参数中提供给方法。
// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
GetTodoItems
和 GetTodoItem
方法的返回类型是 ActionResult<T> 类型。 ASP.NET Core 自动将对象序列化为 JSON,并将 JSON 写入响应消息的正文中。 在假设没有未经处理的异常的情况下,此返回类型的响应代码为 200。 未经处理的异常将转换为 5xx 错误。
ActionResult
返回类型可以表示大范围的 HTTP 状态代码。 例如,GetTodoItem
可以返回两个不同的状态值:
item
则产生 HTTP 200 响应。检查 PutTodoItem
方法:
// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
if (id != todoItem.Id)
{
return BadRequest();
}
_context.Entry(todoItem).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
PutTodoItem
与 PostTodoItem
类似,但是使用的是 HTTP PUT。 响应是 204(无内容)。 根据 HTTP 规范,PUT 请求需要客户端发送整个更新的实体,而不仅仅是更改。 若要支持部分更新,请使用 HTTP PATCH。
如果在调用 PutTodoItem
时出错,请调用 GET
以确保数据库中有项目。
本示例使用内存内、数据库,每次启动应用时都必须对其进行初始化。 在进行 PUT 调用之前,数据库中必须有一个项。 调用 GET,以确保在调用 PUT 之前数据库中存在项目。
更新 ID = 1 的待办事项并将其名称设置为“feed fish”:
{
"ID":1,
"name":"feed fish",
"isComplete":true
}
下图显示 Postman 更新:
检查 DeleteTodoItem
方法:
// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<ActionResult<TodoItem>> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return todoItem;
}
使用 Postman 删除待办事项:
DELETE
。https://localhost:5001/api/TodoItems/1
。目前,示例应用公开了整个 TodoItem
对象。 生产应用通常使用模型的子集来限制输入和返回的数据。 这背后有多种原因,但安全性是主要原因。 模型的子集通常称为数据传输对象 (DTO)、输入模型或视图模型。 本文使用的是 DTO。
DTO 可用于:
若要演示 DTO 方法,请更新 TodoItem
类,使其包含机密字段:
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
public string Secret { get; set; }
}
此应用需要隐藏机密字段,但管理应用可以选择公开它。
确保可以发布和获取机密字段。
创建 DTO 模型:
public class TodoItemDTO
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
更新 TodoItemsController
以使用 TodoItemDTO
:
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItemDTO>>> GetTodoItems()
{
return await _context.TodoItems
.Select(x => ItemToDTO(x))
.ToListAsync();
}
[HttpGet("{id}")]
public async Task<ActionResult<TodoItemDTO>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return ItemToDTO(todoItem);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
{
if (id != todoItemDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoItemDTO.Name;
todoItem.IsComplete = todoItemDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}
[HttpPost]
public async Task<ActionResult<TodoItemDTO>> CreateTodoItem(TodoItemDTO todoItemDTO)
{
var todoItem = new TodoItem
{
IsComplete = todoItemDTO.IsComplete,
Name = todoItemDTO.Name
};
_context.TodoItems.Add(todoItem);
await _context.SaveChangesAsync();
return CreatedAtAction(
nameof(GetTodoItem),
new { id = todoItem.Id },
ItemToDTO(todoItem));
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
private bool TodoItemExists(long id) =>
_context.TodoItems.Any(e => e.Id == id);
private static TodoItemDTO ItemToDTO(TodoItem todoItem) =>
new TodoItemDTO
{
Id = todoItem.Id,
Name = todoItem.Name,
IsComplete = todoItem.IsComplete
};
}
确保无法发布或获取机密字段。
在本教程中,你将了解:
在结束时,你会获得可以管理存储在关系数据库中的“待办事项”的 Web API。
本教程将创建以下 API:
API | 描述 | 请求正文 | 响应正文 |
---|---|---|---|
GET /api/TodoItems | 获取所有待办事项 | None | 待办事项的数组 |
GET /api/TodoItems/{id} | 按 ID 获取项 | None | 待办事项 |
POST /api/TodoItems | 添加新项 | 待办事项 | 待办事项 |
PUT /api/TodoItems/{id} | 更新现有项 ? | 待办事项 | None |
DELETE /api/TodoItems/{id} ? ? | 删除项 ?? | None | None |
下图显示了应用的设计。
警告
如果使用 Visual Studio 2017,请参阅 dotnet/sdk 问题 #3124,以了解无法与 Visual Studio 一起使用的 .NET Core SDK 版本的信息。
Visual Studio Code 说明使用用于 ASP.NET Core 的 .NET Core CLI 开发功能,如项目创建。 可在任何平台(macOS、Linux 或 Windows)上或在任何代码编辑器中遵循这些说明。 如果使用 Visual Studio Code 以外的其他内容,则可能需要进行少量更改。
打开集成终端。
将目录 (cd
) 更改为包含项目文件夹的文件夹。
运行以下命令:
dotnet new webapi -o TodoApi
code -r TodoApi
这些命令会创建新 Web API 项目并在新项目文件夹中打开 Visual Studio Code 的新实例。
当对话框询问是否要将所需资产添加到项目时,选择“是”****。
选择“文件”>“新建解决方案”**** ****。
在版本 8.6 之前的 Visual Studio for Mac 中,依次选择“.NET Core” > “应用” > “API” > “下一步”**** **** **** ****。 在版本 8.6 或更高版本中,依次选择“Web 和控制台” > “应用” > “API” > “下一步”。**** **** **** ****
在“配置新的 ASP.NET Core Web API”**** 对话框中,接受默认的 *.NET Core 2.2**“目标框架”****。
输入“TodoApi”** 作为“项目名称”****,然后选择“创建”****。
项目模板会创建 values
API。 从浏览器调用 Get
方法以测试应用。
按 Ctrl+F5 运行应用。 Visual Studio 启动浏览器并导航到 https://localhost:<port>/api/values
,其中 <port>
是随机选择的端口号。
如果出现询问是否应信任 IIS Express 证书的对话框,则选择“是”****。 在接下来出现的“安全警告”**** 对话框中,选择“是”****。
按 Ctrl+F5 运行应用。 在浏览器中,转到以下 URL:https://localhost:5001/api/values
。
选择“运行”**** > “开始调试”**** 以启动应用。 Visual Studio for Mac 会启动浏览器并导航到 https://localhost:<port>
,其中 <port>
是随机选择的端口号。 将返回 HTTP 404(未找到)错误。 将 /api/values
追加到 URL(将 URL 更改为 https://localhost:<port>/api/values
)。
会返回以下 JSON:
["value1","value2"]
模型** 是一组表示应用管理的数据的类。 此应用的模型是单个 TodoItem
类。
在“解决方案资源管理器”**** 中,右键单击项目。 选择“添加”**** > “新建文件夹”****。 将文件夹命名为“Models”**。
右键单击“Models”** 文件夹,然后选择“添加”**** > “类”****。 将类命名为 TodoItem,然后选择“添加”******。
将模板代码替换为以下代码:
添加名为“Models”的文件夹**。
使用以下代码将 TodoItem
类添加到 Models** 文件夹:
右键单击项目。 选择“添加”**** > “新建文件夹”****。 将文件夹命名为“Models”**。
右键单击“Models”** 文件夹,然后选择“添加”****>“新建文件”****>“常规”****>“空类”****。
将类命名为“TodoItem”,然后单击“新建”******。
将模板代码替换为以下代码:
namespace TodoApi.Models
{
public class TodoItem
{
public long Id { get; set; }
public string Name { get; set; }
public bool IsComplete { get; set; }
}
}
Id
属性用作关系数据库中的唯一键。
模型类可位于项目的任意位置,但按照惯例会使用 Models** 文件夹。
数据库上下文是为数据模型协调 Entity Framework 功能的主类**。 此类由 Microsoft.EntityFrameworkCore.DbContext
类派生而来。
TodoContext
类添加到“Models”** 文件夹。将模板代码替换为以下代码:
using Microsoft.EntityFrameworkCore;
namespace TodoApi.Models
{
public class TodoContext : DbContext
{
public TodoContext(DbContextOptions<TodoContext> options)
: base(options)
{
}
public DbSet<TodoItem> TodoItems { get; set; }
}
}
在 ASP.NET Core 中,服务(如数据库上下文)必须向依赖关系注入 (DI) 容器进行注册。 该容器向控制器提供服务。
使用以下突出显示的代码更新 Startup.cs**:
// Unused usings removed
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using TodoApi.Models;
namespace TodoApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the
//container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TodoContext>(opt =>
opt.UseInMemoryDatabase("TodoList"));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP
//request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for
// production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
前面的代码:
using
声明。右键单击 Controllers** 文件夹。
选择“添加”>“新项” **** ****。
在“添加新项”对话框中,选择“API 控制器类”模板**** ****。
将类命名为 TodoController,然后选择“添加”******。
TodoController
的类**。将模板代码替换为以下代码:
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using TodoApi.Models;
namespace TodoApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TodoController : ControllerBase
{
private readonly TodoContext _context;
public TodoController(TodoContext context)
{
_context = context;
if (_context.TodoItems.Count() == 0)
{
// Create a new TodoItem if collection is empty,
// which means you can't delete all TodoItems.
_context.TodoItems.Add(new TodoItem { Name = "Item1" });
_context.SaveChanges();
}
}
}
}
前面的代码:
[ApiController]
属性标记类。 此属性指示控制器响应 Web API 请求。 有关该属性启用的特定行为的信息,请参阅 使用 ASP.NET Core 创建 Web API。TodoContext
) 注入到控制器中。 数据库上下文将在控制器中的每个 CRUD 方法中使用。Item1
的项添加到数据库。 此代码位于构造函数中,因此在每次出现新 HTTP 请求时运行。 如果删除所有项,则构造函数会在下次调用 API 方法时再次创建 Item1
。 因此删除可能看上去不起作用,不过实际上确实有效。若要提供检索待办事项的 API,请将以下方法添加到 TodoController
类中:
// GET: api/Todo
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
{
return await _context.TodoItems.ToListAsync();
}
// GET: api/Todo/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
这些方法实现两个 GET 终结点:
GET /api/todo
GET /api/todo/{id}
如果应用仍在运行,请停止它。 然后再次运行它以包括最新更改。
通过从浏览器调用两个终结点来测试应用。 例如:
https://localhost:<port>/api/todo
https://localhost:<port>/api/todo/1
以下 HTTP 响应通过调用 GetTodoItems
来生成:
[
{
"id": 1,
"name": "Item1",
"isComplete": false
}
]
[HttpGet]
属性表示响应 HTTP GET 请求的方法。 每个方法的 URL 路径构造如下所示:
在控制器的 Route
属性中以模板字符串开头:
namespace TodoApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TodoController : ControllerBase
{
private readonly TodoContext _context;
将 [controller]
替换为控制器的名称,按照惯例,在控制器类名称中去掉“Controller”后缀。 对于此示例,控制器类名称为“Todo”控制器,因此控制器名称为“todo”****。 ASP.NET Core 路由不区分大小写。
如果 [HttpGet]
属性具有路由模板(例如 [HttpGet("products")]
),则将它追加到路径。 此示例不使用模板。 有关详细信息,请参阅使用 Http [Verb] 特性的特性路由。
在下面的 GetTodoItem
方法中,"{id}"
是待办事项的唯一标识符的占位符变量。 调用 GetTodoItem
时,URL 中 "{id}"
的值会在 id
参数中提供给方法。
// GET: api/Todo/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
GetTodoItems
和 GetTodoItem
方法的返回类型是 ActionResult<T> 类型。 ASP.NET Core 自动将对象序列化为 JSON,并将 JSON 写入响应消息的正文中。 在假设没有未经处理的异常的情况下,此返回类型的响应代码为 200。 未经处理的异常将转换为 5xx 错误。
ActionResult
返回类型可以表示大范围的 HTTP 状态代码。 例如,GetTodoItem
可以返回两个不同的状态值:
item
则产生 HTTP 200 响应。本教程使用 Postman 测试 Web API。
警告
在测试控制器之后重新启用 SSL 证书验证。
https://localhost:<port>/api/todo
。 例如 https://localhost:5001/api/todo
。在 Controllers / TodoController.cs 中添加以下 PostTodoItem
方法**:
// POST: api/Todo
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem item)
{
_context.TodoItems.Add(item);
await _context.SaveChangesAsync();
return CreatedAtAction(nameof(GetTodoItem), new { id = item.Id }, item);
}
前面的代码是 HTTP POST 方法,如 [HttpPost]
属性所指示。 该方法从 HTTP 请求正文获取待办事项的值。
CreatedAtAction
方法:
如果成功,则返回 HTTP 201 状态代码。 HTTP 201 是在服务器上创建新资源的 HTTP POST 方法的标准响应。
将 Location
标头添加到响应。 Location
标头指定新建的待办事项的 URI。 有关详细信息,请参阅创建的 10.2.2 201。
引用 GetTodoItem
操作以创建 Location
标头的 URI。 C# nameof
关键字用于避免在 CreatedAtAction
调用中硬编码操作名称。
// GET: api/Todo/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
return todoItem;
}
生成项目。
在 Postman 中,将 HTTP 方法设置为 POST
。
选择“正文”选项卡****。
选择“原始”单选按钮****。
将类型设置为 JSON (application/json)
在请求正文中,输入待办事项的 JSON:
{
"name":"walk dog",
"isComplete":true
}
选择Send。
如果收到 405 不允许的方法错误,则可能是由于未在添加 PostTodoItem
方法之后编译项目。
在Headers 窗格中选择Response 选项卡。
复制Location 标头值:
将方法设置为“GET”。
粘贴 URI(例如,https://localhost:5001/api/Todo/2
)。
选择Send。
添加以下 PutTodoItem
方法:
// PUT: api/Todo/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem item)
{
if (id != item.Id)
{
return BadRequest();
}
_context.Entry(item).State = EntityState.Modified;
await _context.SaveChangesAsync();
return NoContent();
}
PutTodoItem
与 PostTodoItem
类似,但是使用的是 HTTP PUT。 响应是 204(无内容)。 根据 HTTP 规范,PUT 请求需要客户端发送整个更新的实体,而不仅仅是更改。 若要支持部分更新,请使用 HTTP PATCH。
如果在调用 PutTodoItem
时出错,请调用 GET
以确保数据库中有项目。
本示例使用内存内、数据库,每次启动应用时都必须对其进行初始化。 在进行 PUT 调用之前,数据库中必须有一个项。 调用 GET,以确保在调用 PUT 之前数据库中存在项目。
更新 id = 1 的待办事项并将其名称设置为“feed fish”:
{
"ID":1,
"name":"feed fish",
"isComplete":true
}
下图显示 Postman 更新:
添加以下 DeleteTodoItem
方法:
// DELETE: api/Todo/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
DeleteTodoItem
响应是 204(无内容)。
使用 Postman 删除待办事项:
DELETE
。https://localhost:5001/api/todo/1
。可通过示例应用删除所有项。 但如果删除最后一项,则在下次调用 API 时,模型类构造函数会创建一个新项。
在本部分中,将添加一个 HTML 页面,该页面使用 JavaScript 调用 Web API。 jQuery 可启动该请求。 JavaScript 会使用 Web API 响应的详细信息来更新页面。
通过下面突出显示的代码更新 Startup.cs,配置应用来提供静态文件并实现默认文件映射**:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for
// production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseMvc();
}
在项目目录中创建 wwwroot** 文件夹。
将一个名为 index.html 的 HTML 文件添加到 wwwroot 目录** **。 用以下标记替代其内容:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>To-do CRUD</title>
<style>
input[type='submit'], button, [aria-label] {
cursor: pointer;
}
#spoiler {
display: none;
}
table {
font-family: Arial, sans-serif;
border: 1px solid;
border-collapse: collapse;
}
th {
background-color: #0066CC;
color: white;
}
td {
border: 1px solid;
padding: 5px;
}
</style>
</head>
<body>
<h1>To-do CRUD</h1>
<h3>Add</h3>
<form action="javascript:void(0);" method="POST" onsubmit="addItem()">
<input type="text" id="add-name" placeholder="New to-do">
<input type="submit" value="Add">
</form>
<div id="spoiler">
<h3>Edit</h3>
<form class="my-form">
<input type="hidden" id="edit-id">
<input type="checkbox" id="edit-isComplete">
<input type="text" id="edit-name">
<input type="submit" value="Save">
<a onclick="closeInput()" aria-label="Close">✖</a>
</form>
</div>
<p id="counter"></p>
<table>
<tr>
<th>Is Complete</th>
<th>Name</th>
<th></th>
<th></th>
</tr>
<tbody id="todos"></tbody>
</table>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="site.js"></script>
</body>
</html>
将名为 site.js 的 JavaScript 文件添加到 wwwroot 目录** **。 用以下代码替代其内容:
const uri = "api/todo";
let todos = null;
function getCount(data) {
const el = $("#counter");
let name = "to-do";
if (data) {
if (data > 1) {
name = "to-dos";
}
el.text(data + " " + name);
} else {
el.text("No " + name);
}
}
$(document).ready(function() {
getData();
});
function getData() {
$.ajax({
type: "GET",
url: uri,
cache: false,
success: function(data) {
const tBody = $("#todos");
$(tBody).empty();
getCount(data.length);
$.each(data, function(key, item) {
const tr = $("<tr></tr>")
.append(
$("<td></td>").append(
$("<input/>", {
type: "checkbox",
disabled: true,
checked: item.isComplete
})
)
)
.append($("<td></td>").text(item.name))
.append(
$("<td></td>").append(
$("<button>Edit</button>").on("click", function() {
editItem(item.id);
})
)
)
.append(
$("<td></td>").append(
$("<button>Delete</button>").on("click", function() {
deleteItem(item.id);
})
)
);
tr.appendTo(tBody);
});
todos = data;
}
});
}
function addItem() {
const item = {
name: $("#add-name").val(),
isComplete: false
};
$.ajax({
type: "POST",
accepts: "application/json",
url: uri,
contentType: "application/json",
data: JSON.stringify(item),
error: function(jqXHR, textStatus, errorThrown) {
alert("Something went wrong!");
},
success: function(result) {
getData();
$("#add-name").val("");
}
});
}
function deleteItem(id) {
$.ajax({
url: uri + "/" + id,
type: "DELETE",
success: function(result) {
getData();
}
});
}
function editItem(id) {
$.each(todos, function(key, item) {
if (item.id === id) {
$("#edit-name").val(item.name);
$("#edit-id").val(item.id);
$("#edit-isComplete")[0].checked = item.isComplete;
}
});
$("#spoiler").css({ display: "block" });
}
$(".my-form").on("submit", function() {
const item = {
name: $("#edit-name").val(),
isComplete: $("#edit-isComplete").is(":checked"),
id: $("#edit-id").val()
};
$.ajax({
url: uri + "/" + $("#edit-id").val(),
type: "PUT",
accepts: "application/json",
contentType: "application/json",
data: JSON.stringify(item),
success: function(result) {
getData();
}
});
closeInput();
return false;
});
function closeInput() {
$("#spoiler").css({ display: "none" });
}
可能需要更改 ASP.NET Core 项目的启动设置,以便对 HTML 页面进行本地测试:
launchUrl
以便在项目的默认文件 index.html 强制打开应用**。此示例调用 Web API 的所有 CRUD 方法。 以下是 API 调用的说明。
jQuery 将向 Web API 发送 HTTP GET 请求,该 API 返回表示待办事项的数组的 JSON。 如果请求成功,则调用 success
回调函数。 在该回调中使用待办事项信息更新 DOM。
$(document).ready(function() {
getData();
});
function getData() {
$.ajax({
type: "GET",
url: uri,
cache: false,
success: function(data) {
const tBody = $("#todos");
$(tBody).empty();
getCount(data.length);
$.each(data, function(key, item) {
const tr = $("<tr></tr>")
.append(
$("<td></td>").append(
$("<input/>", {
type: "checkbox",
disabled: true,
checked: item.isComplete
})
)
)
.append($("<td></td>").text(item.name))
.append(
$("<td></td>").append(
$("<button>Edit</button>").on("click", function() {
editItem(item.id);
})
)
)
.append(
$("<td></td>").append(
$("<button>Delete</button>").on("click", function() {
deleteItem(item.id);
})
)
);
tr.appendTo(tBody);
});
todos = data;
}
});
}
jQuery 发送 HTTP POST 请求,请求正文中包含待办事项。 将 accepts
和 contentType
选项设置为 application/json
,以便指定接收和发送的媒体类型。 待办事项使用 JSON.stringify 转换为 JSON。 当 API 返回成功状态的代码时,将调用 getData
函数来更新 HTML 表。
function addItem() {
const item = {
name: $("#add-name").val(),
isComplete: false
};
$.ajax({
type: "POST",
accepts: "application/json",
url: uri,
contentType: "application/json",
data: JSON.stringify(item),
error: function(jqXHR, textStatus, errorThrown) {
alert("Something went wrong!");
},
success: function(result) {
getData();
$("#add-name").val("");
}
});
}
更新待办事项与添加类似。 url
更改为添加项的唯一标识符,并且 type
为 PUT
。
$.ajax({
url: uri + "/" + $("#edit-id").val(),
type: "PUT",
accepts: "application/json",
contentType: "application/json",
data: JSON.stringify(item),
success: function(result) {
getData();
}
});
若要删除待办事项,请将 AJAX 调用上的 type
设为 DELETE
并指定该项在 URL 中的唯一标识符。
$.ajax({
url: uri + "/" + id,
type: "DELETE",
success: function(result) {
getData();
}
});
ASP.NET Core 标识将用户界面 (UI) 登录功能添加到 ASP.NET Core Web 应用。 若要保护 Web API 和 SPA,请使用以下项之一:
IdentityServer4 是适用于 ASP.NET Core 的 OpenID Connect 和 OAuth 2.0 框架。 IdentityServer4 支持以下安全功能:
有关详细信息,请参阅欢迎使用 IdentityServer4。
查看或下载本教程的示例代码。 请参阅如何下载。
有关更多信息,请参见以下资源: