一、RESTful服务基础概念
1.1 REST架构核心原则
- 资源导向:一切皆资源,使用URI标识
- 统一接口:标准HTTP方法操作资源
- 无状态性:每个请求包含完整上下文
- 可缓存性:响应应明确是否可缓存
- 分层系统:客户端无需了解直接连接的服务端
1.2 HTTP方法语义规范
方法 | 幂等性 | 安全 | 典型应用场景 |
---|---|---|---|
GET | 是 | 是 | 获取资源表示 |
POST | 否 | 否 | 创建新资源或执行操作 |
PUT | 是 | 否 | 全量更新已知资源 |
PATCH | 否 | 否 | 部分更新资源 |
DELETE | 是 | 否 | 删除指定资源 |
二、ASP.NET Core RESTful服务搭建
2.1 项目初始化与配置
# 创建WebAPI项目
dotnet new webapi -n ProductService
cd ProductService
# 添加必要NuGet包
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
2.2 基础项目结构
ProductService/
├── Controllers/
│ └── ProductsController.cs
├── Models/
│ ├── Entities/
│ │ └── Product.cs
│ └── DTOs/
│ ├── ProductCreateDto.cs
│ └── ProductResponseDto.cs
├── Services/
│ ├── IProductService.cs
│ └── ProductService.cs
├── Mappings/
│ └── ProductProfile.cs
└── Program.cs
三、核心实现技术
3.1 控制器设计规范
[ApiController]
[Route("api/products")]
[Produces("application/json")]
public class ProductsController : ControllerBase
{
private readonly IProductService _service;
private readonly IMapper _mapper;
public ProductsController(IProductService service, IMapper mapper)
{
_service = service;
_mapper = mapper;
}
/// <summary>
/// 获取所有产品
/// </summary>
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<IEnumerable<ProductResponseDto>>> GetAll()
{
var products = await _service.GetAllAsync();
return Ok(_mapper.Map<List<ProductResponseDto>>(products));
}
}
3.2 高级路由配置
// 复合资源路由
[HttpGet("categories/{categoryId}/products")]
public IActionResult GetProductsByCategory(int categoryId)
{
// ...
}
// 自定义路由约束
[HttpGet("{id:int:min(1)}")]
public IActionResult GetById(int id)
{
// ...
}
// 条件路由
[HttpGet("search", Name = "SearchProducts")]
public IActionResult Search([FromQuery] ProductSearchCriteria criteria)
{
// ...
}
四、数据序列化与内容协商
4.1 响应格式控制
// 配置系统文本JSON序列化
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.JsonSerializerOptions.WriteIndented = true;
});
// 支持XML格式
builder.Services.AddControllers()
.AddXmlSerializerFormatters();
4.2 自定义格式化器
public class CsvOutputFormatter : TextOutputFormatter
{
public CsvOutputFormatter()
{
SupportedMediaTypes.Add("text/csv");
SupportedEncodings.Add(Encoding.UTF8);
}
protected override bool CanWriteType(Type type)
{
return typeof(IEnumerable).IsAssignableFrom(type);
}
public override async Task WriteResponseBodyAsync(
OutputFormatterWriteContext context,
Encoding selectedEncoding)
{
// CSV转换逻辑
}
}
// 注册格式化器
builder.Services.AddControllers(options =>
{
options.OutputFormatters.Add(new CsvOutputFormatter());
});
五、高级功能实现
5.1 HATEOAS支持
public class ProductResource
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public List<Link> Links { get; set; } = new List<Link>();
}
[HttpGet("{id}", Name = "GetProduct")]
public IActionResult GetById(int id)
{
var product = _service.GetById(id);
if (product == null) return NotFound();
var resource = _mapper.Map<ProductResource>(product);
resource.Links.Add(new Link(
href: Url.Link("GetProduct", new { id }),
rel: "self",
method: "GET"));
resource.Links.Add(new Link(
href: Url.Link("UpdateProduct", new { id }),
rel: "update-product",
method: "PUT"));
return Ok(resource);
}
5.2 并发控制
// 使用ETag实现乐观并发
[HttpPut("{id}")]
public IActionResult UpdateProduct(int id,
[FromBody] ProductUpdateDto dto,
[FromHeader(Name = "If-Match")] string etag)
{
var product = _service.GetById(id);
if (product == null) return NotFound();
// 验证ETag
var currentEtag = _service.GetProductVersion(id);
if (etag != currentEtag)
{
return StatusCode(StatusCodes.Status412PreconditionFailed);
}
// 更新逻辑
_service.UpdateProduct(id, dto);
// 返回新ETag
Response.Headers["ETag"] = _service.GetProductVersion(id);
return NoContent();
}
六、安全防护机制
6.1 认证与授权
// JWT配置
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidateAudience = true,
ValidAudience = builder.Configuration["Jwt:Audience"],
ValidateLifetime = true,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
ValidateIssuerSigningKey = true
};
});
// 策略授权
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireAdmin", policy =>
policy.RequireRole("Administrator"));
options.AddPolicy("MinimumAge", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(18)));
});
6.2 输入验证与防护
// 数据注解验证
public class ProductCreateDto
{
[Required(ErrorMessage = "产品名称是必填项")]
[StringLength(100, MinimumLength = 2)]
public string Name { get; set; }
[Range(0.01, double.MaxValue)]
public decimal Price { get; set; }
[Url]
public string ProductUrl { get; set; }
}
// 防跨站请求伪造
builder.Services.AddAntiforgery(options =>
{
options.HeaderName = "X-CSRF-TOKEN";
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
七、性能优化策略
7.1 缓存实现
// 响应缓存
[HttpGet("{id}")]
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]
public IActionResult GetById(int id)
{
// ...
}
// 分布式缓存
[HttpGet("featured")]
public async Task<IActionResult> GetFeaturedProducts()
{
const string cacheKey = "featured_products";
if (!_cache.TryGetValue(cacheKey, out List<Product> products))
{
products = await _service.GetFeaturedProductsAsync();
var cacheOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(5));
_cache.Set(cacheKey, products, cacheOptions);
}
return Ok(products);
}
7.2 分页与数据压缩
[HttpGet]
[ResponseCompression]
public async Task<ActionResult<PagedResponse<ProductDto>>> GetPaged(
[FromQuery] PaginationParams parameters)
{
var pagedData = await _service.GetPagedProductsAsync(parameters);
Response.Headers.Add("X-Pagination",
JsonSerializer.Serialize(pagedData.PaginationMetadata));
return Ok(pagedData);
}
// 响应压缩配置
builder.Services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
options.EnableForHttps = true;
});
八、测试与文档
8.1 单元测试示例
public class ProductsControllerTests
{
private readonly ProductsController _controller;
private readonly Mock<IProductService> _mockService = new();
public ProductsControllerTests()
{
var mapper = new MapperConfiguration(cfg =>
cfg.AddProfile(new ProductProfile())).CreateMapper();
_controller = new ProductsController(_mockService.Object, mapper);
}
[Fact]
public async Task Create_ReturnsCreated_WhenModelValid()
{
// Arrange
var dto = new ProductCreateDto { /* 初始化 */ };
_mockService.Setup(x => x.CreateAsync(It.IsAny<Product>()))
.ReturnsAsync(new Product { Id = 1 });
// Act
var result = await _controller.Create(dto);
// Assert
var createdAtResult = Assert.IsType<CreatedAtActionResult>(result);
Assert.Equal(201, createdAtResult.StatusCode);
}
}
8.2 Swagger文档增强
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Product Service API",
Version = "v1",
Contact = new OpenApiContact
{
Name = "API Support",
Email = "support@productservice.com"
}
});
// 添加JWT支持
var securityScheme = new OpenApiSecurityScheme
{
Name = "JWT Authentication",
Description = "Enter JWT Bearer token",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT",
Reference = new OpenApiReference
{
Id = JwtBearerDefaults.AuthenticationScheme,
Type = ReferenceType.SecurityScheme
}
};
c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme);
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{ securityScheme, Array.Empty<string>() }
});
});
九、部署与监控
9.1 容器化部署
# 多阶段构建Dockerfile
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY . .
RUN dotnet restore "ProductService.csproj"
RUN dotnet publish "ProductService.csproj" -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080
ENTRYPOINT ["dotnet", "ProductService.dll"]
9.2 健康检查与监控
// 健康检查配置
builder.Services.AddHealthChecks()
.AddDbContextCheck<ProductContext>()
.AddRedis(Configuration["Redis:ConnectionString"])
.AddUrlGroup(new Uri("http://external-service"), "External API");
// Prometheus监控
builder.Services.AddOpenTelemetryMetrics(builder =>
{
builder.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddPrometheusExporter();
});
app.UseOpenTelemetryPrometheusScrapingEndpoint();
十、RESTful设计最佳实践
- 资源命名规范:
- 使用名词复数形式(如
/products
) - 避免动词出现在URI中
- 层级关系表达(如
/stores/{id}/products
)
- 状态码正确使用:
- 200 OK – 成功GET请求
- 201 Created – 资源创建成功
- 204 No Content – 成功但无返回内容
- 400 Bad Request – 客户端错误
- 404 Not Found – 资源不存在
- 429 Too Many Requests – 请求限流
- 版本控制策略:
- URL路径版本(
/api/v1/products
) - 查询参数版本(
/api/products?version=1
) - 请求头版本(
Accept: application/vnd.company.api.v1+json
)
- 错误处理统一格式:
{
"error": {
"code": "invalid-request",
"message": "价格不能为负数",
"target": "price",
"details": [
{
"code": "validation-error",
"message": "必须大于0"
}
]
}
}
通过遵循这些原则和实践,您将能够构建出符合行业标准、易于维护且高性能的C# RESTful服务。随着.NET平台的持续演进,这些服务将能够充分利用最新的云原生特性和性能优化技术。