refactor(api): fetchAllConfigs 接口从 GET 改为 POST

- 服务端: 新增 FetchAllRequest 结构体,参数通过 JSON body 传递
- 服务端: 路由从 api.GET 改为 api.POST
- Go/Python/Node.js/Java 客户端同步更新为 POST + JSON body
- 与 fetchChangedConfigs 接口风格统一,token 不再暴露在 URL 中
This commit is contained in:
2026-02-20 21:18:43 +08:00
parent b011b3a0b3
commit 08b2ba4c25
7 changed files with 88 additions and 37 deletions
+16 -3
View File
@@ -1,6 +1,7 @@
package client package client
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@@ -107,14 +108,26 @@ func (c *Client) Stop() {
} }
func (c *Client) fetchAll() error { func (c *Client) fetchAll() error {
url := fmt.Sprintf("%s/api/fetchAllConfigs?appName=%s&env=%s&version=%s&ignoreGlobal=%v", url := fmt.Sprintf("%s/api/fetchAllConfigs", c.config.BaseURL)
c.config.BaseURL, c.config.AppName, c.config.Env, c.config.Version, c.config.IgnoreGlobal)
req, err := http.NewRequest("GET", url, nil) payload := map[string]interface{}{
"appName": c.config.AppName,
"env": c.config.Env,
"version": c.config.Version,
"ignoreGlobal": c.config.IgnoreGlobal,
"token": c.config.Token,
}
payloadBytes, err := json.Marshal(payload)
if err != nil { if err != nil {
return err return err
} }
req, err := http.NewRequest("POST", url, bytes.NewReader(payloadBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(req) resp, err := c.httpClient.Do(req)
if err != nil { if err != nil {
return err return err
@@ -365,11 +365,18 @@ public class ConfigcenterContext {
Map<String, Object> result = null; Map<String, Object> result = null;
String errorMsg = null; String errorMsg = null;
for (String apiBaseUrl : apiBaseUrls) { for (String apiBaseUrl : apiBaseUrls) {
String url = buildTokenParameter(String.format("%s/api/fetchAllConfigs?appName=%s&env=%s&version=%s&ignoreGlobal=%s", String url = apiBaseUrl + "/api/fetchAllConfigs";
apiBaseUrl, app, env, version, ignoreGlobal)); Map<String, Object> params = new HashMap<>();
params.put("appName", app);
params.put("env", env);
params.put("version", version);
params.put("ignoreGlobal", ignoreGlobal);
if (token != null && !token.isEmpty()) {
params.put("token", token);
}
logger.info("fetch configs url:{}", url); logger.info("fetch configs url:{}", url);
try { try {
String jsonString = httpGet(url); String jsonString = httpPostJson(url, JSON.toJSONString(params));
if (jsonString != null) { if (jsonString != null) {
result = JSON.parseObject(jsonString, Map.class); result = JSON.parseObject(jsonString, Map.class);
if (result.containsKey("code")) { if (result.containsKey("code")) {
@@ -256,12 +256,19 @@ public class ConfigcenterContext {
String errorMsg = null; String errorMsg = null;
for (String apiBaseUrl : apiBaseUrls) { for (String apiBaseUrl : apiBaseUrls) {
String url = buildTokenParameter(String.format("%s/api/fetchAllConfigs?appName=%s&env=%s&version=%s&ignoreGlobal=%s", String url = apiBaseUrl + "/api/fetchAllConfigs";
apiBaseUrl, app, env, version, ignoreGlobal)); Map<String, Object> params = new HashMap<>();
params.put("appName", app);
params.put("env", env);
params.put("version", version);
params.put("ignoreGlobal", ignoreGlobal);
if (token != null && !token.isEmpty()) {
params.put("token", token);
}
System.out.println("fetch configs url:" + url); System.out.println("fetch configs url:" + url);
try { try {
String jsonString = HttpUtils.get(url); String jsonString = HttpUtils.postJson(url, objectMapper.writeValueAsString(params));
if (jsonString != null) { if (jsonString != null) {
result = objectMapper.readValue(jsonString, Map.class); result = objectMapper.readValue(jsonString, Map.class);
if (result.containsKey("code")) { if (result.containsKey("code")) {
+13 -2
View File
@@ -62,9 +62,20 @@ export class ConfigClient {
} }
async fetchAll() { async fetchAll() {
const url = `${this.config.baseUrl}/api/fetchAllConfigs?appName=${encodeURIComponent(this.config.appName)}&env=${encodeURIComponent(this.config.env)}&version=${encodeURIComponent(this.config.version)}&ignoreGlobal=${this.config.ignoreGlobal}&token=${encodeURIComponent(this.config.token)}`; const url = `${this.config.baseUrl}/api/fetchAllConfigs`;
const payload = {
appName: this.config.appName,
env: this.config.env,
version: this.config.version,
ignoreGlobal: this.config.ignoreGlobal,
token: this.config.token,
};
const resp = await fetch(url, { const resp = await fetch(url, {
headers: this.config.token ? { Authorization: `Bearer ${this.config.token}` } : {}, method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
signal: AbortSignal.timeout(10000), signal: AbortSignal.timeout(10000),
}); });
if (!resp.ok) { if (!resp.ok) {
+9 -9
View File
@@ -60,15 +60,15 @@ class ConfigClient:
self._stop_event.set() self._stop_event.set()
def _fetch_all(self) -> None: def _fetch_all(self) -> None:
url = ( url = f"{self._config.base_url}/api/fetchAllConfigs"
f"{self._config.base_url}/api/fetchAllConfigs" payload = {
f"?appName={self._config.app_name}" "appName": self._config.app_name,
f"&env={self._config.env}" "env": self._config.env,
f"&version={self._config.version}" "version": self._config.version,
f"&ignoreGlobal={str(self._config.ignore_global).lower()}" "ignoreGlobal": self._config.ignore_global,
f"&token={self._config.token}" "token": self._config.token,
) }
resp = requests.get(url, timeout=10) resp = requests.post(url, json=payload, timeout=10)
resp.raise_for_status() resp.raise_for_status()
data = resp.json() data = resp.json()
+2 -2
View File
@@ -112,10 +112,10 @@ func main() {
user.POST("/logout", authHandler.Logout) user.POST("/logout", authHandler.Logout)
} }
// client API routes (no JWT, uses authtoken or open access) // client API routes (no JWT, uses token or open access)
api := r.Group("/api") api := r.Group("/api")
{ {
api.GET("/fetchAllConfigs", configApiHandler.FetchAllConfigs) api.POST("/fetchAllConfigs", configApiHandler.FetchAllConfigs)
api.POST("/fetchChangedConfigs", configApiHandler.FetchChangedConfigs) api.POST("/fetchChangedConfigs", configApiHandler.FetchChangedConfigs)
api.GET("/ping", configApiHandler.Ping) api.GET("/ping", configApiHandler.Ping)
} }
+28 -15
View File
@@ -21,28 +21,41 @@ func NewConfigApiHandler(crypto *service.CryptoService, parse *service.ConfigPar
return &ConfigApiHandler{cryptoService: crypto, parseService: parse} return &ConfigApiHandler{cryptoService: crypto, parseService: parse}
} }
func (h *ConfigApiHandler) FetchAllConfigs(c *gin.Context) { type FetchAllRequest struct {
appName := c.Query("appName") AppName string `json:"appName" binding:"required"`
env := c.Query("env") Env string `json:"env" binding:"required"`
version := c.DefaultQuery("version", "latest") Version string `json:"version"`
ignoreGlobal := c.DefaultQuery("ignoreGlobal", "false") == "true" IgnoreGlobal bool `json:"ignoreGlobal"`
token := c.Query("token") Token string `json:"token"`
}
if appName == "" || env == "" { func (h *ConfigApiHandler) FetchAllConfigs(c *gin.Context) {
var req FetchAllRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数错误"})
return
}
version := req.Version
if version == "" {
version = "latest"
}
if req.AppName == "" || req.Env == "" {
c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数缺失"}) c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数缺失"})
return return
} }
// find app by code // find app by code
var app model.Application var app model.Application
has, _ := dao.Engine.Where("code = ? AND enabled = 1", appName).Get(&app) has, _ := dao.Engine.Where("code = ? AND enabled = 1", req.AppName).Get(&app)
if !has { if !has {
c.JSON(http.StatusOK, model.WrapperResponse{Code: 1001, Msg: "配置不存在"}) c.JSON(http.StatusOK, model.WrapperResponse{Code: 1001, Msg: "配置不存在"})
return return
} }
// validate token if app has token configured // validate token if app has token configured
if app.Token != "" && app.Token != token { if app.Token != "" && app.Token != req.Token {
c.JSON(http.StatusForbidden, model.WrapperResponse{Code: 403, Msg: "Token 无效"}) c.JSON(http.StatusForbidden, model.WrapperResponse{Code: 403, Msg: "Token 无效"})
return return
} }
@@ -50,10 +63,10 @@ func (h *ConfigApiHandler) FetchAllConfigs(c *gin.Context) {
result := make(map[string]interface{}) result := make(map[string]interface{})
// load global configs // load global configs
if !ignoreGlobal && app.GroupId != nil { if !req.IgnoreGlobal && app.GroupId != nil {
var globalConfigs []model.AppConfig var globalConfigs []model.AppConfig
dao.Engine.Where("env = ? AND version = ? AND enabled = 1 AND group_id = ? AND (app_id is null OR app_id = 0)", dao.Engine.Where("env = ? AND version = ? AND enabled = 1 AND group_id = ? AND (app_id is null OR app_id = 0)",
env, version, *app.GroupId).Find(&globalConfigs) req.Env, version, *app.GroupId).Find(&globalConfigs)
for _, cfg := range globalConfigs { for _, cfg := range globalConfigs {
kvMap := h.parseService.ParseConfigToKVMap(&cfg) kvMap := h.parseService.ParseConfigToKVMap(&cfg)
for k, v := range kvMap { for k, v := range kvMap {
@@ -66,7 +79,7 @@ func (h *ConfigApiHandler) FetchAllConfigs(c *gin.Context) {
if app.ParentId != nil { if app.ParentId != nil {
var parentConfigs []model.AppConfig var parentConfigs []model.AppConfig
dao.Engine.Where("env = ? AND version = ? AND enabled = 1 AND app_id = ?", dao.Engine.Where("env = ? AND version = ? AND enabled = 1 AND app_id = ?",
env, version, *app.ParentId).Find(&parentConfigs) req.Env, version, *app.ParentId).Find(&parentConfigs)
for _, cfg := range parentConfigs { for _, cfg := range parentConfigs {
kvMap := h.parseService.ParseConfigToKVMap(&cfg) kvMap := h.parseService.ParseConfigToKVMap(&cfg)
for k, v := range kvMap { for k, v := range kvMap {
@@ -78,7 +91,7 @@ func (h *ConfigApiHandler) FetchAllConfigs(c *gin.Context) {
// load app configs // load app configs
var appConfigs []model.AppConfig var appConfigs []model.AppConfig
dao.Engine.Where("env = ? AND version = ? AND enabled = 1 AND app_id = ?", dao.Engine.Where("env = ? AND version = ? AND enabled = 1 AND app_id = ?",
env, version, app.Id).Find(&appConfigs) req.Env, version, app.Id).Find(&appConfigs)
for _, cfg := range appConfigs { for _, cfg := range appConfigs {
kvMap := h.parseService.ParseConfigToKVMap(&cfg) kvMap := h.parseService.ParseConfigToKVMap(&cfg)
for k, v := range kvMap { for k, v := range kvMap {
@@ -92,9 +105,9 @@ func (h *ConfigApiHandler) FetchAllConfigs(c *gin.Context) {
} }
// add encryption keys // add encryption keys
globalCryptKey := h.cryptoService.GetCryptKey(0, env) globalCryptKey := h.cryptoService.GetCryptKey(0, req.Env)
result["kenaito.config.global-encrypt-secret"] = globalCryptKey result["kenaito.config.global-encrypt-secret"] = globalCryptKey
appCryptKey := h.cryptoService.GetCryptKey(app.Id, env) appCryptKey := h.cryptoService.GetCryptKey(app.Id, req.Env)
result["kenaito.config.encrypt-secret"] = appCryptKey result["kenaito.config.encrypt-secret"] = appCryptKey
result["latest.fetch.timestamp"] = strconv.FormatInt(time.Now().UnixMilli(), 10) result["latest.fetch.timestamp"] = strconv.FormatInt(time.Now().UnixMilli(), 10)