云计算百科
云计算领域专业知识百科平台

工业上位机接口规范设计:RESTful API + JSON契约

以下是针对《工业上位机接口规范设计:RESTful API + JSON契约》的更多代码示例补充,主要覆盖 .NET 8/9 时代最常见的几种实现方式,以及实际工业项目中最常遇到的几种场景。

我将按以下分类给出更详细、可直接复制粘贴的示例代码:

  • 统一响应模型(基类 + 泛型)
  • 全局异常处理中间件
  • 控制器常用写法(GET / PATCH / POST 带幂等)
  • 参数校验 + 业务错误码
  • OpenAPI 文档增强(Swagger 配置)
  • 幂等性中间件(简单实现)
  • 客户端调用示例(Refit 接口定义)
  • 1. 统一响应模型(推荐做法)

    // ResponseBase.cs
    public abstract class ResponseBase
    {
    public int Code { get; set; } = 0;
    public string Message { get; set; } = "success";
    public string TraceId { get; set; } = Activity.Current?.Id ?? Guid.NewGuid().ToString("N");
    public DateTime Timestamp { get; set; } = DateTime.UtcNow;
    }

    // 成功响应
    public class ApiResponse<T> : ResponseBase
    {
    public T? Data { get; set; }

    public static ApiResponse<T> Success(T data) => new() { Data = data };
    public static ApiResponse<T> Success() => new();
    }

    // 失败响应
    public class ApiErrorResponse : ResponseBase
    {
    public List<ErrorDetail>? Errors { get; set; }

    public static ApiErrorResponse Fail(int code, string message, List<ErrorDetail>? errors = null)
    {
    return new ApiErrorResponse
    {
    Code = code,
    Message = message,
    Errors = errors
    };
    }
    }

    public class ErrorDetail
    {
    public string? Field { get; set; }
    public string? Reason { get; set; }
    public object? Value { get; set; }
    }

    2. 全局异常处理中间件(推荐放在 Program.cs)

    // ExceptionMiddleware.cs
    public class ExceptionMiddleware
    {
    private readonly RequestDelegate _next;
    private readonly ILogger<ExceptionMiddleware> _logger;

    public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
    {
    _next = next;
    _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
    try
    {
    await _next(context);
    }
    catch (ValidationException ex) // FluentValidation
    {
    context.Response.StatusCode = StatusCodes.Status400BadRequest;
    var errors = ex.Errors.Select(e => new ErrorDetail
    {
    Field = e.PropertyName,
    Reason = e.ErrorMessage,
    Value = e.AttemptedValue
    }).ToList();

    await context.Response.WriteAsJsonAsync(ApiErrorResponse.Fail(1001, "参数校验失败", errors));
    }
    catch (BusinessException ex) // 自定义业务异常
    {
    context.Response.StatusCode = StatusCodes.Status400BadRequest;
    await context.Response.WriteAsJsonAsync(ApiErrorResponse.Fail(ex.Code, ex.Message));
    }
    catch (Exception ex)
    {
    _logger.LogError(ex, "未处理的异常发生。TraceId: {TraceId}", Activity.Current?.Id);
    context.Response.StatusCode = StatusCodes.Status500InternalServerError;
    await context.Response.WriteAsJsonAsync(ApiErrorResponse.Fail(9999, "服务器内部错误,请联系管理员"));
    }
    }
    }

    // Program.cs 中使用
    app.UseMiddleware<ExceptionMiddleware>();

    3. 控制器常用写法示例

    [ApiController]
    [Route("api/v{version:apiVersion}/[controller]")]
    [ApiVersion("1.0")]
    public class DevicesController : ControllerBase
    {
    private readonly IDeviceService _deviceService;
    private readonly IIdempotencyService _idempotencyService;

    public DevicesController(IDeviceService deviceService, IIdempotencyService idempotencyService)
    {
    _deviceService = deviceService;
    _idempotencyService = idempotencyService;
    }

    /// <summary>
    /// 获取单台设备实时状态
    /// </summary>
    [HttpGet("{deviceId}/status")]
    [ProducesResponseType(typeof(ApiResponse<DeviceStatusDto>), 200)]
    [ProducesResponseType(typeof(ApiErrorResponse), 400)]
    public async Task<IActionResult> GetStatus(string deviceId)
    {
    var status = await _deviceService.GetDeviceStatusAsync(deviceId);
    return Ok(ApiResponse<DeviceStatusDto>.Success(status));
    }

    /// <summary>
    /// 局部更新设备参数(幂等)
    /// </summary>
    [HttpPatch("{deviceId}/parameters")]
    [ProducesResponseType(204)]
    [ProducesResponseType(typeof(ApiErrorResponse), 400)]
    public async Task<IActionResult> UpdateParameters(string deviceId, [FromBody] UpdateDeviceParametersRequest request)
    {
    await _deviceService.UpdateParametersAsync(deviceId, request);
    return NoContent();
    }

    /// <summary>
    /// 下发生产指令(带幂等键)
    /// </summary>
    [HttpPost("commands")]
    [ProducesResponseType(typeof(ApiResponse<CommandResult>), 200)]
    [ProducesResponseType(typeof(ApiErrorResponse), 400)]
    public async Task<IActionResult> IssueCommand([FromBody] IssueCommandRequest request)
    {
    // 检查幂等键
    if (await _idempotencyService.ExistsAsync(request.IdempotencyKey))
    {
    var previous = await _idempotencyService.GetResultAsync(request.IdempotencyKey);
    return Ok(ApiResponse<CommandResult>.Success(previous));
    }

    var result = await _deviceService.IssueCommandAsync(request);

    // 记录幂等结果(建议异步 + 缓存/数据库)
    await _idempotencyService.SaveResultAsync(request.IdempotencyKey, result, TimeSpan.FromDays(7));

    return Ok(ApiResponse<CommandResult>.Success(result));
    }
    }

    4. DTO 示例(带 FluentValidation 校验)

    public class UpdateDeviceParametersRequest
    {
    public int? TargetSpeed { get; set; }
    public bool? AutoMode { get; set; }
    public string? RecipeId { get; set; }
    }

    public class UpdateDeviceParametersRequestValidator : AbstractValidator<UpdateDeviceParametersRequest>
    {
    public UpdateDeviceParametersRequestValidator()
    {
    RuleFor(x => x.TargetSpeed)
    .InclusiveBetween(800, 3000).When(x => x.TargetSpeed.HasValue)
    .WithMessage("目标速度必须在800-3000之间");

    RuleFor(x => x.RecipeId)
    .MaximumLength(50).When(x => !string.IsNullOrEmpty(x.RecipeId));
    }
    }

    5. Swagger / OpenAPI 配置增强(Program.cs)

    builder.Services.AddSwaggerGen(c =>
    {
    c.SwaggerDoc("v1", new OpenApiInfo
    {
    Title = "工业上位机 REST API",
    Version = "v1.0",
    Description = "工业现场上位机标准接口(RESTful + JSON契约)"
    });

    // 支持 ApiVersion
    c.OperationFilter<AddApiVersionHeader>();

    // JWT 示例
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
    In = ParameterLocation.Header,
    Description = "请输入 Bearer Token",
    Name = "Authorization",
    Type = SecuritySchemeType.ApiKey,
    Scheme = "Bearer"
    });

    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
    {
    new OpenApiSecurityScheme
    {
    Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
    },
    Array.Empty<string>()
    }
    });
    });

    6. 简单幂等中间件(可选,适合轻量项目)

    public class IdempotencyMiddleware
    {
    private readonly RequestDelegate _next;
    private readonly IIdempotencyService _idempotencyService;

    public IdempotencyMiddleware(RequestDelegate next, IIdempotencyService idempotencyService)
    {
    _next = next;
    _idempotencyService = idempotencyService;
    }

    public async Task InvokeAsync(HttpContext context)
    {
    if (context.Request.Method == HttpMethods.Post &&
    context.Request.Headers.TryGetValue("Idempotency-Key", out var idempotencyKeyValues))
    {
    var key = idempotencyKeyValues.FirstOrDefault();
    if (!string.IsNullOrEmpty(key) && await _idempotencyService.ExistsAsync(key))
    {
    var result = await _idempotencyService.GetResultAsync(key);
    context.Response.StatusCode = StatusCodes.Status200OK;
    await context.Response.WriteAsJsonAsync(result);
    return;
    }
    }

    await _next(context);
    }
    }

    7. 客户端调用示例(Refit 接口定义)

    public interface IDeviceApiClient
    {
    [Get("/api/v1/devices/{deviceId}/status")]
    Task<ApiResponse<DeviceStatusDto>> GetDeviceStatus(string deviceId);

    [Patch("/api/v1/devices/{deviceId}/parameters")]
    Task UpdateParameters(string deviceId, [Body] UpdateDeviceParametersRequest request);

    [Post("/api/v1/production/commands")]
    [Headers("Idempotency-Key: {idempotencyKey}")]
    Task<ApiResponse<CommandResult>> IssueCommand(string idempotencyKey, [Body] IssueCommandRequest request);
    }

    这些代码示例覆盖了目前工业上位机项目中最常见的 80% 场景。如果你需要某个具体接口的完整 Controller + Service + DTO + Validator 组合,或者想看 Golang / Python / Java 的对应实现,也可以继续告诉我,我可以继续补充。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 工业上位机接口规范设计:RESTful API + JSON契约
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!