TJC.Cyclops.Web.Core 2025.12.3.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package TJC.Cyclops.Web.Core --version 2025.12.3.2
                    
NuGet\Install-Package TJC.Cyclops.Web.Core -Version 2025.12.3.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TJC.Cyclops.Web.Core" Version="2025.12.3.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TJC.Cyclops.Web.Core" Version="2025.12.3.2" />
                    
Directory.Packages.props
<PackageReference Include="TJC.Cyclops.Web.Core" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TJC.Cyclops.Web.Core --version 2025.12.3.2
                    
#r "nuget: TJC.Cyclops.Web.Core, 2025.12.3.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TJC.Cyclops.Web.Core@2025.12.3.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TJC.Cyclops.Web.Core&version=2025.12.3.2
                    
Install as a Cake Addin
#tool nuget:?package=TJC.Cyclops.Web.Core&version=2025.12.3.2
                    
Install as a Cake Tool

Cyclops.Web.Core

项目概述

Cyclops.Web.Core是Cyclops.Framework框架中的Web核心组件,提供HTTP通信、Web服务构建、请求处理、响应格式化等基础功能。该组件为Web应用和API开发提供了统一的基础设施,包括中间件集合、HTTP客户端扩展、内容协商、错误处理、安全特性和Web工具集,简化了ASP.NET Core应用的开发和扩展。

核心功能模块

HTTP通信

  • 增强型HTTP客户端
  • 请求/响应拦截器
  • 重试与超时策略
  • HTTP内容处理

Web中间件

  • 请求日志中间件
  • 异常处理中间件
  • CORS配置中间件
  • 限流中间件
  • 认证授权中间件

内容处理

  • 自动内容协商
  • 多格式序列化支持
  • 文件上传处理
  • 静态内容服务增强

路由与控制器

  • 路由增强功能
  • 控制器扩展方法
  • 动作过滤器集合
  • 模型绑定扩展

安全特性

  • CSRF防护
  • XSS防御
  • CSP配置
  • 安全头设置

高级功能

  • API版本控制
  • 健康检查集成
  • 性能监控
  • 分布式追踪

技术栈

  • .NET 8.0
  • ASP.NET Core
  • Newtonsoft.Json / System.Text.Json
  • Cyclops.Common
  • Cyclops.Logging

环境依赖

  • .NET 8.0 SDK
  • ASP.NET Core Runtime

安装配置

NuGet安装

Install-Package Cyclops.Web.Core

基本配置

在应用程序启动时进行配置:

// 在Program.cs或Startup.cs中
using Cyclops.Web.Core;

var builder = WebApplication.CreateBuilder(args);

// 添加Cyclops.Web.Core服务
builder.Services.AddCyclopsWebCore(options => {
    // HTTP客户端配置
    options.HttpClientOptions = new HttpClientOptions {
        DefaultTimeout = TimeSpan.FromSeconds(30),
        EnableRetry = true,
        MaxRetryCount = 3,
        RetryDelay = TimeSpan.FromSeconds(1),
        EnableCircuitBreaker = true,
        CircuitBreakingThreshold = 5
    };
    
    // 序列化配置
    options.SerializationOptions = new SerializationOptions {
        DefaultSerializer = SerializerType.SystemTextJson,
        EnableIndentation = builder.Environment.IsDevelopment(),
        IgnoreNullValues = false
    };
    
    // 安全配置
    options.SecurityOptions = new WebSecurityOptions {
        EnableHsts = true,
        EnableXContentTypeOptions = true,
        EnableXFrameOptions = true,
        EnableXXssProtection = true,
        ContentSecurityPolicy = "default-src 'self'"
    };
    
    // API配置
    options.ApiOptions = new ApiOptions {
        EnableApiVersioning = true,
        DefaultApiVersion = "1.0",
        EnableProblemDetails = true
    };
});

var app = builder.Build();

// 配置中间件
app.UseCyclopsWebCore();

// 应用程序配置
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

代码示例

增强型HTTP客户端示例

using Cyclops.Web.Core.Http;
using Microsoft.Extensions.DependencyInjection;

// HTTP客户端服务
public class EnhancedHttpClientService
{
    private readonly IEnhancedHttpClientFactory _httpClientFactory;
    
