From acd6a68507aec41aa8807929a6cf29df222e5e3e Mon Sep 17 00:00:00 2001
From: SikongJueluo <selfconfusion@gmail.com>
Date: Thu, 17 Apr 2025 16:26:12 +0800
Subject: [PATCH] use nswag to publish swagger

---
 server/Program.cs                             |  64 +++---
 server/server.csproj                          |   2 +-
 server/src/Controllers.cs                     | 186 ++++++++++++++++++
 .../src/{JtagController.cs => JtagClient.cs}  |   2 +-
 server/src/Router.cs                          | 179 -----------------
 server/src/UdpServer.cs                       |   1 -
 6 files changed, 214 insertions(+), 220 deletions(-)
 create mode 100644 server/src/Controllers.cs
 rename server/src/{JtagController.cs => JtagClient.cs} (99%)
 delete mode 100644 server/src/Router.cs

diff --git a/server/Program.cs b/server/Program.cs
index 80f8ebb..1c4b39c 100644
--- a/server/Program.cs
+++ b/server/Program.cs
@@ -1,5 +1,3 @@
-using System.Reflection;
-using Microsoft.OpenApi.Models;
 using Newtonsoft.Json;
 using NLog;
 using NLog.Web;
@@ -15,6 +13,7 @@ try
 {
     var builder = WebApplication.CreateBuilder(args);
 
+    // Services Settings
     // Add services to the container.
     builder.Services.AddControllersWithViews();
 
@@ -32,27 +31,32 @@ try
     });
 
     // Add Swagger
-    builder.Services.AddSwaggerGen(options =>
+    builder.Services.AddControllers();
+    builder.Services.AddOpenApiDocument(options =>
     {
-        options.SwaggerDoc("v1", new OpenApiInfo
+        options.PostProcess = document =>
         {
-            Title = "FPGA Web Lab API",
-            Description = "Use FPGA in the cloud",
-            Version = "v1"
-        });
-        // Generate Doc and Example
-        options.IncludeXmlComments(Assembly.GetExecutingAssembly());
-        // var executingAssembly = Assembly.GetExecutingAssembly();
-        // var xmlFilename = $"{executingAssembly.GetName().Name}.xml";
-        // options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
-        // var referencedProjectsXmlDocPaths =
-        //     executingAssembly.GetReferencedAssemblies()
-        //     .Where(assembly => assembly.Name != null && assembly.Name.StartsWith("server", StringComparison.InvariantCultureIgnoreCase))
-        //     .Select(assembly => Path.Combine(AppContext.BaseDirectory, $"{assembly.Name}.xml"))
-        //     .Where(path => File.Exists(path));
-        // foreach (var xmlDocPath in referencedProjectsXmlDocPaths) options.IncludeXmlComments(xmlDocPath);
+            document.Info = new NSwag.OpenApiInfo
+            {
+                Version = "v1",
+                Title = "FPGA Web Lab API",
+                Description = "Use FPGA in the cloud",
+                // TermsOfService = "https://example.com/terms",
+                // Contact = new NSwag.OpenApiContact
+                // {
+                //     Name = "Example Contact",
+                //     Url = "https://example.com/contact"
+                // },
+                // License = new NSwag.OpenApiLicense
+                // {
+                //     Name = "Example License",
+                //     Url = "https://example.com/license"
+                // }
+            };
+        };
     });
 
+    // Application Settings
     var app = builder.Build();
     // Configure the HTTP request pipeline.
     app.UseExceptionHandler("/Home/Error");
@@ -61,20 +65,12 @@ try
     app.UseHttpsRedirection();
     app.UseStaticFiles();
     app.UseRouting();
-
     app.UseAuthorization();
 
-    app.MapControllerRoute(
-        name: "default",
-        pattern: "{controller=Home}/{action=Index}/{id?}");
-
     // if (app.Environment.IsDevelopment())
     // {
