英文:
Golang http client always performs get requests and does not post
问题
我目前遇到一个非常令我沮丧的问题,我绝对找不到我的代码中的任何问题。
我想要实现的目标是向我编写的 iDrac 模拟器(两个软件都是用 Golang 编写)发送一个 HTTP POST 消息,以控制模拟器的电源状态,但无论我为请求配置什么,模拟器总是接收到一个空的请求体的 GET 请求。
这是我用来创建和发送请求的函数:
func (iDrac *IDrac) SetPowerstate(powerstate string) error {
// 创建重置类型的 JSON 字符串
var jsonStr = `{"ResetType":"` + powerstate + `"}`
// 使用授权头创建请求
req, reqErr := iDrac.buildHttpRequestWithAuthorizationHeader(http.MethodPost, "https://"+iDrac.hostAddress+apiBaseURL+apiChassisURL+apiChassisResetAction, jsonStr)
if reqErr != nil {
return fmt.Errorf("COULD_NOT_CREATE_REQUEST: " + reqErr.Error())
}
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
// 发送请求
resp, doErr := http.DefaultClient.Do(req)
if doErr != nil {
return fmt.Errorf("COULD_NOT_SEND_POST_REQUEST_TO_IDRAC_API" + doErr.Error())
}
// 检查请求是否成功
if resp.StatusCode != 200 {
return fmt.Errorf("COULD_NOT_CHANGE_SERVER_POWER_STATUS_OVER_IDRAC HTTP:" + resp.Status)
}
return nil
}
我用于构建请求的辅助函数:
func (iDrac *IDrac) buildHttpRequestWithAuthorizationHeader(method string, url string, content string) (*http.Request, error) {
req, err := http.NewRequest(method, url, bytes.NewBuffer([]byte(content)))
if err != nil {
return nil, err
}
req.Header.Add("Authorization", "Basic "+iDrac.hashedCredentials)
return req, nil
}
最后是模拟器处理请求的函数:
func handlePerformReset(w http.ResponseWriter, r *http.Request) {
garbagelog.Log("Handling reset perform request from " + r.RemoteAddr)
if r.Method != http.MethodPost {
garbagelog.Log("Invalid http method! Got " + r.Method + " expected POST")
w.WriteHeader(405)
return
}
if !checkAuthorization(r.BasicAuth()) {
w.WriteHeader(401)
return
}
var resetType nidrac.ResetType
err := json.NewDecoder(r.Body).Decode(&resetType)
if err != nil {
garbagelog.Log("Could not decode reset type: " + err.Error())
w.WriteHeader(422)
return
}
iDracMock.PerformResetAction(resetType.ResetType)
garbagelog.Log(">>>>SEVERSTATE: " + iDracMock.PowerState().PowerState + "<<<<")
w.WriteHeader(200)
}
模拟器尝试将请求体转换为的类型:
type ResetType struct {
ResetType string
}
当我使用 Postman 访问端点时,它完美地工作:
iDrac 模拟器日志确认成功的请求
Postman 请求配置:
Postman 请求配置
但是,当我尝试使用 Go 代码发送请求时,它却无法工作:
iDrac 模拟器日志显示 HTTP 方法无效(因为它是 GET 而不是 POST)
我花了两个小时尝试找到解决方案,但我没有找到有人遇到与我相同问题的帖子。
编辑:更正了旧代码。即使修复了愚蠢的错误,问题仍然存在:
// 使用授权头创建请求
req, reqErr := iDrac.buildHttpRequestWithAuthorizationHeader(http.MethodPost, "https://"+iDrac.hostAddress+apiBaseURL+apiChassisURL+apiChassisResetAction, jsonStr)
if reqErr != nil {
return fmt.Errorf("COULD_NOT_CREATE_REQUEST: " + reqErr.Error())
}
我如何在 iDrac 模拟器中构建路由:
http.Handle("/", http.HandlerFunc(handleDefault))
http.Handle("/reset", http.HandlerFunc(handleReset))
http.Handle("/redfish/v1/Systems/System.Embedded.1", http.HandlerFunc(handlePowerStateGet))
http.Handle("/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset", http.HandlerFunc(handlePerformReset))
garbagelog.Log("Starting webserver")
err := http.ListenAndServeTLS(":443", currentConfig.CertFile, currentConfig.PrivKeyFile, nil)
if err != nil {
garbagelog.Log("Could not serve TLS: " + err.Error())
}
在我 iDrac 通信模块中创建的请求和 iDrac 模拟器接收到的请求中,都确认了请求的路径是:
> r.URL.Path = /redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset
英文:
I'm currently experiencing a problem that really frustrates me and where i absolutely can't see anny issues with my code.
What i'm trying to achieve is to send a http POST message to a mockup of an iDrac i wrote (both softwares written in golang) to control the mockup's powerstate, but no matter what i configure for the request, the mockup always receives get requests with an empty body.
The function i create and send the request with:
func (iDrac *IDrac) SetPowerstate(powerstate string) error {
//Create reset type json string
var jsonStr = `{"ResetType":"` + powerstate + `"}`
//Create the request with auth header
req, reqErr := iDrac.buildHttpRequestWithAuthorizationHeader(http.MethodPost, "https://"+iDrac.hostAddress+apiBaseURL+apiChassisURL+apiChassisResetAction, jsonStr)
if reqErr != nil {
return fmt.Errorf("COULD_NOT_CREATE_REQUEST: " + reqErr.Error())
}
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
//Make the request
resp, doErr := http.DefaultClient.Do(req)
if doErr != nil {
return fmt.Errorf("COULD_NOT_SEND_POST_REQUEST_TO_IDRAC_API" + doErr.Error())
}
//Check if the request was successful
if resp.StatusCode != 200 {
return fmt.Errorf("COULD_NOT_CHANGE_SERVER_POWER_STATUS_OVER_IDRAC HTTP:" + resp.Status)
}
return nil
}
The helper function i use to build the request with:
func (iDrac *IDrac) buildHttpRequestWithAuthorizationHeader(method string, url string, content string) (*http.Request, error) {
req, err := http.NewRequest(method, url, bytes.NewBuffer([]byte(content)))
if err != nil {
return nil, err
}
req.Header.Add("Authorization", "Basic "+iDrac.hashedCredentials)
return req, nil
}
And finally the function where the mockup proccesses the request:
func handlePerformReset(w http.ResponseWriter, r *http.Request) {
garbagelog.Log("Handling reset perform request from " + r.RemoteAddr)
if r.Method != http.MethodPost {
garbagelog.Log("Invalid http method! Got " + r.Method + " expected POST")
w.WriteHeader(405)
return
}
if !checkAuthorization(r.BasicAuth()) {
w.WriteHeader(401)
return
}
var resetType nidrac.ResetType
err := json.NewDecoder(r.Body).Decode(&resetType)
if err != nil {
garbagelog.Log("Could not decode reset type: " + err.Error())
w.WriteHeader(422)
return
}
iDracMock.PerformResetAction(resetType.ResetType)
garbagelog.Log(">>>>SEVERSTATE: " + iDracMock.PowerState().PowerState + "<<<<")
w.WriteHeader(200)
}
The type the iDrac mock tries to convert the body to:
type ResetType struct {
ResetType string
}
It works flawlessly when i try to reach the endpoint with postman:
iDrac mockup log confirming the successful request
Postnam request configuration:
Postman request configuration
But it somehow does not work when i try making the request with go code:
iDrac mockup log saying that the http method is invalid (because it's get instead of post)
I spent two hours trying to find a solution but i somehow did not find a post with someone having the same problem that i have.
Edit: Corrected old code. The problem remains even with the silly mistake fixed:
//Create the request with auth header
req, reqErr := iDrac.buildHttpRequestWithAuthorizationHeader(http.MethodPost, "https://"+iDrac.hostAddress+apiBaseURL+apiChassisURL+apiChassisResetAction, jsonStr)
if reqErr != nil {
return fmt.Errorf("COULD_NOT_CREATE_REQUEST: " + reqErr.Error())
}
How i build the routes in the iDrac mockup:
http.Handle("/", http.HandlerFunc(handleDefault))
http.Handle("/reset", http.HandlerFunc(handleReset))
http.Handle("/redfish/v1/Systems/System.Embedded.1", http.HandlerFunc(handlePowerStateGet))
http.Handle("/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset", http.HandlerFunc(handlePerformReset))
garbagelog.Log("Starting webserver")
err := http.ListenAndServeTLS(":443", currentConfig.CertFile, currentConfig.PrivKeyFile, nil)
if err != nil {
garbagelog.Log("Could not serve TLS: " + err.Error())
}
In both requests, the one created in my iDrac comms module and the one received by the iDrac mockup, did confirm that the requested path is:
> r.URL.Path = /redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset
答案1
得分: 2
我找到了问题:
我用来构建URL的常量定义如下:
const (
apiBaseURL = "/redfish/v1/"
apiChassisURL = "/Systems/System.Embedded.1"
apiChassisResetAction = "/Actions/ComputerSystem.Reset"
)
导致URL看起来像这样:
https://host/redfish/v1//Systems/System.Embedded.1/Actions/ComputerSystem.Reset
(注意v1和Systems之间有两个//)
所以我已经修复了它:
const (
apiBaseURL = "/redfish/v1"
apiChassisURL = "/Systems/System.Embedded.1"
apiChassisResetAction = "/Actions/ComputerSystem.Reset"
)
现在一切都正常工作了:
测试结果显示每个测试都成功
我感谢每个人的帮助,让我没有完全失去理智。
英文:
I found the problem:
The constants i built the urls with were defined like this:
const (
apiBaseURL = "/redfish/v1/"
apiChassisURL = "/Systems/System.Embedded.1"
apiChassisResetAction = "/Actions/ComputerSystem.Reset"
)
Leading to a url that looks like this:
> https://host/redfish/v1//Systems/System.Embedded.1/Actions/ComputerSystem.Reset
(Notice the double // between v1 and Systems)
So i've fixed it:
const (
apiBaseURL = "/redfish/v1"
apiChassisURL = "/Systems/System.Embedded.1"
apiChassisResetAction = "/Actions/ComputerSystem.Reset"
)
And everything works correctly:
Test results showing that every test was successful
I thank everyone for their input for helping me not lose my mind completely.
答案2
得分: 1
根据你的截图,我可以看到你在Postman中使用了一个"POST"请求,但是在你的代码中是"GET"。
我认为代码没有问题,只是方法不对
英文:
based on your screenshot, I can see you are using a "POST" request in your postman, but in your code is "GET".
I think nothing wrong with the code but the method only
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论