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
当从API调用API时,它可以通过 http://localhost:8282/uri?value 正常工作。
更多关于Golang实现localhost内部API间通信的方法探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在启用nginx的情况下进行localhost内部API调用,需要确保内部请求正确路由到后端Go服务器。以下是解决方案和示例代码:
问题分析
当nginx作为反向代理时,内部API调用应该使用:
- 内部网络地址(localhost:8282)而不是外部域名
- 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)
// ...
}
关键点
- 协议:内部调用使用HTTP(nginx已处理HTTPS)
- 端口:直接使用Go服务器的8282端口
- 地址:使用localhost或127.0.0.1
- 头部传递:确保nginx正确传递请求头
这样修改后,内部API调用将通过HTTP直接访问Go服务器,绕过nginx的SSL和外部路由逻辑,确保内部通信正常。