-    app.UseSwagger();
-    app.UseSwaggerUI(c =>
-    {
-        c.SwaggerEndpoint("/swagger/v1/swagger.json", "FPAG WebLab API V1");
-    });
+    app.UseOpenApi();
+    app.UseSwaggerUi();
     // }
 
     // Setup Program
@@ -83,15 +79,7 @@ try
     // Router
     // API Get
     app.MapGet("/", () => Results.Redirect("/swagger"));
-    app.MapGet("/api/GetRecvDataArray", Router.API.GetRecvDataArray);
-    // API Post
-    app.MapPost("/api/SendString", Router.API.SendString);
-    app.MapPost("/api/SendBytes", Router.API.SendBytes);
-    app.MapPost("/api/SendAddrPackage", Router.API.SendAddrPackage);
-    app.MapPost("/api/SendDataPackage", Router.API.SendDataPackage);
-    // API Jtag
-    app.MapPost("/api/jtag/RunCommand", Router.API.Jtag.RunCommand);
-    app.MapGet("/api/jtag/GetIDCode", Router.API.Jtag.GetDeviceIDCode);
+    app.MapControllers();
 
     app.Run("http://localhost:5000");
 }
diff --git a/server/server.csproj b/server/server.csproj
index a903e41..dca031e 100644
--- a/server/server.csproj
+++ b/server/server.csproj
@@ -18,7 +18,7 @@
     <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
     <PackageReference Include="NLog" Version="5.4.0" />
     <PackageReference Include="NLog.Web.AspNetCore" Version="5.4.0" />
-    <PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.0" />
+    <PackageReference Include="NSwag.AspNetCore" Version="14.3.0" />
   </ItemGroup>
 
 </Project>
