|
| 1 | +using Juggle.Application.Models.Request; |
| 2 | +using Juggle.Application.Models.Response; |
| 3 | +using Juggle.Domain.Entities; |
| 4 | +using Juggle.Infrastructure.Persistence; |
| 5 | +using Microsoft.AspNetCore.Authorization; |
| 6 | +using Microsoft.AspNetCore.Mvc; |
| 7 | +using Microsoft.EntityFrameworkCore; |
| 8 | +using System.Text.Json; |
| 9 | + |
| 10 | +namespace Juggle.Api.Controllers.Api; |
| 11 | + |
| 12 | +[ApiController] |
| 13 | +[Route("api/suite/api")] |
| 14 | +[Authorize] |
| 15 | +public class ApiController : ControllerBase |
| 16 | +{ |
| 17 | + private readonly JuggleDbContext _db; |
| 18 | + private readonly IHttpClientFactory _httpClientFactory; |
| 19 | + |
| 20 | + public ApiController(JuggleDbContext db, IHttpClientFactory httpClientFactory) |
| 21 | + { |
| 22 | + _db = db; |
| 23 | + _httpClientFactory = httpClientFactory; |
| 24 | + } |
| 25 | + |
| 26 | + [HttpPost("add")] |
| 27 | + public async Task<ApiResult> Add([FromBody] ApiAddRequest req) |
| 28 | + { |
| 29 | + // 检查套件是否存在 |
| 30 | + var suite = await _db.Suites.FirstOrDefaultAsync(s => s.SuiteCode == req.SuiteCode && s.Deleted == 0); |
| 31 | + if (suite == null) return ApiResult.Fail("套件不存在"); |
| 32 | + |
| 33 | + var code = $"api_{Guid.NewGuid():N}"; |
| 34 | + var entity = new ApiEntity |
| 35 | + { |
| 36 | + SuiteCode = req.SuiteCode, |
| 37 | + MethodCode = code, |
| 38 | + MethodName = req.MethodName, |
| 39 | + MethodDesc = req.MethodDesc, |
| 40 | + Url = req.Url, |
| 41 | + RequestType = req.RequestType, |
| 42 | + ContentType = req.ContentType, |
| 43 | + MockJson = req.MockJson, |
| 44 | + MethodType = req.MethodType, |
| 45 | + CreatedAt = DateTime.Now.ToString("o") |
| 46 | + }; |
| 47 | + _db.Apis.Add(entity); |
| 48 | + await _db.SaveChangesAsync(); |
| 49 | + return ApiResult.Success(entity.Id); |
| 50 | + } |
| 51 | + |
| 52 | + [HttpDelete("delete/{id}")] |
| 53 | + public async Task<ApiResult> Delete(long id) |
| 54 | + { |
| 55 | + var entity = await _db.Apis.FindAsync(id); |
| 56 | + if (entity == null) return ApiResult.Fail("接口不存在"); |
| 57 | + entity.Deleted = 1; |
| 58 | + entity.UpdatedAt = DateTime.Now.ToString("o"); |
| 59 | + await _db.SaveChangesAsync(); |
| 60 | + return ApiResult.Success(); |
| 61 | + } |
| 62 | + |
| 63 | + [HttpPut("update")] |
| 64 | + public async Task<ApiResult> Update([FromBody] ApiUpdateRequest req) |
| 65 | + { |
| 66 | + var entity = await _db.Apis.FindAsync(req.Id); |
| 67 | + if (entity == null) return ApiResult.Fail("接口不存在"); |
| 68 | + entity.MethodName = req.MethodName; |
| 69 | + entity.MethodDesc = req.MethodDesc; |
| 70 | + entity.Url = req.Url; |
| 71 | + entity.RequestType = req.RequestType; |
| 72 | + entity.ContentType = req.ContentType; |
| 73 | + entity.MockJson = req.MockJson; |
| 74 | + entity.MethodType = req.MethodType; |
| 75 | + entity.UpdatedAt = DateTime.Now.ToString("o"); |
| 76 | + await _db.SaveChangesAsync(); |
| 77 | + return ApiResult.Success(); |
| 78 | + } |
| 79 | + |
| 80 | + [HttpGet("info/{id}")] |
| 81 | + public async Task<ApiResult> Info(long id) |
| 82 | + { |
| 83 | + var entity = await _db.Apis.FindAsync(id); |
| 84 | + if (entity == null || entity.Deleted == 1) return ApiResult.Fail("接口不存在"); |
| 85 | + var inputParams = await _db.Parameters.Where(p => p.OwnerId == id && p.ParamType == 1 && p.Deleted == 0).OrderBy(p => p.SortNum).ToListAsync(); |
| 86 | + var outputParams = await _db.Parameters.Where(p => p.OwnerId == id && p.ParamType == 2 && p.Deleted == 0).OrderBy(p => p.SortNum).ToListAsync(); |
| 87 | + var headerParams = await _db.Parameters.Where(p => p.OwnerId == id && p.ParamType == 4 && p.Deleted == 0).OrderBy(p => p.SortNum).ToListAsync(); |
| 88 | + return ApiResult.Success(new { api = entity, inputParams, outputParams, headerParams }); |
| 89 | + } |
| 90 | + |
| 91 | + [HttpPost("list")] |
| 92 | + public async Task<ApiResult> List([FromBody] dynamic req) |
| 93 | + { |
| 94 | + string suiteCode = req.GetProperty("suiteCode").GetString() ?? ""; |
| 95 | + var list = await _db.Apis.Where(a => a.SuiteCode == suiteCode && a.Deleted == 0).OrderBy(a => a.Id).ToListAsync(); |
| 96 | + return ApiResult.Success(list); |
| 97 | + } |
| 98 | + |
| 99 | + [HttpPost("debug")] |
| 100 | + public async Task<ApiResult> Debug([FromBody] ApiDebugRequest req) |
| 101 | + { |
| 102 | + var api = await _db.Apis.FindAsync(req.ApiId); |
| 103 | + if (api == null) return ApiResult.Fail("接口不存在"); |
| 104 | + |
| 105 | + var methodType = api.MethodType?.ToUpper() ?? "HTTP"; |
| 106 | + |
| 107 | + try |
| 108 | + { |
| 109 | + // Mock 模式:如果接口配置了 MockJson,直接返回预设数据 |
| 110 | + if (!string.IsNullOrEmpty(api.MockJson)) |
| 111 | + { |
| 112 | + return ApiResult.Success(new { response = api.MockJson, mock = true }); |
| 113 | + } |
| 114 | + |
| 115 | + // WebService(SOAP 1.1)调试 |
| 116 | + if (methodType == "WEBSERVICE") |
| 117 | + { |
| 118 | + return await DebugWebService(api, req); |
| 119 | + } |
| 120 | + |
| 121 | + // HTTP 调用(原有逻辑) |
| 122 | + var client = _httpClientFactory.CreateClient(); |
| 123 | + foreach (var h in req.Headers) |
| 124 | + client.DefaultRequestHeaders.TryAddWithoutValidation(h.Key, h.Value?.ToString()); |
| 125 | + |
| 126 | + string responseJson; |
| 127 | + var requestType = api.RequestType?.ToUpper() ?? "GET"; |
| 128 | + |
| 129 | + if (requestType == "GET" || requestType == "DELETE") |
| 130 | + { |
| 131 | + var url = api.Url!; |
| 132 | + if (req.Params.Count > 0) |
| 133 | + { |
| 134 | + var query = string.Join("&", req.Params.Select(kv => |
| 135 | + $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value?.ToString() ?? "")}")); |
| 136 | + url = url.Contains('?') ? $"{url}&{query}" : $"{url}?{query}"; |
| 137 | + } |
| 138 | + var resp = requestType == "GET" ? await client.GetAsync(url) : await client.DeleteAsync(url); |
| 139 | + responseJson = await resp.Content.ReadAsStringAsync(); |
| 140 | + } |
| 141 | + else |
| 142 | + { |
| 143 | + var content = new StringContent( |
| 144 | + JsonSerializer.Serialize(req.Params), |
| 145 | + System.Text.Encoding.UTF8, "application/json"); |
| 146 | + var resp = requestType == "PUT" ? await client.PutAsync(api.Url, content) : await client.PostAsync(api.Url, content); |
| 147 | + responseJson = await resp.Content.ReadAsStringAsync(); |
| 148 | + } |
| 149 | + |
| 150 | + return ApiResult.Success(new { response = responseJson }); |
| 151 | + } |
| 152 | + catch (Exception ex) |
| 153 | + { |
| 154 | + return ApiResult.Fail($"调用失败: {ex.Message}"); |
| 155 | + } |
| 156 | + } |
| 157 | + |
| 158 | + /// <summary>调试 WebService(SOAP 1.1)接口</summary> |
| 159 | + private async Task<ApiResult> DebugWebService(ApiEntity api, ApiDebugRequest req) |
| 160 | + { |
| 161 | + // 从 Url 中提取 soapAction(可通过 Header "SOAPAction" 传入,也可为空) |
| 162 | + var soapAction = req.Headers.ContainsKey("SOAPAction") ? req.Headers["SOAPAction"]?.ToString() ?? "" : ""; |
| 163 | + |
| 164 | + // 构建 SOAP 1.1 请求体:用入参 key/value 组装 XML 参数 |
| 165 | + var paramXml = string.Join("", req.Params.Select(kv => |
| 166 | + $"<{kv.Key}>{System.Security.SecurityElement.Escape(kv.Value?.ToString() ?? "")}</{kv.Key}>")); |
| 167 | + |
| 168 | + // 从 Url 中解析 method name(取路径最后一段,例如 http://ws/HelloService?op=SayHello → SayHello) |
| 169 | + var methodName = "Request"; |
| 170 | + var uri = new Uri(api.Url!); |
| 171 | + // 解析 ?op=MethodName 参数 |
| 172 | + var queryParams = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(uri.Query); |
| 173 | + if (queryParams.TryGetValue("op", out var opVal) && !string.IsNullOrEmpty(opVal)) |
| 174 | + methodName = opVal!; |
| 175 | + |
| 176 | + var soapBody = $@"<?xml version=""1.0"" encoding=""utf-8""?> |
| 177 | +<soap:Envelope xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema""> |
| 178 | + <soap:Body> |
| 179 | + <{methodName} xmlns=""{uri.GetLeftPart(UriPartial.Path)}""> |
| 180 | + {paramXml} |
| 181 | + </{methodName}> |
| 182 | + </soap:Body> |
| 183 | +</soap:Envelope>"; |
| 184 | + |
| 185 | + var client = _httpClientFactory.CreateClient(); |
| 186 | + // SOAP 1.1 必须 POST,Content-Type: text/xml |
| 187 | + var content = new StringContent(soapBody, System.Text.Encoding.UTF8, "text/xml"); |
| 188 | + if (!string.IsNullOrEmpty(soapAction)) |
| 189 | + content.Headers.Add("SOAPAction", $"\"{soapAction}\""); |
| 190 | + |
| 191 | + // 附加其他 headers |
| 192 | + foreach (var h in req.Headers) |
| 193 | + if (h.Key != "SOAPAction") |
| 194 | + client.DefaultRequestHeaders.TryAddWithoutValidation(h.Key, h.Value?.ToString()); |
| 195 | + |
| 196 | + var resp = await client.PostAsync(api.Url, content); |
| 197 | + var responseText = await resp.Content.ReadAsStringAsync(); |
| 198 | + return ApiResult.Success(new { response = responseText, soapBody }); |
| 199 | + } |
| 200 | +} |
0 commit comments