    public EnhancedHttpClientService(IEnhancedHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }
    
    // 创建并使用增强的HTTP客户端
    public async Task<ApiResponse<T>> GetApiDataAsync<T>(string endpoint)
    {
        // 创建命名的HTTP客户端
        var httpClient = _httpClientFactory.CreateClient("ApiClient");
        
        try
        {
            // 设置基础地址(如果在配置中未设置)
            httpClient.BaseAddress = new Uri("https://api.example.com/");
            
            // 添加请求头
            httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
            httpClient.DefaultRequestHeaders.Add("X-Client-Version", "1.0.0");
            
            // 执行请求并自动处理序列化
            var response = await httpClient.GetAsync<T>(endpoint, 
                successStatusCode: System.Net.HttpStatusCode.OK);
            
            if (response.IsSuccess)
            {
                Console.WriteLine($"API请求成功,状态码: {response.StatusCode}");
                return response;
            }
            else
            {
                Console.WriteLine($"API请求失败,状态码: {response.StatusCode}");
                Console.WriteLine($"错误信息: {response.ErrorMessage}");
                return response;
            }
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"HTTP请求异常: {ex.Message}");
            throw;
        }
    }
    
    // 带请求拦截器的HTTP客户端
    public async Task PostDataAsync<T>(string endpoint, T data)
    {
        // 创建配置了拦截器的HTTP客户端
        var httpClient = _httpClientFactory.CreateClient("AuthenticatedClient", configure => {
            // 添加请求拦截器(如添加认证令牌)
            configure.AddRequestInterceptor(async (request) => {
                // 模拟获取令牌
                var token = await GetAuthTokenAsync();
                request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
                
                Console.WriteLine($"请求被拦截,添加认证令牌: {request.RequestUri}");
                return request;
            });
            
            // 添加响应拦截器
            configure.AddResponseInterceptor(async (request, response) => {
                Console.WriteLine($"响应被拦截: {request.RequestUri}, 状态码: {response.StatusCode}");
                
                // 可以在这里处理特定响应
                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    Console.WriteLine("认证失败,需要刷新令牌");
                    // 处理令牌刷新逻辑...
                }
                
                return response;
            });
        });
        
        // 执行POST请求
        var result = await httpClient.PostAsJsonAsync(endpoint, data);
        await result.EnsureSuccessStatusCodeAsync();
        
        Console.WriteLine("数据提交成功");
    }
    
    // 配置重试策略的HTTP客户端
    public async Task GetDataWithRetryAsync(string endpoint)
    {
        var httpClient = _httpClientFactory.CreateClient("ResilientClient", configure => {
            // 配置重试策略
            configure.ConfigureRetry(retryOptions => {
                retryOptions.RetryCount = 5;
                retryOptions.RetryDelay = TimeSpan.FromSeconds(2);
                retryOptions.MaxRetryDelay = TimeSpan.FromSeconds(30);
                retryOptions.RetryBackoffMultiplier = 2;
                
                // 配置重试条件
                retryOptions.RetryConditions = new[] {
                    System.Net.HttpStatusCode.RequestTimeout,
                    System.Net.HttpStatusCode.ServiceUnavailable,
                    System.Net.HttpStatusCode.GatewayTimeout
                };
                
                // 配置重试回调
                retryOptions.OnRetry = (attempt, delay, statusCode, exception) => {
                    Console.WriteLine($"请求失败,正在进行第 {attempt} 次重试,延迟: {delay.TotalSeconds}秒");
                    Console.WriteLine($"失败原因: {statusCode} - {exception?.Message}");
                    return Task.CompletedTask;
                };
            });
        });
        
        // 执行请求
        var response = await httpClient.GetAsync(endpoint);
        response.EnsureSuccessStatusCode();
        
        var content = await response.Content.ReadAsStringAsync();
        Console.WriteLine($"获取数据成功,内容长度: {content.Length} 字符");
    }
    
    // 模拟获取认证令牌
    private async Task<string> GetAuthTokenAsync()
    {
        // 实际应用中应从认证服务获取令牌
        await Task.Delay(10);
        return "sample-auth-token-12345";
    }
}

