ASP.NET Core应用程序3:高级Web服务特性
1 准备工作
添加SuppliersController控制器。
[ApiController] [Route("api/[controller]")] public class SuppliersController:ControllerBase { private DataContext _context; public SuppliersController(DataContext dataContext) { _context = dataContext; } [HttpGet("{id}")] public async Task<Supplier> GetSupplier(long id) { Supplier supplier = await _context.Suppliers.FindAsync(id); return supplier; } }
2 处理相关数据
当使用Include方法时,EFCore可通过数据库中的关系填充导航属性。
[HttpGet("{id}")] public async Task<Supplier> GetSupplier(long id) { Supplier supplier = await _context.Suppliers .Include(s => s.Products) .FirstAsync(s => s.SupplierId == id); return supplier; }
但是这样会导致报错:对象循环。因为Suppliers和Product对象的导航属性之前创建了一个循环引用。
打破循环引用最简单的办法如下。
foreach (Product p in supplier.Products) { p.Supplier = null; }; return supplier;
3 支持HTTP Patch方法
使用Patch请求只向Web服务发送更改,而不是完整地替换对象。
3.1 理解JSON Patch
客户端会在HTTP Patch请求中发送Web服务Json数据,如下:
[ {"op":"replace","path":"Name","value":"abc"}, ]
3.2 安装和配置JSON Patch
安装JSON Patch包
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson --version 3.1.1
在Startup方法中启用序列化器。
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<DataContext>(opts => { opts.UseSqlServer(Configuration["ConnectionStrings:ProductConnection"]); opts.EnableSensitiveDataLogging(true); }); services.AddControllers().AddNewtonsoftJson(); services.Configure<MvcNewtonsoftJsonOptions>(opts => { opts.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore; }); }
AddNewtonsoftJson方法启用JSON.NET序列化器,替代标准的ASP.NET Core序列化器。它有自己的配置类MvcNewtonsoftJsonOptions,设置了NullValueHandlinge值丢弃空值。
3.4 定义操作方法
[HttpPatch("{id}")] public async Task<Supplier> PatchSupplier(long id, JsonPatchDocument<Supplier> supplierDocument) { Supplier supplier = await _context.Suppliers.FindAsync(id); if (supplier != null) { supplierDocument.ApplyTo(supplier); await _context.SaveChangesAsync(); } return supplier; }
用HttpPatch修饰方法,通过JsonPatchDocument
发送以下请求修改:
Invoke-RestMethod http://localhost:5000/api/suppliers/1 -Method Patch -ContentType "application/json" -Body '[{"op":"replace","path":"Name","value":"abc"}]'
4 理解内容的格式化
4.1 理解默认的内容策略
- 如果操作方法返回一个字符串,则吧未修改的字符串返回客户端,响应的Content-Type头设置为text/plain。
- 对于所有其他类型,包括int等其他简单类型,数据格式化为json,响应的Content-Type头设置为application/json。
添加ContentController类。
[ApiController] [Route("api/[controller]")] public class ContentController : ControllerBase { private DataContext _dataContext; public ContentController(DataContext dataContext) { _dataContext = dataContext; } [HttpGet("string")] public string GetString() => "addffdsfdfdfd"; [HttpGet("object")] public async Task<Product> GetObject() { return await _dataContext.Products.FirstAsync(); } }
Invoke-WebRequest http://localhost:5000/api/content/string | select @{n='Content-Type';e={$_.Headers."Content-Type"}},Content
Invoke-WebRequest http://localhost:5000/api/content/object | select @{n='Content-Type';e={$_.Headers."Content-Type"}},Content
4.2 理解内容协商
(1)启用XML格式
要使内容协商生效,必须对应用程序进行配置,以便可以选择使用的格式。Json已经成为默认格式,但是MVC框架也可以支持将数据编码为Xml。
启用xml格式。
services.AddControllers().AddNewtonsoftJson().AddXmlSerializerFormatters();
xml序列化不能处理EFCore导航属性,因为它是通过接口定义的。修改GetObject使用可序列化的ProductBindingTarget对象
[HttpGet("object")] public async Task<ProductBindingTarget> GetObject() { Product p = await _dataContext.Products.FirstAsync(); return new ProductBindingTarget() { Name = p.Name, Price = p.Price, CategoryId = p.CategoryId, SupplierId = p.SupplierId }; }
只有可用Json格式时别无选择,现在有了选择可以内容协商用application/xml输出。
Invoke-WebRequest http://localhost:5000/api/content/object -Headers @{Accept="application/xml"} | select @{n='Content-Type';e={$_.Headers."Content-Type"}},Content
(2)完全尊重Accept表头
如果Accept表头包含任何格式的/,mvc框架始终使用json格式。我们需要两个配置告诉mvc框架尊重客户端设置,默认情况下不发送json数据。
services.Configure<MvcOptions>(opts => { opts.RespectBrowserAcceptHeader = true; opts.ReturnHttpNotAcceptable = true; });
设置MvcOptions对象属性,RespectBrowserAcceptHeader为true表示禁止在Accept包含/时回退到json,ReturnHttpNotAcceptable为true表示禁止在客户端请求不支持数据格式时回退到json。
4.3 指定操作结果格式
[HttpGet("object")] [Produces("application/json")] public async Task<ProductBindingTarget> GetObject()
4.4 在URL中请求格式
通过使用FormatFilter属性修饰操作方法并确保操作方法的路由中有一个format段变量来启用该特性。
[HttpGet("object/{format?}")] [FormatFilter] [Produces("application/json", "application/xml")] public async Task<ProductBindingTarget> GetObject()
可以在URl中设置请求格式,如:http://localhost:5000/api/content/object/xml、http://localhost:5000/api/content/object/json。
4.5 限制操作方法接收的格式
可以将Consumes属性应用于操作方法,以限制要处理的数据类型。
[HttpPost] [Consumes("application/json")] public string SaveProductJson(ProductBindingTarget product) { return $"JSON: {product.Name}"; } [HttpPost] [Consumes("application/xml")] public string SaveProductXml(ProductBindingTarget product) { return $"XML: {product.Name}"; }
5 记录和探索Web服务
OpenAPI规范也称为Swagger,它提供描述Web服务。
5.1 解决操作冲突
OpenAPI发现过程要求对每个操作方法使用唯一HTTP方法和URL模式组合。该进程不支持Consumes属性,需要删除这些方法。
5.2 安装和配置Swashbuckle包
dotnet add package Swashbuckle.AspNetCore --version 5.5.0-rc2
在Startup中配置。
services.AddSwaggerGen(options => { options.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApp", Version = "v1" }); });
app.UseSwagger(); app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApp"); });
5.3 微调API描述
(1)运行API分析器
在项目文件夹.csproj文件中启用分析器。
<PropertyGroup> <IncludeOpenAPIAnalyzers>true</IncludeOpenAPIAnalyzers> </PropertyGroup>
分析器会检测到return NotFound();
只返回一个状态码响应。
(2)声明操作方法结果类型
要修复检测到的问题,可以使用ProducesResponseType属性声明操作方法结果类型。
[HttpGet("{id}")] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status404NotFound)] public async Task<IActionResult> GetProduct(long id)
本文来自博客园,作者:一纸年华,转载请注明原文链接:https://www.cnblogs.com/nullcodeworld/p/18154358
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!