diff --git a/server/src/Controllers.cs b/server/src/Controllers.cs
new file mode 100644
index 0000000..d563152
--- /dev/null
+++ b/server/src/Controllers.cs
@@ -0,0 +1,186 @@
+using System.Net;
+using Common;
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json;
+using WebProtocol;
+
+namespace server.Controllers;
+
+/// <summary>
+/// UDP API 
+/// </summary>
+[ApiController]
+[Route("api/[controller]")]
+public class UDPController : ControllerBase
+{
+    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
+
+    private const string LOCALHOST = "127.0.0.1";
+
+    /// <summary>
+    /// 页面
+    /// </summary>
+    [HttpGet]
+    public string Index()
+    {
+        return "This is UDP Controller";
+    }
+
+    /// <summary>
+    /// 发送字符串
+    /// </summary>
+    /// <param name="address">IPV4 或者 IPV6 地址</param>
+    /// <param name="port">设备端口号</param>
+    /// <param name="text">发送的文本</param>
+    /// <response code="200">发送成功</response>
+    /// <response code="500">发送失败</response>
+    [HttpPost("SendString")]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+    public async ValueTask<IResult> SendString(string address = LOCALHOST, int port = 1234, string text = "Hello Server!")
+    {
+        var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
+        var ret = await UDPClientPool.SendStringAsync(endPoint, [text]);
+
+        if (ret) { return TypedResults.Ok(); }
+        else { return TypedResults.InternalServerError(); }
+    }
+
+    /// <summary>
+    /// 发送二进制数据
+    /// </summary>
+    /// <param name="address" example="127.0.0.1">IPV4 或者 IPV6 地址</param>
+    /// <param name="port" example="1234">设备端口号</param>
+    /// <param name="bytes" example="FFFFAAAA">16进制文本</param>
+    [HttpPost("SendBytes")]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+    public async ValueTask<IResult> SendBytes(string address, int port, string bytes)
+    {
+        var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
+        var ret = await UDPClientPool.SendBytesAsync(endPoint, Number.StringToBytes(bytes));
+
+        if (ret) { return TypedResults.Ok(); }
+        else { return TypedResults.InternalServerError(); }
+    }
+
+
+    /// <summary>
+    /// 发送地址包
+    /// </summary>
+    /// <param name="address">IP地址</param>
+    /// <param name="port">UDP 端口号</param>
+    /// <param name="opts">地址包选项</param>
+    [HttpPost("SendAddrPackage")]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+    public async ValueTask<IResult> SendAddrPackage(
+        string address,
+        int port,
+        [FromBody] SendAddrPackOptions opts)
+    {
+        var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
+        var ret = await UDPClientPool.SendAddrPackAsync(endPoint, new WebProtocol.SendAddrPackage(opts));
+
+        if (ret) { return TypedResults.Ok(); }
+        else { return TypedResults.InternalServerError(); }
+    }
+
+    /// <summary>
+    /// 发送数据包
+    /// </summary>
+    /// <param name="address">IP地址</param>
+    /// <param name="port">UDP 端口号</param>
+    /// <param name="data">16进制数据</param>
+    [HttpPost("SendDataPackage")]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+    public async ValueTask<IResult> SendDataPackage(string address, int port, string data)
+    {
+        var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
+        var ret = await UDPClientPool.SendDataPackAsync(endPoint,
+            new WebProtocol.SendDataPackage(Number.StringToBytes(data)));
+
+        if (ret) { return TypedResults.Ok(); }
+        else { return TypedResults.InternalServerError(); }
+    }
+
+    /// <summary>
+    /// 获取指定IP地址接受的数据列表
+    /// </summary>
+    /// <param name="address">IP地址</param>
+    [HttpGet("GetRecvDataArray")]
+    [ProducesResponseType(typeof(List<UDPData>), StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+    public async ValueTask<IResult> GetRecvDataArray(string address)
+    {
+        var ret = await MsgBus.UDPServer.GetDataArrayAsync(address);
+
+        if (ret.HasValue)
+        {
+            var dataJson = JsonConvert.SerializeObject(ret.Value);
+            logger.Debug($"Get Receive Successfully: {dataJson}");
+
+            return TypedResults.Ok(ret.Value);
+        }
+        else
+        {
+            logger.Debug("Get Receive Failed");
+            return TypedResults.InternalServerError();
+        }
+    }
+
+}
+
+/// <summary>
+/// Jtag API
+/// </summary>
+[ApiController]
+[Route("api/[controller]")]
+public class JtagController : ControllerBase
+{
+    /// <summary>
+    /// 页面
+    /// </summary>
+    [HttpGet]
+    public string Index()
+    {
+        return "This is Jtag Controller";
+    }
+    /// <summary>
+    /// 执行一个Jtag命令
+    /// </summary>
+    /// <param name="address"> 设备地址 </param>
+    /// <param name="port"> 设备端口 </param>
+    /// <param name="hexDevAddr"> 16进制设备目的地址(Jtag) </param>
+    /// <param name="hexCmd"> 16进制命令 </param>
+    [HttpPost("RunCommand")]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+    public async ValueTask<IResult> RunCommand(string address, int port, string hexDevAddr, string hexCmd)
+    {
+        var jtagCtrl = new JtagClient.Jtag(address, port);
+        var ret = await jtagCtrl.RunCommand(Convert.ToUInt32(hexDevAddr, 16), Convert.ToUInt32(hexCmd, 16));
+
+        if (ret.IsSuccessful) { return TypedResults.Ok(ret.Value); }
+        else { return TypedResults.InternalServerError(ret.Error); }
+    }
+
+
+    /// <summary>
+    /// 获取Jtag ID Code
+    /// </summary>
+    /// <param name="address"> 设备地址 </param>
+    /// <param name="port"> 设备端口 </param>
+    [HttpGet("GetDeviceIDCode")]
+    [ProducesResponseType(StatusCodes.Status200OK)]
+    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
+    public async ValueTask<IResult> GetDeviceIDCode(string address, int port)
+    {
+        var jtagCtrl = new JtagClient.Jtag(address, port);
+        var ret = await jtagCtrl.ReadIDCode();
+
+        if (ret.IsSuccessful) { return TypedResults.Ok(ret.Value); }
+        else { return TypedResults.InternalServerError(ret.Error); }
+    }
+}
diff --git a/server/src/JtagController.cs b/server/src/JtagClient.cs
similarity index 99%
rename from server/src/JtagController.cs
rename to server/src/JtagClient.cs
index 3146ef1..5fb6e4e 100644
--- a/server/src/JtagController.cs
+++ b/server/src/JtagClient.cs
@@ -3,7 +3,7 @@ using Common;
 using DotNext;
 using WebProtocol;
 
-namespace JtagController;
+namespace JtagClient;
 
 /// <summary>
 /// Global Constant Jtag Address
diff --git a/server/src/Router.cs b/server/src/Router.cs
deleted file mode 100644
index 34d92ac..0000000
--- a/server/src/Router.cs
+++ /dev/null
@@ -1,179 +0,0 @@
-using System.Net;
-using Common;
-using Microsoft.AspNetCore.Mvc;
-using Newtonsoft.Json;
-using WebProtocol;
-
-namespace Router
-{
-    /// <summary>
-    /// Custom Web Http Response
-    /// </summary>
-    public class Response
-    {
-        /// <summary>
-        /// 是否成功执行
-        /// </summary>
-        public bool IsSuccess { get; set; }
-
-        /// <summary>
-        /// 数据
-        /// </summary>
-        public object? Data { get; set; }
-
-        /// <summary>
-        /// 转换为Json格式的字符串
-        /// </summary>
-        /// <returns>Json字符串</returns>
-        public override string ToString()
-        {
-            return JsonConvert.SerializeObject(this);
-        }
-    }
-
-    /// <summary>
-    /// Web API 
-    /// </summary>
-    class API
-    {
-        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
-
-        private const string LOCALHOST = "127.0.0.1";
-
-        /// <summary>
-        /// 发送字符串
-        /// </summary>
-        /// <param name="address" example="127.0.0.1">IPV4 或者 IPV6 地址</param>
-        /// <param name="port" example="1234">设备端口号</param>
-        /// <param name="text" example="Hello Server!">Text for send</param>
-        [HttpPost("{address}/{port}")]
-        [ProducesResponseType(StatusCodes.Status200OK)]
-        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
-        public static async ValueTask<IResult> SendString(string address, int port, string text)
-        {
-            var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
-            var ret = await UDPClientPool.SendStringAsync(endPoint, [text]);
-
-            if (ret) { return TypedResults.Ok(); }
-            else { return TypedResults.InternalServerError(); }
-        }
-
-        /// <summary>
-        /// 发送二进制数据
-        /// </summary>
-        /// <param name="address" example="127.0.0.1">IPV4 或者 IPV6 地址</param>
-        /// <param name="port" example="1234">设备端口号</param>
-        /// <param name="bytes" example="FFFFAAAA">16进制文本</param>
-        [HttpPost("{address}/{port}")]
-        [ProducesResponseType(StatusCodes.Status200OK)]
-        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
-        public static async ValueTask<IResult> SendBytes(string address, int port, string bytes)
-        {
-            var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
-            var ret = await UDPClientPool.SendBytesAsync(endPoint, Number.StringToBytes(bytes));
-
-            if (ret) { return TypedResults.Ok(); }
-            else { return TypedResults.InternalServerError(); }
-        }
-
-
-        /// <summary>
-        /// 发送地址包
-        /// </summary>
-        /// <param name="address" example="127.0.0.1">IP地址</param>
-        /// <param name="port" example="1234">UDP 端口号</param>
-        /// <param name="opts">地址包选项</param>
-        [HttpPost("{address}/{port}")]
-        [ProducesResponseType(StatusCodes.Status200OK)]
-        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
-        public static async ValueTask<IResult> SendAddrPackage(
-            string address,
-            int port,
-            [FromBody] SendAddrPackOptions opts)
-        {
-            var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
-            var ret = await UDPClientPool.SendAddrPackAsync(endPoint, new WebProtocol.SendAddrPackage(opts));
-
-            if (ret) { return TypedResults.Ok(); }
-            else { return TypedResults.InternalServerError(); }
-        }
-
-        [HttpPost("{address}/{port}")]
-        [ProducesResponseType(StatusCodes.Status200OK)]
-        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
-        public static async ValueTask<IResult> SendDataPackage(string address, int port, string data)
-        {
-            var endPoint = new IPEndPoint(IPAddress.Parse(address), port);
-            var ret = await UDPClientPool.SendDataPackAsync(endPoint,
-                new WebProtocol.SendDataPackage(Number.StringToBytes(data)));
-
-            if (ret) { return TypedResults.Ok(); }
-            else { return TypedResults.InternalServerError(); }
-        }
-
-        [HttpGet("{address}")]
-        [ProducesResponseType(typeof(List<UDPData>), StatusCodes.Status200OK)]
-        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
-        public static async ValueTask<IResult> GetRecvDataArray(string address)
-        {
-            var ret = await MsgBus.UDPServer.GetDataArrayAsync(address);
-
-            if (ret.HasValue)
-            {
-                var dataJson = JsonConvert.SerializeObject(ret.Value);
-                logger.Debug($"Get Receive Successfully: {dataJson}");
-
-                return TypedResults.Ok(ret.Value);
-            }
-            else
-            {
-                logger.Debug("Get Receive Failed");
-                return TypedResults.InternalServerError();
-            }
-        }
-
-        public class Jtag
-        {
-            private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
-
-            /// <summary>
-            /// 执行一个Jtag命令
-            /// </summary>
-            /// <param name="address"> 设备地址 </param>
-            /// <param name="port"> 设备端口 </param>
-            /// <param name="hexDevAddr"> 16进制设备目的地址(Jtag) </param>
-            /// <param name="hexCmd"> 16进制命令 </param>
-            [HttpPost]
-            [ProducesResponseType(StatusCodes.Status200OK)]
-            [ProducesResponseType(StatusCodes.Status500InternalServerError)]
-            public static async ValueTask<IResult> RunCommand(string address, int port, string hexDevAddr, string hexCmd)
-            {
-                var jtagCtrl = new JtagController.Jtag(address, port);
-                var ret = await jtagCtrl.RunCommand(Convert.ToUInt32(hexDevAddr, 16), Convert.ToUInt32(hexCmd, 16));
-
-                if (ret.IsSuccessful) { return TypedResults.Ok(ret.Value); }
-                else { return TypedResults.InternalServerError(ret.Error); }
-            }
-
-
-            /// <summary>
-            /// 获取Jtag ID Code
-            /// </summary>
-            /// <param name="address"> 设备地址 </param>
-            /// <param name="port"> 设备端口 </param>
-            [HttpGet]
-            [ProducesResponseType(StatusCodes.Status200OK)]
-            [ProducesResponseType(StatusCodes.Status500InternalServerError)]
-            public static async ValueTask<IResult> GetDeviceIDCode(string address, int port)
-            {
-                var jtagCtrl = new JtagController.Jtag(address, port);
-                var ret = await jtagCtrl.ReadIDCode();
-
-                if (ret.IsSuccessful) { return TypedResults.Ok(ret.Value); }
-                else { return TypedResults.InternalServerError(ret.Error); }
-            }
-
-
-        }
-    }
-}
diff --git a/server/src/UdpServer.cs b/server/src/UdpServer.cs
index e4eb4ff..1295bbe 100644
--- a/server/src/UdpServer.cs
+++ b/server/src/UdpServer.cs
@@ -2,7 +2,6 @@ using System.Net;
 using System.Net.Sockets;
 using System.Runtime.CompilerServices;
 using System.Text;
-using System.Threading.Tasks;
 using DotNext;
 using DotNext.Threading;
 using Newtonsoft.Json;