尝试使用urlfetch.Transport读取*http.Response Body时出现运行时错误。

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

Runtime error when attempting to read *http.Response Body, having used urlfetch.Transport

问题

App Engine不允许使用DefaultClient,而是提供了urlfetch服务。以下是一个最小化的示例,部署和工作效果基本符合预期:

package app

import (
    "fmt"
    "net/http"
    "appengine"
    "appengine/urlfetch"
    "code.google.com/p/goauth2/oauth"
)

func init () {
    http.HandleFunc("/", home)
}

func home(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    config := &oauth.Config{
        ClientId:     "<redacted>",
        ClientSecret: "<redacted>",
        Scope:        "email",
        AuthURL:      "https://www.facebook.com/dialog/oauth",
        TokenURL:     "https://graph.facebook.com/oauth/access_token",
        RedirectURL:  "http://example.com/",
    }

    code := r.FormValue("code")
    if code == "" {
        http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
    }
    t := &oauth.Transport{Config: config, Transport: &urlfetch.Transport{Context: c}}
    tok, _ := t.Exchange(code)
    graphResponse, _ := t.Client().Get("https://graph.facebook.com/me")

    fmt.Fprintf(w, "<pre>%s<br />%s</pre>", tok, graphResponse)
}

使用正确的ClientId、ClientSecret和RedirectURL,这将产生以下输出(为了简洁起见进行了编辑):

&{AAADTWGsQ5&lt;snip&gt;kMdjh5VKwZDZD  0001-01-01 00:00:00 +0000 UTC}

&{200 OK %!s(int=200) HTTP/1.1 %!s(int=1) %!s(int=1) 
map[Connection:[keep-alive] Access-Control-Allow-Origin:[*] 
&lt;snip&gt;
Content-Type:[text/javascript; charset=UTF-8] 
Date:[Wed, 06 Feb 2013 12:06:45 GMT] X-Google-Cache-Control:[remote-fetch] 
Cache-Control:[private, no-cache, no-store, must-revalidate] Pragma:[no-cache] 
X-Fb-Rev:[729873] Via:[HTTP/1.1 GWA] Expires:[Sat, 01 Jan 2000 00:00:00 GMT]] 
%!s(*urlfetch.bodyReader=&amp;{[123 34 105 100 &lt;big snip&gt; 48 48 34 125] false false}) 
%!s(int64=306) [] %!s(bool=true) map[] %!s(*http.Request=&amp;{GET 0xf840087230 
HTTP/1.1 1 1 map[Authorization:[Bearer AAADTWGsQ5NsBAC4yT0x1shZAJAtODOIx0tZCb
TYTjxFC4esEqCjPDi3REMKHBUjZCX4FIKLO1UjMpJxhJZCfGFcOJlFu7UvehkMdjh5VKwZDZD]]
  0 [] false graph.facebook.com map[]  map[]   })}

看起来我一直得到了一个*http.Response,所以我期望能够从响应体中读取。然而,任何对Body的提及,例如:

defer graphResponse.Body.Close()

编译、部署,但会导致以下运行时错误:

panic: runtime error: invalid memory address or nil pointer dereference
runtime.panic go/src/pkg/runtime/proc.c:1442
runtime.panicstring go/src/pkg/runtime/runtime.c:128
runtime.sigpanic go/src/pkg/runtime/thread_linux.c:199
app.home app/app.go:33
net/http.HandlerFunc.ServeHTTP go/src/pkg/net/http/server.go:704
net/http.(*ServeMux).ServeHTTP go/src/pkg/net/http/server.go:942
appengine_internal.executeRequestSafely go/src/pkg/appengine_internal/api_prod.go:240
appengine_internal.(*server).HandleRequest go/src/pkg/appengine_internal/api_prod.go:190
reflect.Value.call go/src/pkg/reflect/value.go:526
reflect.Value.Call go/src/pkg/reflect/value.go:334
_ _.go:316
runtime.goexit go/src/pkg/runtime/proc.c:270

我错过了什么?这是因为使用了urlfetch而不是DefaultClient吗?

英文:

App Engine does not allow use of DefaultClient, providing the urlfetch service instead. The following minimal example deploys and works pretty much as expected:

package app

import (
	&quot;fmt&quot;
	&quot;net/http&quot;
	&quot;appengine&quot;
	&quot;appengine/urlfetch&quot;
	&quot;code.google.com/p/goauth2/oauth&quot;
)

func init () {
	http.HandleFunc(&quot;/&quot;, home)
}