// API响应封装
public class ApiResponse<T>
{
    public bool IsSuccess { get; set; }
    public T Data { get; set; }
    public System.Net.HttpStatusCode StatusCode { get; set; }
    public string ErrorMessage { get; set; }
    public IDictionary<string, string[]> ValidationErrors { get; set; }
}

中间件使用示例

using Cyclops.Web.Core.Middleware;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

// 中间件配置扩展
public static class MiddlewareConfigurationExtensions
{
    // 配置Cyclops中间件
    public static IApplicationBuilder ConfigureCyclopsMiddleware(this IApplicationBuilder app)
    {
        // 使用请求日志中间件
        app.UseRequestLogging(options => {
            options.LogRequestHeaders = true;
            options.LogResponseHeaders = false;
            options.LogRequestBody = true;
            options.LogResponseBody = false;
            options.MaxBodyLogSize = 1024; // 最大记录1KB的请求体
            options.ExcludePaths = new[] { "/health", "/metrics" };
            options.LogLevel = LogLevel.Information;
        });
        
        // 使用异常处理中间件
        app.UseExceptionHandling(options => {
            options.ExceptionHandlers = new Dictionary<Type, Func<HttpContext, Exception, Task>> {
                {
                    typeof(NotFoundException), async (context, ex) => {
                        context.Response.StatusCode = StatusCodes.Status404NotFound;
                        await context.Response.WriteJsonAsync(new {
                            error = "ResourceNotFound",
                            message = ex.Message,
                            timestamp = DateTime.UtcNow
                        });
                    }
                },
                {
                    typeof(ValidationException), async (context, ex) => {
                        context.Response.StatusCode = StatusCodes.Status400BadRequest;
                        var validationEx = ex as ValidationException;
                        await context.Response.WriteJsonAsync(new {
                            error = "ValidationError",
                            message = "请求参数验证失败",
                            details = validationEx?.Errors,
                            timestamp = DateTime.UtcNow
                        });
                    }
                }
            };
            
            options.DefaultHandler = async (context, ex) => {
                // 记录异常日志
                var logger = context.RequestServices.GetRequiredService<ILogger<ExceptionHandlingMiddleware>>();
                logger.LogError(ex, "未处理的异常: {Path}", context.Request.Path);
                
                // 向客户端返回错误信息
                context.Response.StatusCode = StatusCodes.Status500InternalServerError;
                await context.Response.WriteJsonAsync(new {
                    error = "InternalServerError",
                    message = "服务器内部错误",
                    timestamp = DateTime.UtcNow
                });
            };
        });
        
        // 使用CORS中间件
        app.UseCorsPolicy(options => {
            options.AllowOrigins = new[] { "https://example.com", "https://api.example.com" };
            options.AllowMethods = new[] { "GET", "POST", "PUT", "DELETE", "OPTIONS" };
            options.AllowHeaders = new[] { "Content-Type", "Authorization", "X-Requested-With" };
            options.AllowCredentials = true;
            options.MaxAge = TimeSpan.FromHours(1);
        });
        
        // 使用限流中间件
        app.UseRateLimiting(options => {
            options.Policies = new[] {
                new RateLimitPolicy {
                    Name = "ApiRateLimit",
                    PathPattern = "/api/**",
                    Limit = 100, // 100个请求
                    Per = TimeSpan.FromMinutes(1), // 每分钟
                    ExcludePaths = new[] { "/api/health", "/api/public/**" },
                    LimitExceededHandler = async (context) => {
                        context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
                        context.Response.Headers["Retry-After"] = "60";
                        await context.Response.WriteJsonAsync(new {
                            error = "TooManyRequests",
                            message = "请求频率过高,请稍后再试",
                            retryAfter = 60
                        });
                    }
                }
            };
        });
        
        // 使用安全头中间件
        app.UseSecurityHeaders(options => {
            options.Headers = new[] {
                new SecurityHeader { Name = "X-Content-Type-Options", Value = "nosniff" },
                new SecurityHeader { Name = "X-Frame-Options", Value = "SAMEORIGIN" },
                new SecurityHeader { Name = "X-XSS-Protection", Value = "1; mode=block" },
                new SecurityHeader { Name = "Referrer-Policy", Value = "strict-origin-when-cross-origin" },
                new SecurityHeader { 
                    Name = "Content-Security-Policy", 
                    Value = "default-src 'self'; script-src 'self' https://trusted-cdn.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:;"
                }
            };
        });
        
        return app;
    }
}

