Golang实现localhost内部API间通信的方法探讨

Golang实现localhost内部API间通信的方法探讨 我有一个由可信中心签发证书的nginx服务器。以下是nginx配置:

root /var/www/html;
        # 如果使用PHP,请添加index.php到列表中
        index index.html index.htm index.nginx-debian.html;

        server_name site.ru;
        ssl_certificate $HOME/.site.crt;
        ssl_certificate_key $HOME/.site.key;
        if ($scheme != "https") {
                return 301 https://localhost:8282$request_uri;
        }
        location / {
                # 首先尝试将请求作为文件处理,
                # 然后作为目录处理,最后回退到显示404。
                proxy_pass http://localhost:8282/;
        }

在这个nginx后面,端口8282上运行着一个带有某些API的Golang服务器。如果我尝试从其他网络(非本地主机,例如Android网络浏览器+SIM卡网络)访问API https://domain.name/myapi/?param1=value,如果这个API没有内部调用其他API,我可以从这个API获得正确的响应。但是,如果我尝试调用一个在本地主机内部调用其他API的API,我会得到不正确的结果,因为如果API位于同一个网络(本地主机)内,它们之间无法通信。以下是一个位于本地主机内并获得不正确结果的API示例:

func Create_user(w http.ResponseWriter, r *http.Request) {
    encode_data := Get_encoded_form_values(w, r)
    decode_data := JSON_struct{}
    tmplsrv.Decoding(encode_data, &decode_data)
    params := url.Values{
        "email":    {r.FormValue("email")},
        "password": {r.FormValue("password")},
    }
    //调用其他API来检查数据库中用户是否存在
    check_user_answer := Requester("https://localhost/check_user/", w, r, encode_data, params)
    answer := Answer{}
    tmplsrv.Decoding(check_user_answer, &answer)
    if answer.Answr == "NotFound" {
        //调用其他API生成新令牌。
        new_token_answer := Requester("https://localhost/create_token/", w, r, encode_data, params)
        tmplsrv.Decoding(new_token_answer, &answer)
        decode_data.JWT_token = answer.Answr
        database.Inserting_user_in_db(tmplsrv.Encoding(decode_data))
        w.WriteHeader(http.StatusCreated)
        w.Write([]byte("created"))
    }
    if answer.Answr == "" {
        // 我总是进入这个条件,因为内部API调用不成功
        w.Write([]byte("Unable to create user"))
    } else {
        w.Write([]byte("User exists"))
    }
}

但是,如果我禁用nginx,我可以通过 http://ip:port/uri?values 进行内部API调用。

我如何在启用nginx的情况下在本地主机内调用API?谢谢。


更多关于Golang实现localhost内部API间通信的方法探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

当从API调用API时,它可以通过 http://localhost:8282/uri?value 正常工作。

更多关于Golang实现localhost内部API间通信的方法探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你能发布一些Go服务器(监听部分)的代码吗?另外,你为什么使用nginx,因为你可以直接从Go服务器提供页面,它也可以使用证书。

在启用nginx的情况下进行localhost内部API调用,需要确保内部请求正确路由到后端Go服务器。以下是解决方案和示例代码:

问题分析

当nginx作为反向代理时,内部API调用应该使用:

  1. 内部网络地址(localhost:8282)而不是外部域名
  2. HTTP协议而不是HTTPS(因为nginx已经处理了SSL终止)

解决方案

1. 修改内部API调用使用HTTP和正确端口

func Create_user(w http.ResponseWriter, r *http.Request) {
    encode_data := Get_encoded_form_values(w, r)
    decode_data := JSON_struct{}
    tmplsrv.Decoding(encode_data, &decode_data)
    params := url.Values{
        "email":    {r.FormValue("email")},
        "password": {r.FormValue("password")},
    }
    
    // 关键修改:使用HTTP协议和8282端口
    check_user_answer := Requester("http://localhost:8282/check_user/", w, r, encode_data, params)
    answer := Answer{}
    tmplsrv.Decoding(check_user_answer, &answer)
    
    if answer.Answr == "NotFound" {
        // 同样修改这里
        new_token_answer := Requester("http://localhost:8282/create_token/", w, r, encode_data, params)
        tmplsrv.Decoding(new_token_answer, &answer)
        decode_data.JWT_token = answer.Answr
        database.Inserting_user_in_db(tmplsrv.Encoding(decode_data))
        w.WriteHeader(http.StatusCreated)
        w.Write([]byte("created"))
    }
    
    if answer.Answr == "" {
        w.Write([]byte("Unable to create user"))
    } else {
        w.Write([]byte("User exists"))
    }
}

2. 创建通用的内部请求函数

func InternalRequester(endpoint string, w http.ResponseWriter, r *http.Request, encode_data []byte, params url.Values) []byte {
    // 构建内部API URL
    internalURL := fmt.Sprintf("http://localhost:8282%s", endpoint)
    
    // 创建新的HTTP请求
    req, err := http.NewRequest("POST", internalURL, strings.NewReader(params.Encode()))
    if err != nil {
        return []byte(`{"Answr":""}`)
    }
    
    // 复制原始请求的头部信息
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    req.Header.Set("X-Forwarded-For", r.RemoteAddr)
    
    // 发送请求
    client := &http.Client{
        Timeout: time.Second * 10,
    }
    
    resp, err := client.Do(req)
    if err != nil {
        return []byte(`{"Answr":""}`)
    }
    defer resp.Body.Close()
    
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        return []byte(`{"Answr":""}`)
    }
    
    return body
}

// 使用示例
func Create_user(w http.ResponseWriter, r *http.Request) {
    encode_data := Get_encoded_form_values(w, r)
    decode_data := JSON_struct{}
    tmplsrv.Decoding(encode_data, &decode_data)
    params := url.Values{
        "email":    {r.FormValue("email")},
        "password": {r.FormValue("password")},
    }
    
    // 使用内部请求函数
    check_user_answer := InternalRequester("/check_user/", w, r, encode_data, params)
    // ... 其余代码
}

3. 配置nginx传递原始主机头(可选)

修改nginx配置以确保正确的头部传递:

location / {
    proxy_pass http://localhost:8282/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

4. 使用环境变量配置API端点

var (
    internalAPIHost = os.Getenv("INTERNAL_API_HOST")
    internalAPIPort = os.Getenv("INTERNAL_API_PORT")
)

func getInternalAPIURL(endpoint string) string {
    if internalAPIHost == "" {
        internalAPIHost = "localhost"
    }
    if internalAPIPort == "" {
        internalAPIPort = "8282"
    }
    return fmt.Sprintf("http://%s:%s%s", internalAPIHost, internalAPIPort, endpoint)
}

// 使用示例
func Create_user(w http.ResponseWriter, r *http.Request) {
    // ...
    check_user_url := getInternalAPIURL("/check_user/")
    check_user_answer := Requester(check_user_url, w, r, encode_data, params)
    // ...
}

关键点

  1. 协议:内部调用使用HTTP(nginx已处理HTTPS)
  2. 端口:直接使用Go服务器的8282端口
  3. 地址:使用localhost或127.0.0.1
  4. 头部传递:确保nginx正确传递请求头

这样修改后,内部API调用将通过HTTP直接访问Go服务器,绕过nginx的SSL和外部路由逻辑,确保内部通信正常。

回到顶部