func home(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	config := &amp;oauth.Config{
		ClientId:     &quot;&lt;redacted&gt;&quot;,
		ClientSecret: &quot;&lt;redacted&gt;&quot;,
		Scope:        &quot;email&quot;,
		AuthURL:      &quot;https://www.facebook.com/dialog/oauth&quot;,
		TokenURL:     &quot;https://graph.facebook.com/oauth/access_token&quot;,
		RedirectURL:  &quot;http://example.com/&quot;,
	}

	code := r.FormValue(&quot;code&quot;)
	if code == &quot;&quot; {
		http.Redirect(w, r, config.AuthCodeURL(&quot;foo&quot;), http.StatusFound)
	}
	t := &amp;oauth.Transport{Config: config, Transport: &amp;urlfetch.Transport{Context: c}}
	tok, _ := t.Exchange(code)
	graphResponse, _ := t.Client().Get(&quot;https://graph.facebook.com/me&quot;)

	fmt.Fprintf(w, &quot;&lt;pre&gt;%s&lt;br /&gt;%s&lt;/pre&gt;&quot;, tok, graphResponse)
}

With correct ClientId, ClientSecret and RedirectURL, this produces the following output (edited for brevity):

&amp;{AAADTWGsQ5&lt;snip&gt;kMdjh5VKwZDZD  0001-01-01 00:00:00 +0000 UTC}

&amp;{200 OK %!s(int=200) HTTP/1.1 %!s(int=1) %!s(int=1) 
map[Connection:[keep-alive] Access-Control-Allow-Origin:[*] 
&lt;snip&gt;
Content-Type:[text/javascript; charset=UTF-8] 
Date:[Wed, 06 Feb 2013 12:06:45 GMT] X-Google-Cache-Control:[remote-fetch] 
Cache-Control:[private, no-cache, no-store, must-revalidate] Pragma:[no-cache] 
X-Fb-Rev:[729873] Via:[HTTP/1.1 GWA] Expires:[Sat, 01 Jan 2000 00:00:00 GMT]] 
%!s(*urlfetch.bodyReader=&amp;{[123 34 105 100 &lt;big snip&gt; 48 48 34 125] false false}) 
%!s(int64=306) [] %!s(bool=true) map[] %!s(*http.Request=&amp;{GET 0xf840087230 
HTTP/1.1 1 1 map[Authorization:[Bearer AAADTWGsQ5NsBAC4yT0x1shZAJAtODOIx0tZCb
TYTjxFC4esEqCjPDi3REMKHBUjZCX4FIKLO1UjMpJxhJZCfGFcOJlFu7UvehkMdjh5VKwZDZD]]
  0 [] false graph.facebook.com map[]  map[]   })}

It certainly seems like I'm consistently getting an *http.Response back, so I would expect to be able to read from the response Body. However, any mention of Body--for example with:

defer graphResponse.Body.Close()

compiles, deploys, but results in the following runtime error:

panic: runtime error: invalid memory address or nil pointer dereference
runtime.panic go/src/pkg/runtime/proc.c:1442
runtime.panicstring go/src/pkg/runtime/runtime.c:128
runtime.sigpanic go/src/pkg/runtime/thread_linux.c:199
app.home app/app.go:33
net/http.HandlerFunc.ServeHTTP go/src/pkg/net/http/server.go:704
net/http.(*ServeMux).ServeHTTP go/src/pkg/net/http/server.go:942
appengine_internal.executeRequestSafely go/src/pkg/appengine_internal/api_prod.go:240
appengine_internal.(*server).HandleRequest go/src/pkg/appengine_internal/api_prod.go:190
reflect.Value.call go/src/pkg/reflect/value.go:526
reflect.Value.Call go/src/pkg/reflect/value.go:334
_ _.go:316
runtime.goexit go/src/pkg/runtime/proc.c:270

What am I missing? Is this because of the use of urlfetch rather than DefaultClient?

答案1

得分: 0

好的,这当然是我的愚蠢错误,但我可以看出其他人可能会陷入同样的陷阱,所以这是解决方案,由Andrew Gerrand和Kyle Lemons在这个google-appengine-go主题中提出(谢谢你们)。

首先,我没有处理对favicon.ico的请求。可以通过按照这里的说明并在app.yaml中添加一个部分来解决:

- url: /favicon\.ico
  static_files: images/favicon.ico
  upload: images/favicon\.ico

这解决了对favicon请求的恐慌,但对于对'/'的请求仍然会产生恐慌。问题是,我假设http.Redirect在那一点上结束处理程序的执行。它并不是这样。需要的是在重定向后添加一个return语句,或者添加一个else子句:

code := r.FormValue("code")
if code == "" {
	http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
} else {
	t := &oauth.Transport{Config: config, Transport: &urlfetch.Transport{Context: c}}
	tok, _ := t.Exchange(code)

	fmt.Fprintf(w, "%s", tok.AccessToken)
	// ...
}

当然,我不建议忽略错误,但这个部署和运行如预期,生成一个有效的令牌。

英文:

Okay, this was of course my own silly fault but I can see how others could fall into the same trap so here's the solution, prompted by Andrew Gerrand and Kyle Lemons in this google-appengine-go topic (thanks guys).

First of all, I wasn't handling requests to favicon.ico. That can be taken care of by following the instructions here and adding a section to app.yaml:

- url: /favicon\.ico
  static_files: images/favicon.ico
  upload: images/favicon\.ico

This fixed panics on favicon requests, but not panics on requests to '/'. Problem was, I'd assumed that an http.Redirect ends handler execution at that point. It doesn't. What was needed was either a return statement following the redirect, or an else clause:

code := r.FormValue(&quot;code&quot;)
if code == &quot;&quot; {
	http.Redirect(w, r, config.AuthCodeURL(&quot;foo&quot;), http.StatusFound)
} else {
	t := &amp;oauth.Transport{Config: config, Transport: &amp;urlfetch.Transport{Context: c}}
	tok, _ := t.Exchange(code)

	fmt.Fprintf(w, &quot;%s&quot;, tok.AccessToken)
	// ...
}

I don't recommend ignoring the error of course but this deploys and runs as expected, producing a valid token.

huangapple
  • 本文由 发表于 2013年2月6日 20:22:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/14729004.html
匿名

发表评论

匿名网友

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

确定