// 自定义异常类型
public class NotFoundException : Exception
{
    public NotFoundException(string message) : base(message) { }
}

public class ValidationException : Exception
{
    public ValidationException(string message, IDictionary<string, string[]> errors = null) : base(message)
    {
        Errors = errors ?? new Dictionary<string, string[]>();
    }
    
    public IDictionary<string, string[]> Errors { get; }
}

控制器与API扩展示例

using Cyclops.Web.Core.Attributes;
using Cyclops.Web.Core.Controllers;
using Cyclops.Web.Core.Extensions;
using Microsoft.AspNetCore.Mvc;

// 基础控制器
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[Produces("application/json")]
[Consumes("application/json")]
public abstract class ApiControllerBase : ControllerBase
{
    // 统一的成功响应
    protected ActionResult<T> OkResponse<T>(T data, string message = null)
    {
        return Ok(new ApiResponse<T> {
            Success = true,
            Data = data,
            Message = message,
            Timestamp = DateTime.UtcNow
        });
    }
    
    // 统一的错误响应
    protected ActionResult ErrorResponse(string message, int statusCode = StatusCodes.Status400BadRequest, object details = null)
    {
        var errorResponse = new ErrorResponse {
            Success = false,
            Message = message,
            Details = details,
            Timestamp = DateTime.UtcNow
        };
        
        return StatusCode(statusCode, errorResponse);
    }
    
    // 获取当前用户ID
    protected string GetCurrentUserId()
    {
        return User.FindFirstValue(ClaimTypes.NameIdentifier);
    }
    
    // 获取客户端IP地址
    protected string GetClientIpAddress()
    {
        return HttpContext.Connection.RemoteIpAddress?.ToString();
    }
}

// API响应模型
public class ApiResponse<T>
{
    public bool Success { get; set; }
    public T Data { get; set; }
    public string Message { get; set; }
    public DateTime Timestamp { get; set; }
}

public class ErrorResponse
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public object Details { get; set; }
    public DateTime Timestamp { get; set; }
}

// 具体控制器示例
public class ProductsController : ApiControllerBase
{
    private readonly IProductService _productService;
    
    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }
    
    [HttpGet]
    [ProducesResponseType(typeof(ApiResponse<IEnumerable<ProductDto>>), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status500InternalServerError)]
    public async Task<ActionResult> GetProducts([FromQuery] ProductQuery query)
    {
        try
        {
            var products = await _productService.GetProductsAsync(query);
            return OkResponse(products, $"成功获取 {products.Count()} 个产品");
        }
        catch (Exception ex)
        {
            return ErrorResponse("获取产品列表失败", StatusCodes.Status500InternalServerError);
        }
    }
    
    [HttpGet("{id:int}")]
    [ProducesResponseType(typeof(ApiResponse<ProductDetailDto>), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status404NotFound)]
    public async Task<ActionResult> GetProductById(int id)
    {
        var product = await _productService.GetProductByIdAsync(id);
        
        if (product == null)
        {
            return ErrorResponse($"产品ID {id} 不存在", StatusCodes.Status404NotFound);
        }
        
        return OkResponse(product);
    }
    
    [HttpPost]
    [Authorize(Roles = "Admin,ProductManager")]
    [ValidateModel]
    [ProducesResponseType(typeof(ApiResponse<ProductDto>), StatusCodes.Status201Created)]
    [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status403Forbidden)]
    public async Task<ActionResult> CreateProduct([FromBody] CreateProductDto request)
    {
        try
        {
            var product = await _productService.CreateProductAsync(request);
            return CreatedAtAction(nameof(GetProductById), new { id = product.Id }, OkResponse(product, "产品创建成功"));
        }
        catch (ValidationException ex)
        {
            return ErrorResponse("产品创建失败", StatusCodes.Status400BadRequest, ex.Errors);
        }
    }
}

