如何使用Go编辑WireMock动态JSON映射数据?
动态WireMock映射JSON的URL修改实现
问题场景
我需要实现一个服务,接收结构不固定的WireMock映射数据,修改其中所有request.url的值(比如在原URL前添加/geronimo前缀)后返回。WireMock的映射结构主要有两种形式:
单映射结构
{ "request": { "method": "GET", "url": "/some/thing" }, "response": { "status": 200, "body": "Hello world!", "headers": { "Content-Type": "text/plain" } } }
多映射数组结构
{ "mappings": [ { "request": { "method": "GET", "url": "/one" }, "response": { "status": 200 } }, { "id": "8c5db8b0-2db4-4ad7-a99f-38c9b00da3f7", "request": { "url": "/two" }, "response": { "status": 200, "body": "Body content" } } ], "importOptions": { "duplicatePolicy": "IGNORE", "deleteAllNotInImport": true } }
我已经用json.Unmarshal将JSON解析到map[string]interface{}中,但不清楚如何替换url的取值,现有代码如下:
package main import ( "encoding/json" "errors" "fmt" "io" "log" "net/http" ) var anytype map[string]interface{} func parseMappings() { } func parseReq(req map[string]interface{}) (string, error) { fmt.Printf("%T %v\n", req, req) for k, v := range req { if k == "url" { if v, ok := v.(string); ok { return ("/geronimo" + v), nil } return "", errors.New("Not a string") } } return "", nil } func handler(w http.ResponseWriter, req *http.Request) { payload, err := io.ReadAll(req.Body) if err != nil { log.Fatal(err) } err = json.Unmarshal(payload, &anytype) if err != nil { log.Fatal(err) } fmt.Printf("%T %v\n", anytype, anytype) for k, v := range anytype { if k == "request" { if v, ok := v.(map[string]interface{}); ok { s, err := parseReq(v) if err != nil { log.Fatal(err) } // what to do here? how replace url with new value? // url = s fmt.Println("Done", s) } } else { fmt.Printf("%v %T\n", k, v) } } } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }
解决方案
针对动态JSON结构的修改,核心思路是直接操作map[string]interface{}和切片,遍历所有可能存在request.url的位置并修改:
1. 修改URL处理函数:直接操作原映射
不需要返回新URL字符串,直接在传入的request映射上修改url字段:
func modifyReqURL(req map[string]interface{}) error { urlVal, ok := req["url"].(string) if !ok { return errors.New("url field is not a string") } req["url"] = "/geronimo" + urlVal return nil }
2. 兼容两种映射结构的处理逻辑
在请求处理函数中,同时处理顶层request字段和mappings数组中的每个request:
func handler(w http.ResponseWriter, req *http.Request) { payload, err := io.ReadAll(req.Body) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } var data map[string]interface{} err = json.Unmarshal(payload, &data) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // 处理顶层的request if reqMap, ok := data["request"].(map[string]interface{}); ok { if err := modifyReqURL(reqMap); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } } // 处理mappings数组中的每个request if mappings, ok := data["mappings"].([]interface{}); ok { for _, item := range mappings { if mappingMap, ok := item.(map[string]interface{}); ok { if reqMap, ok := mappingMap["request"].(map[string]interface{}); ok { if err := modifyReqURL(reqMap); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } } } } } // 将修改后的数据序列化为JSON并返回 modifiedJSON, err := json.MarshalIndent(data, "", " ") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(modifiedJSON) }
3. 完整修改后的代码
package main import ( "encoding/json" "errors" "io" "net/http" ) func modifyReqURL(req map[string]interface{}) error { urlVal, ok := req["url"].(string) if !ok { return errors.New("url field is not a string") } req["url"] = "/geronimo" + urlVal return nil } func handler(w http.ResponseWriter, req *http.Request) { payload, err := io.ReadAll(req.Body) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } var data map[string]interface{} err = json.Unmarshal(payload, &data) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // 处理顶层request if reqMap, ok := data["request"].(map[string]interface{}); ok { if err := modifyReqURL(reqMap); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } } // 处理mappings数组中的request if mappings, ok := data["mappings"].([]interface{}); ok { for _, item := range mappings { if mapping, ok := item.(map[string]interface{}); ok { if reqMap, ok := mapping["request"].(map[string]interface{}); ok { if err := modifyReqURL(reqMap); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } } } } } // 返回修改后的JSON modifiedData, err := json.MarshalIndent(data, "", " ") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") w.Write(modifiedData) } func main() { http.HandleFunc("/", handler) http.ListenAndServe(":8080", nil) }
关键说明
- 移除全局变量
anytype,改用局部变量存储解析后的JSON数据,保证并发安全 - 每次访问嵌套结构时必须做类型断言检查,避免运行时panic
- 使用
json.MarshalIndent生成格式化后的JSON,提升返回结果的可读性
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

