Hello. I'm using the go language in Google App Engine. I'm having trouble getting the logged-in user's information. Similarly, the login URL and logout URL cannot be obtained. All nil will be returned. user.IsAdmin (c) returns false. please help me.
admin.go
func Entry(w http.ResponseWriter, r *http.Request) {
...
c := appengine.NewContext(r)
inUrl, err := user.LoginURL(c, "/admin/top/")
...
}
func AdminTop(w http.ResponseWriter, r *http.Request) {
...
c := appengine.NewContext(r)
booisadmin := user.IsAdmin(c)
u := user.Current(c)
outUrl, err := user.LogoutURL(c, "/")
...
}
app.yaml
runtime: go116
app_engine_apis: true
handlers:
- url: /assets/css
mime_type: text/css
static_dir: assets/css
- url: /assets/html
mime_type: text/html
static_dir: assets/html
- url: /assets/img
static_dir: assets/img
- url: /admin/.*
login: require
script: _go_app
- url: /.*
script: _go_app
When you use login: required in app.yaml, you can get the logged in users information via the following headers -
X-Appengine-User-Id
X-Appengine-User-Nickname
X-Appengine-User-Email
I confirmed the above works in Go (ran it on my local machine)
I believe the same headers should work when you use the Users API but you can always dump all the headers to figure out the values that you need.
Regarding using the User API to get login/logout urls, I also got blank values when I tried it on my local machine but I'm a novice when it comes to Go. You might want to try and see if the calls work when you deploy to Production
Thank you. I was able to solve it. It was because I was using Gorillamux. I solved it with the following code.
import (
...
"github.com/gorilla/mux"
"net/http"
...
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/", indexHandler)
r.HandleFunc("/admin/", admin.Entry)
http.Handle("/", r)
}
The last http.Handle ("/", r) was missing.
I wrote the details here.
https://uubaago.blogspot.com/
Thank you very much NoCommandLine!
Related
I'm trying to implement a google cloud function to test Google Logging client library. below is my code
// Package p contains an HTTP Cloud Function.
package loggingclient
import (
"cloud.google.com/go/logging"
"net/http"
"context"
"fmt"
)
// HelloWorld prints the JSON encoded "message" field in the body
// of the request or "Hello, World!" if there isn't one.
func HelloWorld(w http.ResponseWriter, r *http.Request) {
label := map[string]string{"priority": "High"}
var projectName = "my-project-id"
ctx := context.Background()
client, err := logging.NewClient(ctx, projectName)
if err != nil {
fmt.Printf("client not created: %v", err)
}
lg := client.Logger("MY-LOGGER")
lg.Log(logging.Entry{
Payload: "Hello, This is error!!",
Severity: logging.Error,
Labels: label,
})
client.Close()
}
Here, I'm expecting a log entry with a message:"Hello, This is error!!" and with a lable:"priority": "High" and severirty "ERROR"
But actually, when I trigger this Cloud Function, I didn't get any new log entries. Therefore don't client logging libraries work inside cloud functions?, How to resolve this?
Thanks
It works on cloud functions. I have done the exact same thing in a cloud function before. You can use google's official documenation with cloud function logging here
Also ensure that the service account have necessary permissions for logging
https://cloud.google.com/logging/docs/access-control
I have a Go app that use Gin gonic and a Nginx reverse proxy that send trafic to another app on domain.com and send all the *.domain.com subdomains traffic directly to my go app.
My Go app then has a middleware that will read the hostname that nginx passes to it from Context and allow my handlers to know what subdomain is being request and return the proper data and cookies for said subdomain.
It's a pretty simple setup and it seems to work fine from my test in postman as all my routes are the same across all my subdomains so this way i can only use one router for all of them instead of one router per subodmain.
Now my big problem come when i'm trying to do end to end testing.
I'm setting up my test like this :
router := initRouter()
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/api/login", bytes.NewBuffer(jsonLogin))
req.Header.Set("Content-Type", "application/json")
router.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
with initRouter() returning a gin engine with all my routes and middlewares loaded and the rest as a basic test setup.
Obviously the test will fail as the gin Context won't ever receive a subdomain from context and act as if everything is coming from localhost:8000.
Is there a way to either :
"Mock" a subdomain so that the router think the call is coming from foo.localhost.com instead of localhost
Setup my test suit so that the test request are routed thought nginx.. i'd prefer solution 1 as this would be a mess to setup / maintain.
Edit :
As per the httptest doc i've tried to hard code foo.localhost as the param of the NewRequest but it doesn't really behave as i need it to behave :
NewRequest returns a new incoming server Request, suitable for passing to an http.Handler for testing.
The target is the RFC 7230 "request-target": it may be either a path or an absolute URL. If target is an absolute URL, the host name from the URL is used. Otherwise, "example.com" is used.
When hardcoding http://foo.localhost.com/api/login or foo.localhost.com/api/login as the request target it directly passes it to my router under "foo.localhost.com/api/login" while nginx would just hit the /api/login directly and parse from c.Request.Host
Edit 2:
I'm currently exploring setting the host manually using :
req.Header.Set("Host", "foo.localhost")
The request returned by http.NewRequest isn't suitable for passing directly to ServeHTTP. Use one returned by httptest.NewRequest instead.
Simply set the Host field directly:
package main
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestHelloWorld(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if r.Host != "foobar" {
t.Errorf("Host is %q, want foobar", r.Host)
}
})
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/api/login", nil)
r.Host = "foobar"
mux.ServeHTTP(w, r)
}
In documentation https://github.com/swaggo/swag is using gin to initialize server, but in my application i'm using http.ServeMux and how initialize swaggo without use gin server
in Docs use
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
how can I use something like ...
mu.Handle("/swagger/*any", swaggerFiles.Handler)
......
follows as my initial idea, but don't work... rsrs
func Server() *http.ServeMux {
docs.SwaggerInfo.Title = "Swagger Example API"
docs.SwaggerInfo.Description = "This is a sample server Petstore server."
docs.SwaggerInfo.Version = "1.0"
docs.SwaggerInfo.Host = "petstore.swagger.io"
mu := http.NewServeMux()
mu.Handle("/metrics", promhttp.Handler())
mu.Handle("/swagger/*any", swaggerFiles.Handler)
mu.HandleFunc("/helloWorld", handlers.NewHelloWorldHandler().HelloWorldHandler)
mu.HandleFunc("/production/", handlers.NewProductionHandler().ProductionHandler)
return mu
}
If you have your swagger files built for distribution (i.e. static files) and are in say the directory: /some/dir/path/static/swagger
This should work with go's http router:
staticFilesPath := "/some/dir/path/static"
staticRoute := "/static/"
h := http.NewServeMux()
// static file handler for route '/static/*'
h.Handle(
staticRoute,
http.StripPrefix(
staticRoute,
http.FileServer(http.Dir(staticFilesPath)),
),
)
I find it helpful to add this also:
// (redirect a route if not recognized - remove in production)
//
// unrecognized routes will redirect to Swagger API docs
h.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, staticRoute + "swagger/", http.StatusSeeOther)
})
package main
import (
"log"
"net/http"
)
func main() {
fs := http.FileServer(http.Dir("."))
http.Handle("/", fs)
log.Println("Listening...")
http.ListenAndServe(":3000", nil)
}
So I have a index.html file and want server to stop showing it.
The docs for FileServer state that:
As a special case, the returned file server redirects any request
ending in "/index.html" to the same path, without the final
"index.html".
So /index.html is redirected to /, /foo/bar/index.html is redirected to /foo/bar/.
To avoid this register an additional handler for the special case.
http.HandleFunc("/index.html", func(w http.ResponseWriter, r *http.Request) {
f, err := os.Open("index.html")
if err != nil {
// handle error
return
}
http.ServeContent(w, r, "index.html", time.Now(), f)
})
Please note I'm using ServeContent insead of ServeFile because ServeFile handles /index.html requests the same way as FileServer.
There's no redirection going on, the default file to render when requesting a directory is index.html. The directory listing is a fallback for when this file isn't found, so you can't get a directory listing without removing the index.html file.
If you want a directory listing, you'll have to write it out yourself, which you can then format and style however you choose. The basic structure is very simple if you want to write it directly, take the internal dirList function for example:
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, "<pre>\n")
for _, d := range dirs {
name := d.Name()
if d.IsDir() {
name += "/"
}
url := url.URL{Path: name}
fmt.Fprintf(w, "%s\n", url.String(), htmlReplacer.Replace(name))
}
fmt.Fprintf(w, "</pre>\n")
I think there are 3 ways:
Make a PR which allow accessing /index.html without redirect to golang's net/http/fs library. https://github.com/golang/go/issues/53870
Hack your code: replace /index.html with / to stop 301 redirect, e.g., https://github.com/ahuigo/go-lib/commit/1a99191d5c01cf9025136ce8ddb9668f123de05c#diff-67e8621fbb99281a50c089bae53d4874663d3d21ca5e90809ec207c070029351R44
Customize your own http fs handler instead of official tools.
I have been looking for an example GAE script in go to get my image that I got from the resulted screenshot of PageSpeed Insights and saved it as json_decode object using Kohana/Cache to Google Cloud Storage (GCS).
The reason of this method is simply because I found this Kohana model is the most convenient way writing files to GCS, although I am seeking also other way like this to write files to GCS using Blobstore to serve them while the Go API Files has been deprecate as documented here.
Here is the form of stored object containing the screenshot image data (base64) which is saved as public in default application bucket with object name images/thumb/mythumb.jpg:
stdClass Object
(
[screenshot] => stdClass Object
(
[data] => _9j_4AAQSkZJRgABAQAAAQABAAD_...= // base64 data
[height] => 240
[mime_type] => image/jpeg
[width] => 320
)
[otherdata] => Array
(
[..] => ..
[..] => ..
)
)
I want to get this image that set as public using my customized url as below that to be proceed through go module and also I need it to be expired in a certain time because I have managed to update the image content itself regularly:
http://myappId.appspot.com/image/thumb/mythumb.jpg
I have set in disptach.yaml to send all image request to my go module as below:
- url: "*/images/*"
module: go
and set the handler in go.yaml to proceed the image request as below:
handlers:
- url: /images/thumb/.*
script: _go_app
- url: /images
static_dir: images
Using this directive I have got that all /images/ request (other than /images/thumb/ request) serve images from the static directory and that /images/thumb/mythumb.jpg goes to the module application.
So left what code I have to use (see ????) in my application file named thumb.go as below:
package thumb
import(
//what to import
????
????
)
const (
googleAccessID = "<serviceAccountEmail>#developer.gserviceaccount.com"
serviceAccountPEMFilename = "YOUR_SERVICE_ACCOUNT_KEY.pem"
bucket = "myappId.appspot.com"
)
var (
expiration = time.Now().Add(time.Second * 60) //expire in 60 seconds
)
func init() {
http.HandleFunc("/images/thumb/", handleThumb)
}
func handleThumb(w http.ResponseWriter, r *http.Request) {
ctx := cloud.NewContext(appengine.AppID(c), hc)
???? //what code to get the string of 'mythumb.jpg' from url
???? //what code to get the image stored data from GCS
???? //what code to encoce base64 data
w.Header().Set("Content-Type", "image/jpeg;")
fmt.Fprintf(w, "%v", mythumb.jpg)
}
I have taken many codes from some examples like this, this or this but could not get one works so far. I have also tried a sample from this which is almost close to my case but also found no luck.
So in generally t was mainly due to lack on what are the correct code to be put on the line that I marked by ???? as well the relevant library or path to be imported. I have also checked the GCS permission if something have been missing as described here and here.
I shall thank you much for your help and advise.
From what I've read in your description, it seems that the only relevant parts are the ???? lines in the actual Go code. Let me know if that's not the case.
First ????: "what code to get the string of 'mythumb.jpg' from url"?
From reading the code, you're looking to extract mythumb.jpg from a url like http://localhost/images/thumb/mythumb.jpg. A working example is available at the Writing Web Applications tutorial:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Such that
http://localhost:8080/monkeys
Prints
Hi there, I love monkeys!
Second ????: "what code to get the image stored data from GCS"?
The API method you're probably looking to use is storage.objects.get.
You did link to one of the JSON API Go Examples for Google Cloud Storage, which is a good general reference, but is not related to the problem you're trying to solve. That particular example is put together for Client-side applications (hence the redirectURL = "urn:ietf:wg:oauth:2.0:oob" line). Additionally, this sample uses deprecated/out-of-date oauth2 and storage packages.
One of the cleanest (and non-deprecated) ways to do this for an application which wants to access its own buckets on behalf of itself would be to use the golang/oauth2 and Google APIs Client Library for Go packages.
An example of how to authenticate with JSON Web Token auth with the golang/oauth2 package is available in the repo:
func ExampleJWTConfig() {
conf := &jwt.Config{
Email: "xxx#developer.com",
// The contents of your RSA private key or your PEM file
// that contains a private key.
// If you have a p12 file instead, you
// can use `openssl` to export the private key into a pem file.
//
// $ openssl pkcs12 -in key.p12 -out key.pem -nodes
//
// It only supports PEM containers with no passphrase.
PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
Subject: "user#example.com",
TokenURL: "https://provider.com/o/oauth2/token",
}
// Initiate an http.Client, the following GET request will be
// authorized and authenticated on the behalf of user#example.com.
client := conf.Client(oauth2.NoContext)
client.Get("...")
}
Next, instead of using the oauth2 client directly, use that client with the Google APIs Client Library for Go mentioned earlier:
service, err := storage.New(client)
if err != nil {
fatalf(service, "Failed to create service %v", err)
}
Notice the similarity to the out-of-date JSON API Go Examples?
In your handler, you'll want to go out and get the related object using func ObjectsService.Get. Assuming that you know the name of the object and bucket, that is.
Straight from the previous example, you can use code similar to what's below to retrieve the download link:
if res, err := service.Objects.Get(bucketName, objectName).Do(); err == nil {
fmt.Printf("The media download link for %v/%v is %v.\n\n", bucketName, res.Name, res.MediaLink)
} else {
fatalf(service, "Failed to get %s/%s: %s.", bucketName, objectName, err)
}
Then, fetch the file, or do whatever you want with it. Full example:
import (
"golang.org/x/oauth2"
"golang.org/x/oauth2/jwt"
"google.golang.org/api/storage/v1"
"fmt"
)
...
const (
bucketName = "YOUR_BUCKET_NAME"
objectName = "mythumb.jpg"
)
func main() {
conf := &jwt.Config{
Email: "xxx#developer.com",
PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
Subject: "user#example.com",
TokenURL: "https://provider.com/o/oauth2/token",
}
client := conf.Client(oauth2.NoContext)
service, err := storage.New(client)
if err != nil {
fatalf(service, "Failed to create service %v", err)
}
if res, err := service.Objects.Get(bucketName, objectName).Do(); err == nil {
fmt.Printf("The media download link for %v/%v is %v.\n\n", bucketName, res.Name, res.MediaLink)
} else {
fatalf(service, "Failed to get %s/%s: %s.", bucketName, objectName, err)
}
// Go fetch the file, etc.
}
Third ????: "what code to encoce base64 data"?
Pretty simple with the encoding/base64 package. SO simple, that they've included an example:
package main
import (
"encoding/base64"
"fmt"
)
func main() {
data := []byte("any + old & data")
str := base64.StdEncoding.EncodeToString(data)
fmt.Println(str)
}
Hope that helps.