// 自定义特性
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            // 转换验证错误为字典格式
            var errors = context.ModelState
                .Where(x => x.Value.Errors.Any())
                .ToDictionary(
                    x => x.Key,
                    x => x.Value.Errors.Select(e => e.ErrorMessage).ToArray()
                );
            
            // 返回验证错误响应
            context.Result = new BadRequestObjectResult(new ErrorResponse {
                Success = false,
                Message = "请求参数验证失败",
                Details = errors,
                Timestamp = DateTime.UtcNow
            });
        }
    }
}

// 查询参数模型
public class ProductQuery
{
    [FromQuery(Name = "category")]
    public int? CategoryId { get; set; }
    
    [FromQuery(Name = "min_price")]
    [Range(0, double.MaxValue)]
    public decimal? MinPrice { get; set; }
    
    [FromQuery(Name = "max_price")]
    [Range(0, double.MaxValue)]
    public decimal? MaxPrice { get; set; }
    
    [FromQuery(Name = "page")]
    [Range(1, int.MaxValue)]
    public int Page { get; set; } = 1;
    
    [FromQuery(Name = "page_size")]
    [Range(1, 100)]
    public int PageSize { get; set; } = 20;
    
    [FromQuery(Name = "sort")]
    public string SortBy { get; set; } = "created_at";
    
    [FromQuery(Name = "order")]
    [AllowedValues("asc", "desc")]
    public string SortOrder { get; set; } = "desc";
}

// DTO模型
public class ProductDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public DateTime CreatedAt { get; set; }
    public DateTime UpdatedAt { get; set; }
}

public class ProductDetailDto : ProductDto
{
    public string Sku { get; set; }
    public int StockQuantity { get; set; }
    public string ImageUrl { get; set; }
    public List<ProductAttributeDto> Attributes { get; set; }
}

public class CreateProductDto
{
    [Required]
    [StringLength(100)]
    public string Name { get; set; }
    
    [StringLength(500)]
    public string Description { get; set; }
    
    [Required]
    [Range(0.01, double.MaxValue)]
    public decimal Price { get; set; }
    
    [Required]
    [Range(1, int.MaxValue)]
    public int CategoryId { get; set; }
    
    [Required]
    [StringLength(50)]
    public string Sku { get; set; }
    
    [Range(0, int.MaxValue)]
    public int StockQuantity { get; set; } = 0;
    
    public List<ProductAttributeCreateDto> Attributes { get; set; }
}

public class ProductAttributeDto
{
    public string Name { get; set; }
    public string Value { get; set; }
}

public class ProductAttributeCreateDto
{
    [Required]
    [StringLength(50)]
    public string Name { get; set; }
    
    [Required]
    [StringLength(100)]
    public string Value { get; set; }
}

内容处理与序列化示例

using Cyclops.Web.Core.Content;
using Cyclops.Web.Core.Serialization;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json.Serialization;

// 内容协商配置
public static class ContentNegotiationExtensions
{
    public static IMvcBuilder ConfigureContentNegotiation(this IMvcBuilder mvcBuilder)
    {
        // 配置JSON序列化选项
        mvcBuilder.AddJsonOptions(options => {
            // 忽略循环引用
            options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
            
            // 日期时间格式
            options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
            options.JsonSerializerOptions.Converters.Add(new DateTimeConverter());
            options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
            options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
        });
        
        // 添加自定义格式支持
        mvcBuilder.AddFormatterMappings(mappings => {
            mappings.SetMediaTypeMappingForFormat("xml", MediaTypeNames.Application.Xml);
            mappings.SetMediaTypeMappingForFormat("json", MediaTypeNames.Application.Json);
            mappings.SetMediaTypeMappingForFormat("csv", "text/csv");
            mappings.SetMediaTypeMappingForFormat("pdf", "application/pdf");
        });
        
        // 自定义内容协商提供程序
        mvcBuilder.ConfigureApiBehaviorOptions(options => {
            options.InvalidModelStateResponseFactory = context => {
                // 自定义验证错误响应
                var result = new ValidationFailedResult(context.ModelState);
                result.ContentTypes.Add(MediaTypeNames.Application.Json);
                return result;
            };
        });
        
        return mvcBuilder;
    }
}

