Golang的HTTP客户端始终执行GET请求,而不是POST请求。

huangapple go评论66阅读模式
英文:

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 = `{&quot;ResetType&quot;:&quot;` + powerstate + `&quot;}`

	//Create the request with auth header
	req, reqErr := iDrac.buildHttpRequestWithAuthorizationHeader(http.MethodPost, &quot;https://&quot;+iDrac.hostAddress+apiBaseURL+apiChassisURL+apiChassisResetAction, jsonStr)
	if reqErr != nil {
		return fmt.Errorf(&quot;COULD_NOT_CREATE_REQUEST: &quot; + reqErr.Error())
	}
	req.Header.Set(&quot;Content-Type&quot;, &quot;application/json; charset=UTF-8&quot;)

	//Make the request
	resp, doErr := http.DefaultClient.Do(req)
	if doErr != nil {
		return fmt.Errorf(&quot;COULD_NOT_SEND_POST_REQUEST_TO_IDRAC_API&quot; + doErr.Error())
	}

	//Check if the request was successful
	if resp.StatusCode != 200 {
		return fmt.Errorf(&quot;COULD_NOT_CHANGE_SERVER_POWER_STATUS_OVER_IDRAC HTTP:&quot; + 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(&quot;Authorization&quot;, &quot;Basic &quot;+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(&quot;Handling reset perform request from &quot; + r.RemoteAddr)

	if r.Method != http.MethodPost {
		garbagelog.Log(&quot;Invalid http method! Got &quot; + r.Method + &quot; expected POST&quot;)
		w.WriteHeader(405)
		return
	}
	if !checkAuthorization(r.BasicAuth()) {
		w.WriteHeader(401)
		return
	}

	var resetType nidrac.ResetType
	err := json.NewDecoder(r.Body).Decode(&amp;resetType)
	if err != nil {
		garbagelog.Log(&quot;Could not decode reset type: &quot; + err.Error())
		w.WriteHeader(422)
		return
	}

	iDracMock.PerformResetAction(resetType.ResetType)
	garbagelog.Log(&quot;&gt;&gt;&gt;&gt;SEVERSTATE: &quot; + iDracMock.PowerState().PowerState + &quot;&lt;&lt;&lt;&lt;&quot;)

	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, &quot;https://&quot;+iDrac.hostAddress+apiBaseURL+apiChassisURL+apiChassisResetAction, jsonStr)
	if reqErr != nil {
		return fmt.Errorf(&quot;COULD_NOT_CREATE_REQUEST: &quot; + reqErr.Error())
	}

How i build the routes in the iDrac mockup:

http.Handle(&quot;/&quot;, http.HandlerFunc(handleDefault))
	http.Handle(&quot;/reset&quot;, http.HandlerFunc(handleReset))
	http.Handle(&quot;/redfish/v1/Systems/System.Embedded.1&quot;, http.HandlerFunc(handlePowerStateGet))
	http.Handle(&quot;/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset&quot;, http.HandlerFunc(handlePerformReset))

	garbagelog.Log(&quot;Starting webserver&quot;)
	err := http.ListenAndServeTLS(&quot;:443&quot;, currentConfig.CertFile, currentConfig.PrivKeyFile, nil)
	if err != nil {
		garbagelog.Log(&quot;Could not serve TLS: &quot; + 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            = &quot;/redfish/v1/&quot;
	apiChassisURL         = &quot;/Systems/System.Embedded.1&quot;
	apiChassisResetAction = &quot;/Actions/ComputerSystem.Reset&quot;
)

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            = &quot;/redfish/v1&quot;
	apiChassisURL         = &quot;/Systems/System.Embedded.1&quot;
	apiChassisResetAction = &quot;/Actions/ComputerSystem.Reset&quot;
)

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"。

我认为代码没有问题,只是方法不对 Golang的HTTP客户端始终执行GET请求,而不是POST请求。

英文:

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 Golang的HTTP客户端始终执行GET请求,而不是POST请求。

huangapple
  • 本文由 发表于 2022年7月5日 18:00:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/72867482.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定