[ASP.NET MVC 小牛之路]18 - Web API 详解与实战
在现代Web开发中,Web API 是构建后端服务的核心组件之一,它允许我们为不同类型的客户端(如浏览器、移动App、第三方系统)提供标准化的数据接口。ASP.NET Web API专注于构建RESTful风格的服务,支持JSON、XML等多种数据格式,是构建前后端分离应用、微服务的核心工具。
本文将从基础概念出发,逐步深入Web API的开发实践,涵盖项目创建、控制器设计、路由配置、CRUD实现、安全认证及最佳实践,帮助你快速掌握ASP.NET Web API的核心技能。
目录#
-
Web API 基础概念
1.1 什么是Web API?
1.2 RESTful 架构风格
1.3 Web API vs ASP.NET MVC -
创建Web API项目
2.1 项目模板选择
2.2 项目结构解析 -
Web API 控制器与动作
3.1ApiController与控制器设计
3.2 动作方法的返回类型
3.3 示例:产品管理控制器 -
路由配置
4.1 默认路由与自定义路由
4.2 路由属性(RoutePrefix、Route)
4.3 路由约束与参数传递 -
CRUD 操作实现
5.1 GET:查询资源(单条/列表)
5.2 POST:创建资源
5.3 PUT:更新资源
5.4 DELETE:删除资源
5.5 错误处理与状态码 -
内容协商与格式化
6.1 内容协商原理
6.2 配置格式化器
6.3 自定义格式化器 -
安全与认证
7.1 认证与授权基础
7.2 基于Token的认证(JWT)
7.3[Authorize]属性的使用 -
最佳实践
8.1 路由设计规范
8.2 错误处理最佳实践
8.3 API版本控制
8.4 性能优化与缓存 -
实战:待办事项API开发
9.1 项目搭建与模型设计
9.2 控制器实现(CRUD)
9.3 测试与部署 -
参考资料
1. Web API 基础概念#
1.1 什么是Web API?#
Web API是一种通过HTTP协议对外提供数据服务的接口,客户端(如浏览器、移动App、其他服务器)通过发送HTTP请求(GET/POST/PUT/DELETE等)获取或操作数据。ASP.NET Web API专注于构建RESTful风格的服务,支持JSON、XML等多种数据格式,是构建前后端分离应用、微服务的核心工具。
1.2 RESTful 架构风格#
REST(Representational State Transfer)是一种软件架构风格,核心原则包括:
- 资源(Resource):以URI标识(如
/api/products表示产品资源集合)。 - 统一接口:使用HTTP方法(GET/POST/PUT/DELETE)操作资源。
- 无状态:每个请求包含足够信息,服务器不保留客户端状态。
- 表现层:资源的表现形式(如JSON/XML)与资源本身分离。
示例:
GET /api/products:获取所有产品(资源列表)。GET /api/products/1:获取ID为1的产品(单个资源)。POST /api/products:创建新产品(资源)。PUT /api/products/1:更新ID为1的产品。DELETE /api/products/1:删除ID为1的产品。
1.3 Web API vs ASP.NET MVC#
| 对比项 | ASP.NET MVC | ASP.NET Web API |
|---|---|---|
| 核心目标 | 渲染页面(View) | 提供数据服务(JSON/XML等) |
| 路由设计 | 面向页面(如 /Home/Index) | 面向资源(如 /api/products) |
| 返回类型 | ActionResult(视图/数据) | IHttpActionResult/HttpResponseMessage/直接返回模型 |
| 客户端场景 | 浏览器为主 | 多客户端(移动、前端、第三方) |
2. 创建Web API项目#
2.1 项目模板选择#
- 打开Visual Studio,新建项目 → 选择 ASP.NET Web Application。
- 命名项目(如
WebApiDemo),选择框架版本(建议.NET Framework 4.6+或.NET Core)。 - 模板选择:Web API(若使用.NET Core,选择“API”模板)。
注意:.NET Core中,ASP.NET Core Web API是更现代的选择,本文以.NET Framework的Web API 2为例,核心概念与.NET Core版本兼容。
2.2 项目结构解析#
创建后的项目结构包含:
Controllers文件夹:存放API控制器(继承ApiController),默认包含ValuesController.cs(示例控制器)。Models文件夹:存放数据模型(如实体类、DTO)。App_Start文件夹:WebApiConfig.cs:配置Web API路由、格式化器等。RouteConfig.cs:MVC路由(若项目同时包含MVC)。
Global.asax.cs:应用程序启动入口,注册路由等。
3. Web API 控制器与动作#
3.1 ApiController 与控制器设计#
Web API控制器需继承 System.Web.Http.ApiController(.NET Framework)或 Microsoft.AspNetCore.Mvc.ControllerBase(.NET Core)。
示例:创建ProductController管理产品资源:
using System.Web.Http;
using WebApiDemo.Models;
public class ProductController : ApiController
{
// 模拟数据存储
private static List<Product> products = new List<Product>
{
new Product { Id = 1, Name = "手机", Price = 3999 },
new Product { Id = 2, Name = "电脑", Price = 5999 }
};
// 动作方法...
}
// 模型类
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}3.2 动作方法的返回类型#
Web API动作方法支持三种返回类型:
- 直接返回模型:如
List<Product>,框架自动序列化为JSON/XML。 HttpResponseMessage:手动控制HTTP响应(状态码、头部、内容)。IHttpActionResult:推荐!封装常见响应(如Ok()/NotFound()/BadRequest()),更简洁。
示例(IHttpActionResult):
// 获取所有产品
public IHttpActionResult Get()
{
return Ok(products); // 返回200 OK,包含产品列表
}
// 获取单个产品
public IHttpActionResult Get(int id)
{
var product = products.FirstOrDefault(p => p.Id == id);
if (product == null)
{
return NotFound(); // 返回404 Not Found
}
return Ok(product);
}3.3 示例:产品管理控制器#
完整的ProductController示例(包含CRUD):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using WebApiDemo.Models;
public class ProductController : ApiController
{
private List<Product> products = new List<Product>
{
new Product { Id = 1, Name = "手机", Price = 3999 },
new Product { Id = 2, Name = "电脑", Price = 5999 }
};
// GET: api/Product
public IHttpActionResult Get()
{
return Ok(products);
}
// GET: api/Product/5
public IHttpActionResult Get(int id)
{
var product = products.FirstOrDefault(p => p.Id == id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
// POST: api/Product
public IHttpActionResult Post([FromBody]Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState); // 返回400,包含验证错误
}
// 模拟新增(实际应使用数据库)
product.Id = products.Max(p => p.Id) + 1;
products.Add(product);
return CreatedAtRoute("DefaultApi", new { id = product.Id }, product);
}
// PUT: api/Product/5
public IHttpActionResult Put(int id, [FromBody]Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != product.Id)
{
return BadRequest("ID不匹配");
}
var existing = products.FirstOrDefault(p => p.Id == id);
if (existing == null)
{
return NotFound();
}
// 更新属性
existing.Name = product.Name;
existing.Price = product.Price;
return Ok(existing);
}
// DELETE: api/Product/5
public IHttpActionResult Delete(int id)
{
var product = products.FirstOrDefault(p => p.Id == id);
if (product == null)
{
return NotFound();
}
products.Remove(product);
return Ok(product);
}
}4. 路由配置#
4.1 默认路由与自定义路由#
Web API的默认路由在 WebApiConfig.cs 中配置:
using System.Web.Http;
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// 默认路由:api/{controller}/{id}
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}默认路由规则:
GET /api/Product→ 调用ProductController.Get()(无参)。GET /api/Product/1→ 调用ProductController.Get(1)。POST /api/Product→ 调用ProductController.Post(...)。
4.2 路由属性(RoutePrefix、Route)#
若需更灵活的路由(如嵌套资源、自定义URL),可使用路由属性:
示例:为ProductController设置路由前缀,并为动作方法自定义路由:
using System.Web.Http;
[RoutePrefix("api/v1/products")] // 路由前缀
public class ProductController : ApiController
{
// GET api/v1/products
[Route("")]
public IHttpActionResult GetAll()
{
// ...
}
// GET api/v1/products/5
[Route("{id:int}")] // 约束id为整数
public IHttpActionResult GetById(int id)
{
// ...
}
// POST api/v1/products
[Route("")]
public IHttpActionResult Create([FromBody]Product product)
{
// ...
}
}4.3 路由约束与参数传递#
路由约束可限制参数类型(如int/string/datetime),示例:
// 匹配 /api/products/1(id为整数)
[Route("{id:int}")]
// 匹配 /api/products/abc(id为字符串)
[Route("{id:alpha}")]5. CRUD 操作实现#
5.1 GET:查询资源#
- 列表查询:返回所有资源,使用
GET /api/resource。 - 单条查询:返回指定ID的资源,使用
GET /api/resource/{id}。
示例(续上ProductController的Get方法):
// 列表查询
public IHttpActionResult Get()
{
return Ok(products);
}
// 单条查询
public IHttpActionResult Get(int id)
{
var product = products.FirstOrDefault(p => p.Id == id);
return product == null ? (IHttpActionResult)NotFound() : Ok(product);
}5.2 POST:创建资源#
使用POST /api/resource,请求体包含资源数据(JSON/XML)。需注意:
- 模型验证(
ModelState.IsValid)。 - 创建成功后返回
201 Created,并包含资源位置(CreatedAtRoute)。
示例:
public IHttpActionResult Post([FromBody]Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
// 模拟新增
product.Id = products.Max(p => p.Id) + 1;
products.Add(product);
// 返回201,并指定资源位置(如 /api/Product/3)
return CreatedAtRoute("DefaultApi", new { id = product.Id }, product);
}5.3 PUT:更新资源#
使用PUT /api/resource/{id},请求体包含更新后的资源数据。需注意:
- ID匹配(URL中的ID与请求体中的ID一致)。
- 资源存在性检查。
示例:
public IHttpActionResult Put(int id, [FromBody]Product product)
{
if (id != product.Id)
{
return BadRequest("ID不匹配");
}
var existing = products.FirstOrDefault(p => p.Id == id);
if (existing == null)
{
return NotFound();
}
// 更新属性
existing.Name = product.Name;
existing.Price = product.Price;
return Ok(existing);
}5.4 DELETE:删除资源#
使用DELETE /api/resource/{id},删除指定ID的资源。需注意:
- 资源存在性检查。
- 删除后返回
200 OK或204 No Content(无响应体)。
示例:
public IHttpActionResult Delete(int id)
{
var product = products.FirstOrDefault(p => p.Id == id);
if (product == null)
{
return NotFound();
}
products.Remove(product);
return Ok(product); // 或 return StatusCode(HttpStatusCode.NoContent);
}5.5 错误处理与状态码#
Web API通过IHttpActionResult的方法返回标准HTTP状态码:
NotFound()→ 404。BadRequest()→ 400。Unauthorized()→ 401。InternalServerError()→ 500。
示例:处理无效请求:
public IHttpActionResult Post([FromBody]Product product)
{
if (string.IsNullOrEmpty(product.Name))
{
ModelState.AddModelError("Name", "产品名称不能为空");
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState); // 返回400,包含错误信息
}
// ...
}6. 内容协商与格式化#
6.1 内容协商原理#
Web API根据请求头的Accept字段决定返回格式(如Accept: application/json则返回JSON)。框架默认支持JSON和XML格式化器。
6.2 配置格式化器#
若需只返回JSON(移除XML格式化器),在WebApiConfig.cs中配置:
public static void Register(HttpConfiguration config)
{
// 移除XML格式化器
config.Formatters.Remove(config.Formatters.XmlFormatter);
// 配置JSON格式化器(可选,如缩进、日期格式)
config.Formatters.JsonFormatter.SerializerSettings.Formatting =
Newtonsoft.Json.Formatting.Indented;
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver(); // 小驼峰命名
}6.3 自定义格式化器#
若需支持自定义格式(如CSV),可实现MediaTypeFormatter,但通常JSON/XML足够。
7. 安全与认证#
7.1 认证与授权基础#
Web API的安全包括认证(用户身份验证)和授权(用户权限控制)。常见方案:
- 基本认证:在请求头中传递Base64编码的用户名/密码(不推荐明文传输,需HTTPS)。
- Bearer Token:基于OAuth2,使用
Authorization: Bearer {token}传递令牌。 - ASP.NET Identity:结合Cookie或Token认证。
7.2 基于Token的认证(JWT)#
JSON Web Token(JWT)是轻量级的认证方式,步骤:
- 客户端登录,服务端生成JWT(包含用户信息、过期时间)。
- 客户端后续请求在
Authorization头中携带JWT。 - 服务端验证JWT有效性,授权访问。
示例:使用[Authorize]属性保护API:
[Authorize] // 需认证才能访问
public IHttpActionResult GetUserInfo()
{
var user = User.Identity.Name;
return Ok(new { UserName = user });
}7.3 [Authorize] 属性的使用#
可在控制器或动作方法上添加[Authorize],也可指定角色/用户:
// 只允许Admin角色访问
[Authorize(Roles = "Admin")]
public IHttpActionResult Delete(int id)
{
// ...
}
// 只允许特定用户访问
[Authorize(Users = "Alice,Bob")]
public IHttpActionResult GetSecret()
{
// ...
}8. 最佳实践#
8.1 路由设计规范#
- 资源为中心:URI以资源命名(复数,如
/api/products)。 - 层级清晰:嵌套资源(如
/api/orders/1/items表示订单1的商品)。 - 避免动词:用HTTP方法代替(如不用
/api/products/getAll,而用GET /api/products)。
8.2 错误处理最佳实践#
- 返回适当的HTTP状态码(如404表示资源不存在,400表示请求错误)。
- 错误信息包含可读的描述(如
{"message": "产品名称不能为空"})。 - 使用
IHttpActionResult的方法(NotFound()/BadRequest())而非手动设置状态码。
8.3 API版本控制#
随着业务迭代,API需版本控制,方案:
- URI版本:
/api/v1/products、/api/v2/products。 - 请求头版本:通过
X-API-Version头指定版本。 - 媒体类型版本:通过
Accept头的version参数(如Accept: application/json;version=2)。
8.4 性能优化与缓存#
- 异步操作:控制器方法使用
async/await,避免阻塞线程(如public async Task<IHttpActionResult> Get())。 - 缓存:使用
OutputCache或自定义缓存逻辑(如[CacheOutput(ClientTimeSpan = 60)])。 - 数据过滤:返回必要字段(如用DTO代替全量模型)。
9. 实战:待办事项API开发#
9.1 项目搭建与模型设计#
- 新建Web API项目,命名
TodoApi。 - 添加
TodoItem模型:public class TodoItem { public int Id { get; set; } public string Title { get; set; } public bool IsCompleted { get; set; } public DateTime CreatedAt { get; set; } = DateTime.Now; }
9.2 控制器实现(CRUD)#
创建TodoController,实现CRUD:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
public class TodoController : ApiController
{
private List<TodoItem> todos = new List<TodoItem>();
// GET: api/Todo
public IHttpActionResult Get()
{
return Ok(todos);
}
// GET: api/Todo/5
public IHttpActionResult Get(int id)
{
var todo = todos.FirstOrDefault(t => t.Id == id);
return todo == null ? (IHttpActionResult)NotFound() : Ok(todo);
}
// POST: api/Todo
public IHttpActionResult Post([FromBody]TodoItem todo)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
todo.Id = todos.Count > 0 ? todos.Max(t => t.Id) + 1 : 1;
todos.Add(todo);
return CreatedAtRoute("DefaultApi", new { id = todo.Id }, todo);
}
// PUT: api/Todo/5
public IHttpActionResult Put(int id, [FromBody]TodoItem todo)
{
if (id != todo.Id)
{
return BadRequest("ID不匹配");
}
var existing = todos.FirstOrDefault(t => t.Id == id);
if (existing == null)
{
return NotFound();
}
existing.Title = todo.Title;
existing.IsCompleted = todo.IsCompleted;
return Ok(existing);
}
// DELETE: api/Todo/5
public IHttpActionResult Delete(int id)
{
var todo = todos.FirstOrDefault(t => t.Id == id);
if (todo == null)
{
return NotFound();
}
todos.Remove(todo);
return Ok(todo);
}
}9.3 测试与部署#
- 测试:使用Postman发送HTTP请求,验证各端点(如
GET http://localhost:5000/api/Todo)。 - 部署:发布到IIS或云服务器(如Azure App Service)。
参考资料#
- 微软官方文档:ASP.NET Web API 2 文档
- 书籍:《ASP.NET Web API 2框架揭秘》(Web API核心原理)
- 博客:Restful API 设计最佳实践
- JWT认证:JSON Web Token 入门教程
通过本文,你已掌握ASP.NET Web API的核心开发流程:从项目创建、控制器设计、路由配置到CRUD实现、安全认证及最佳实践。实践是掌握的关键,建议结合示例项目(如待办事项API)反复调试,深入理解RESTful风格与Web API的设计思想。
希望这篇“小牛之路”能助你在Web API开发的道路上更进一步!如有疑问,欢迎留言交流~