// 自定义验证结果
public class ValidationFailedResult : ObjectResult
{
    public ValidationFailedResult(ModelStateDictionary modelState) : 
        base(new ValidationErrorResponse(modelState))
    {
        StatusCode = StatusCodes.Status400BadRequest;
    }
}

// 验证错误响应
public class ValidationErrorResponse
{
    public ValidationErrorResponse(ModelStateDictionary modelState)
    {
        Errors = modelState
            .Where(e => e.Value.Errors.Count > 0)
            .ToDictionary(
                k => k.Key,
                v => v.Value.Errors.Select(e => e.ErrorMessage).ToArray()
            );
        
        Success = false;
        Message = "请求参数验证失败";
        Timestamp = DateTime.UtcNow;
    }
    
    public bool Success { get; set; }
    public string Message { get; set; }
    public Dictionary<string, string[]> Errors { get; set; }
    public DateTime Timestamp { get; set; }
}

// 自定义日期时间转换器
public class DateTimeConverter : JsonConverter<DateTime>
{
    private readonly string _format = "yyyy-MM-ddTHH:mm:ss.fffZ";
    
    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (reader.TokenType == JsonTokenType.String)
        {
            if (DateTime.TryParseExact(reader.GetString(), _format, CultureInfo.InvariantCulture, DateTimeStyles.None, out var date))
            {
                return date;
            }
        }
        
        return reader.GetDateTime();
    }
    
    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(value.ToString(_format, CultureInfo.InvariantCulture));
    }
}

// 自定义格式化器
public class CsvOutputFormatter : TextOutputFormatter
{
    public CsvOutputFormatter()
    {
        SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/csv"));
        SupportedEncodings.Add(Encoding.UTF8);
        SupportedEncodings.Add(Encoding.Unicode);
    }
    
    protected override bool CanWriteType(Type type)
    {
        return typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string);
    }
    
    public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
    {
        var response = context.HttpContext.Response;
        var data = context.Object as IEnumerable;
        
        using (var writer = new StreamWriter(response.Body, selectedEncoding))
        using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
        {
            // 处理泛型集合
            var itemType = data.GetType().GetGenericArguments().FirstOrDefault();
            
            if (itemType != null)
            {
                // 自动映射属性
                csv.WriteHeader(itemType);
                csv.NextRecord();
                
                foreach (var record in data)
                {
                    csv.WriteRecord(record);
                    csv.NextRecord();
                }
            }
            
            return writer.FlushAsync();
        }
    }
}

API版本控制示例

using Cyclops.Web.Core.Versioning;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.AspNetCore.Mvc;

// API版本控制配置
public static class ApiVersioningExtensions
{
    public static IServiceCollection ConfigureApiVersioning(this IServiceCollection services)
    {
        services.AddApiVersioning(options => {
            // 使用URL路径参数作为版本控制方式
            options.ApiVersionReader = new UrlSegmentApiVersionReader();
            
            // 默认版本
            options.DefaultApiVersion = new ApiVersion(1, 0);
            
            // 允许隐式版本
            options.AssumeDefaultVersionWhenUnspecified = true;
            
            // 报告所有可用版本
            options.ReportApiVersions = true;
            
            // 版本控制错误响应
            options.ErrorResponses = new CyclopsErrorResponseProvider();
        });
        
        // 添加API探索服务
        services.AddVersionedApiExplorer(options => {
            options.GroupNameFormat = "'v'VVV";
            options.SubstituteApiVersionInUrl = true;
        });
        
        return services;
    }
}

// 自定义错误响应提供程序
public class CyclopsErrorResponseProvider : IErrorResponseProvider
{
    public IActionResult CreateErrorResponse(ErrorResponseContext context)
    {
        var errorResponse = new ErrorResponse {
            Success = false,
            Message = GetErrorMessageForError(context.ErrorCode),
            Details = context.MessageDetail,
            Timestamp = DateTime.UtcNow
        };
        
        return new ObjectResult(errorResponse) {
            StatusCode = context.StatusCode
        };
    }
    
