在Mac下创建ASP.NET Core Web API

这系列文章是参考了.NET Core文档和源码,可能有人要问,直接看官方的英文文档不就可以了吗,为什么还要写这些文章呢?

原因如下:

  • 官方文档涉及的内容相当全面,属于那种大而全的知识仓库,不太适合初学者,很容易让人失去重要,让人掉入到具体的细节之中。
  • 对于大多数人来讲开发语言只是工具,程序员都有一个通病,就是死磕工具,把工具学深。我认为在工具上没有必要投入太多时间,以能高效地完成日常的工作项目为准即可。要需求驱动学习,你需要什么学什么。如果你学的新技术新特性只是屠龙之技或者只需要用到的时候去查一下即可的话,这种死磕这又有什么用。没有必要花120%的时间去学100%的知识,你只需要花20%的时间去学习80%的知识就可以了,剩下的等实际的项目中用到的时候去查就可以了,工具只是工具,不是工作本身。
  • 目前基本所有的文章都是基于Windows平台的Visual Studio IDE来介绍的。而我用的是一台Mac,所以我将基于Mac平台的Visual Studio Code讲解适合我们实际项目中遇到的知识。
  • 还有一点,就是这是我个人的学习总结。

这系列文章就是让你去花20%的时间去学80%的东西,剩下的20%再去看官方文档。

在.NET Core里面MVC和WebAPI两者被整合成一个框架,分享同一套代码和管线。这样我们就可以更方便地开发MVC应用程序和Web API接口。

创建项目

在这篇文章中我们将要创建的API如下:

API 描述
GET /api/user 获取所有的用户信息
GET /api/user/{id} 根据ID获取指定的用户
POST /api/user 添加新的用户
PUT /api/user/{id} 更新用户信息
PATCH /api/user/{id} 更新用户信息
DELETE /api/user/{id} 删除用户信息

根据上一篇文章,我们通过Yeoman创建一个WebAPI项目,命名为UserWebAPI:

添加模型类

然后在项目根目录下面新建一个Models文件夹,在该文件夹下面利用yo aspnet:class UserItem新建一个UserItem类。