    private string GetErrorMessageForError(string errorCode)
    {
        return errorCode switch {
            "UnsupportedApiVersion" => "不支持的API版本",
            "ApiVersionUnspecified" => "未指定API版本",
            "InvalidApiVersion" => "无效的API版本格式",
            "AmbiguousApiVersion" => "API版本不明确",
            "NoRouteMatched" => "未找到匹配的路由",
            _ => "API版本错误"
        };
    }
}

// 版本化控制器示例
[ApiController]
[Route("api/v{version:apiVersion}/products")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class ProductsV1Controller : ApiControllerBase
{
    private readonly IProductService _productService;
    
    public ProductsV1Controller(IProductService productService)
    {
        _productService = productService;
    }
    
    [HttpGet]
    [ProducesResponseType(typeof(ApiResponse<IEnumerable<ProductDto>>), StatusCodes.Status200OK)]
    public async Task<ActionResult> GetProducts([FromQuery] ProductQuery query)
    {
        var products = await _productService.GetProductsAsync(query);
        return OkResponse(products);
    }
}

// V2版本控制器(扩展V1)
[ApiController]
[Route("api/v{version:apiVersion}/products")]
[ApiVersion("2.0")]
public class ProductsV2Controller : ApiControllerBase
{
    private readonly IProductServiceV2 _productService;
    
    public ProductsV2Controller(IProductServiceV2 productService)
    {
        _productService = productService;
    }
    
    [HttpGet]
    [ProducesResponseType(typeof(ApiResponse<IEnumerable<ProductV2Dto>>), StatusCodes.Status200OK)]
    public async Task<ActionResult> GetProducts([FromQuery] ProductV2Query query)
    {
        var products = await _productService.GetProductsAsync(query);
        
        // V2版本的增强响应
        return OkResponse(products, new {
            Pagination = new {
                Total = products.TotalCount,
                Page = query.Page,
                PageSize = query.PageSize,
                Pages = products.PageCount
            },
            Filters = query.ToDictionary()
        });
    }
    
    // V2版本新增的端点
    [HttpGet("trending")]
    [ProducesResponseType(typeof(ApiResponse<IEnumerable<ProductV2Dto>>), StatusCodes.Status200OK)]
    public async Task<ActionResult> GetTrendingProducts([FromQuery] int limit = 10)
    {
        var products = await _productService.GetTrendingProductsAsync(limit);
        return OkResponse(products, "获取热门产品成功");
    }
}

版本信息

  • 当前版本 NuGet version (Cyclops.Framework)
  • 作者:yswenli
  • 描述:企服版框架中Web核心组件

贡献者

  • yswenli

许可证

保留所有权利

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on TJC.Cyclops.Web.Core:

Package Downloads
TJC.Cyclops.Wechat

企服版框架中微信对接相关业务核心项目

TJC.Cyclops.TaskSystem

企服版任务核心

TJC.Cyclops.UCenter

企服版框架中Discuz的UCenter用户同步注册、登录

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2026.2.4.1 110 2/4/2026
2026.1.15.1 120 1/15/2026
2026.1.14.2 116 1/14/2026
2026.1.14.1 107 1/14/2026
2026.1.13.2 115 1/13/2026
2026.1.13.1 131 1/13/2026
2026.1.7.1 134 1/7/2026
2025.12.23.1 219 12/23/2025
2025.12.16.1 319 12/16/2025
2025.12.15.2 268 12/15/2025
2025.12.15.1 299 12/15/2025
2025.12.12.1 173 12/12/2025
2025.12.11.1 471 12/11/2025
2025.12.4.1 233 12/4/2025
2025.12.3.3 734 12/3/2025
2025.12.3.2 713 12/3/2025
2025.12.3.1 705 12/3/2025
2025.12.2.1 719 12/2/2025
2025.11.28.1 221 11/28/2025
2025.11.25.1 234 11/25/2025
Loading failed

企服版框架中api核心功能项目,基于aspnetcore集成di、jwt、swagger、codefirtst、支持多种常见数据库、nacos配置中心、统一接口回复参数、全局异常捕获、全局接口日志、防重放攻击、图形验证码、快捷上下文对象、上传下载、数据导入导出等功能