namespace UserWebAPI.Models
{
public class UserItem
{
public string Key { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}

添加仓储类

Repository类是封装了数据层的对象,包含了获取数据、并映射到实体模型类的业务逻辑。

首先我们在Models文件夹下面定义一个IUserRepositoryrepository接口。

通过运行yo aspnet:interface IUserRepository命令来创建该接口。

namespace UserWebAPI.Models
{
public interface IUserRepository
{
void Add(UserItem item);
IEnumerable<UserItem> GetAll();
UserItem Find(string key);
UserItem Remove(string key);
void Update(UserItem item);
}
}

接着再添加一个UserRepository类来实现IUserRepository接口。

namespace UserWebAPI.Models
{
public class UserRepository : IUserRepository
{
private static ConcurrentDictionary<string, UserItem> _users
= new ConcurrentDictionary<string, UserItem>(); public UserRepository()
{
Add(new UserItem { Name = "Charlie", Age = 18 });
} public void Add(UserItem item)
{
item.Key = Guid.NewGuid().ToString();
_users[item.Key] = item;
} public UserItem Find(string key)
{
UserItem user;
_users.TryGetValue(key, out user);
return user;
} public IEnumerable<UserItem> GetAll()
{
return _users.Values;
} public UserItem Remove(string key)
{
UserItem user;
_users.TryRemove(key, out user);
return user;
} public void Update(UserItem item)
{
_users[item.Key] = item;
}
}
}

注册仓储

通过定义一个repository接口,我们从使用它的MVC Controller来解耦该repository类。我们在此将通过注入一个UserRepository来代替直接在Controller里面实例化一个UserRepository类。

为了注入一个repository到controller,我们必须通过DI容器来注册它,打开Startup.cs文件,在ConfigureServices方法添加如下代码:

添加控制器

控制器是用于处理HTTP请求并创建HTTP响应的对象,这里通过运行yo aspnet:webapicontroller UserController命令生成UserController控制器。

namespace UserWebAPI.Controllers
{
[Route("api/[controller]")]
public class UserController : Controller
{
public IUserRepository UserItems { get; set; } public UserController(IUserRepository userItems)
{
UserItems = userItems;
}
}
}

获取用户信息

[HttpGet]
public IEnumerable<UserItem> GetAll()
{
return UserItems.GetAll();
} [HttpGet("{id}", Name = "GetUser")]
public IActionResult GetById(string id)
{
var item = UserItems.Find(id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}

上述两个方法实现了两个GET方法:

  • GET /api/user
  • GET /api/user/{id}

运行dotnet restoredotnet run之后,应用程序将会在本机启动,并在http://localhost:5000上开启监听服务。

然后在Postman上测试你的API接口是否能正确运行。

GetById方法中:

[HttpGet("{id}", Name = "GetTodo")]
public IActionResult GetById(string id)

其中"{id}"是UserItem的ID占位符,当GetById被调用时,URL中的“{id}”值会被分配给该方法的id参数。

Name = "GetTodo"创建了一个命名的路由,并允许你在HTTP响应中链接到该路由。

GetAll方法返回了一个IEnumerable,MVC会自动将对象序列化成JSON并将JSON写入到响应消息的正文中。该方法的响应状态码为200,假设没有发生任何未处理异常。

GetById方法返回的是一个更为通用的IActionResult类型。该方法有两种不同的返回类型:

  • 如果没有项匹配指定的请求ID,该方法通过返回NotFound表示一个404错误。
  • 否则,该方法返回一个JSON响应正文和200响应码,通过返回ObjectResult来表示。

添加新用户

[HttpPost]
public IActionResult Create([FromBody]UserItem item)
{
if (item == null)
{
return BadRequest();
}
UserItems.Add(item);
return CreatedAtRoute("GetUser", new { id = item.Key }, item);
}

通过[HttpPost] attribute 标明这个一个HTTP POST方法,[FromBody] attribute 告诉MVC从HTTP 请求的正文中获取用户UserItem值。

CreatedAtRoute方法返回一个201响应状态码(实际上是CreatedAtRouteResult对象),201状态码是通过POST方法在服务器上成功创建了一个新的资源时的标准响应码。CreateAtRoute也在响应里面添加了一个Location头信息,这个头信息指定了最新创建的User URI。

/// <summary>
/// Creates a <see cref="CreatedAtRouteResult"/> object that produces a Created (201) response.
/// </summary>
/// <param name="routeName">The name of the route to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The content value to format in the entity body.</param>
/// <returns>The created <see cref="CreatedAtRouteResult"/> for the response.</returns>
[NonAction]
public virtual CreatedAtRouteResult CreatedAtRoute(string routeName, object routeValues,
object value)
{
return new CreatedAtRouteResult(routeName, routeValues, value);
}
/// <summary>
/// Initializes a new instance of the <see cref="CreatedAtRouteResult"/> class with the values
/// provided.
/// </summary>
/// <param name="routeName">The name of the route to use for generating the URL.</param>
/// <param name="routeValues">The route data to use for generating the URL.</param>
/// <param name="value">The value to format in the entity body.</param>
public CreatedAtRouteResult(string routeName, object routeValues, object value)
: base(value)
{
RouteName = routeName;
RouteValues = routeValues == null ? null : new RouteValueDictionary(routeValues);
StatusCode = StatusCodes.Status201Created;
}

通过查看CreatedAtRouteResult的构造函数可以看到StatusCode(从ObjectResult对象继承而来)被直接设置成了Status201Created枚举值。

201状态码是当你在用POST/PUT在服务器端成功创建了一个新的资源时,服务器就应当返回201 Created同时在响应头添加一个Location来指定刚刚创建好的资源的URI。

通过Postman来发送Create请求

刚服务器接收到请求,会在VS Code的控制台显示出相应的信息:

点击Headers tab可以看到Location的值显示刚刚创建好的资源的URI。

更新用户信息(HTTP PUT)

[HttpPut("{id}")]
public IActionResult Update(string id, [FromBody] UserItem item)
{
if (item == null || item.Key != id)
{
return BadRequest();
}
var user=UserItems.Find(id);
if(user==null)
{
return NotFound();
} UserItems.Update(item);
return new NoContentResult();
}

采用了HTTP PUT标记Update方法,并且响应状态码设置为204(No Content)。根据HTTP规范,PUT请求要求客户端发送整个被更新实体,而不是增量更新部分。如果要支持局部更新,则需要使用HTTP PATCH。

204(No Content)状态码表示服务器已经成功处理了你的请求,但不需要返回具体的数据。浏览器不用刷新页面,也不用重定向到新的页面,会保留发送了该请求的页面,不产生任何文档视图上的变化,只停留在当前页面。由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾。对提交到服务器进行处理的数据,如果只需要返回是否成功的话,可考虑使用状态码204来作为返回信息,从而减少多余的数据传输。

NoContentResult类在构造函数中调用了父类的构造函数并把Status204NoContent传给了该类。

namespace Microsoft.AspNetCore.Mvc
{
public class NoContentResult : StatusCodeResult
{
public NoContentResult()
: base(StatusCodes.Status204NoContent)
{
}
}
}

更新用户信息(HTTP PATCH)

[HttpPatch("{id}")]
public IActionResult Update([FromBody] UserItem item, string id)
{
if (item == null)
{
return BadRequest();
} var user = UserItems.Find(id);
if (user == null)
{
return NotFound();
}
item.Key = user.Key; UserItems.Update(item);
return new NoContentResult();
}

删除用户

[HttpDelete("{id}")]
public IActionResult Delete(string id)
{
var user = UserItems.Find(id);
if (user == null)
{
return NotFound();
}
UserItems.Remove(id);
return new NoContentResult();
}

这个响应状态码同样是204(No Content)

个人博客

我的个人博客

在Mac下创建ASP.NET Core Web API的更多相关文章

  1. C#实现多级子目录Zip压缩解压实例 NET4.6下的UTC时间转换 [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 asp.Net Core免费开源分布式异常日志收集框架Exceptionless安装配置以及简单使用图文教程 asp.net core异步进行新增操作并且需要判断某些字段是否重复的三种解决方案 .NET Core开发日志

    C#实现多级子目录Zip压缩解压实例 参考 https://blog.csdn.net/lki_suidongdong/article/details/20942977 重点: 实现多级子目录的压缩, ...

  2. ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目

    一.前言 这几年前端的发展速度就像坐上了火箭,各种的框架一个接一个的出现,需要学习的东西越来越多,分工也越来越细,作为一个 .NET Web 程序猿,多了解了解行业的发展,让自己扩展出新的技能树,对自 ...

  3. [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了

    [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 本文首发自:博客园 文章地址: https://www.cnblogs.com/yilezhu/p/ ...

  4. List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac

    List多个字段标识过滤 class Program{  public static void Main(string[] args) { List<T> list = new List& ...

  5. 在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务

    在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务 https://procodeguide.com/programming/polly-in-aspnet-core ...

  6. ASP.NET Core Web API 跨域(CORS) Cookie问题

    身为一个Web API,处理来自跨域不同源的请求,是一件十分合理的事情. 先上已有的文章,快速复制粘贴,启用CORS: Microsoft:启用 ASP.NET Core 中的跨域请求 (CORS) ...

  7. 在Mac下运行ASP.NET Core应用程序

    在Mac下运行ASP.NET Core应用程序 通过参照.NET Core相关官方文档,在我的Mac电脑上用Visual Studio Code创建了我的第一个ASP.NET应用. 开发环境搭建 首先 ...

  8. Mac下运行ASP.NET Core应用程序

    Mac下运行ASP.NET Core应用程序 在Mac下运行ASP.NET Core应用程序 通过参照.NET Core相关官方文档,在我的Mac电脑上用Visual Studio Code创建了我的 ...

  9. ASP.NET Core Web API下事件驱动型架构的实现(一):一个简单的实现

    很长一段时间以来,我都在思考如何在ASP.NET Core的框架下,实现一套完整的事件驱动型架构.这个问题看上去有点大,其实主要目标是为了实现一个基于ASP.NET Core的微服务,它能够非常简单地 ...

随机推荐

  1. 《寒江独钓_Windows内核安全编程》中修改类驱动分发函数

    最近在阅读<寒江独钓_Windows内核安全编程>一书的过程中,发现修改类驱动分发函数这一技术点,书中只给出了具体思路和部分代码,没有完整的例子. 按照作者的思路和代码,将例子补充完整,发 ...

  2. 剑指Offer面试题:6.用两个栈实现队列

    一.题目:用两个栈实现队列 题目:用两个栈实现一个队列.队列的声明如下,请实现它的两个函数appendTail和deleteHead,分别完成在队列尾部插入结点和在队列头部删除结点的功能. 原文是使用 ...

  3. 部分安卓手机微信浏览器中使用XMLHttpRequest 2上传图片显示字节数为0的解决办法

    前端JS中使用XMLHttpRequest 2上传图片到服务器,PC端和大部分手机上都正常,但在少部分安卓手机上上传失败,服务器上查看图片,显示字节数为0.下面是上传图片的核心代码: HTML < ...

  4. Mint linux 自定义上下文菜单实现ZIP压缩文件无乱码解压

    1. 前提条件 我的Mint Linux 是Thunar文件管理器(默认的). 2. 配置自定义动作 打开Thunar文件管理器,点击菜单“编辑”=>“配置自定义动作”.点击“+”添加一个新的. ...

  5. Log4net入门使用

    简介 几乎所有的大型应用都会有自己的用于跟踪调试的API.因为一旦程序被部署以后,就不太可能再利用专门的调试工具了.然而一个管理员可能需要有一套强大的日志系统来诊断和修复配置上的问题. 经验表明,日志 ...

  6. 2013 duilib入门简明教程 -- 结合win32和MFC (16)

        虽然duilib自带在MFC中使用duilib的Demo,但只是MFC窗口和duilib窗口不重叠的情况.如果要在MFC窗口中嵌入duilib控件,或者在duilib控件中嵌入MFC的控件的话 ...

  7. PHP扩展-扩展的生成和编译

    首先说明一下,PHP扩展有两种编译方式:方式一:在编译PHP时直接将扩展编译进去方式二:扩展被编译成.so文件,在php.ini里配置加载路径: 以下开始说明创建PHP扩展并编译的步骤:下载PHP源码 ...

  8. IM消息送达保证机制实现(二):保证离线消息的可靠投递

    1.前言 本文的上篇<IM消息送达保证机制实现(一):保证在线实时消息的可靠投递>中,我们讨论了在线实时消息的投递可以通过应用层的确认.发送方的超时重传.接收方的去重等手段来保证业务层面消 ...

  9. contains与compareDocumentPosition

    Sizzle中有一个contains方法(jQuery.contains),用来遍历是否包含指定的元素 IE率先引入一个contains()方法,可以在节点中查找某一个节点是不是当前节点的厚点,如果 ...

  10. 深入理解javascript对象系列第三篇——神秘的属性描述符

    × 目录 [1]类型 [2]方法 [3]详述[4]状态 前面的话 对于操作系统中的文件,我们可以驾轻就熟将其设置为只读.隐藏.系统文件或普通文件.于对象来说,属性描述符提供类似的功能,用来描述对象的值 ...