I'm using google oauth services, provided by golang.org/x/oauth2 library. I can successfully login and get data as needed. Also, store token values in DB.
However, when logged in and I refresh the page, it results in an error,
This page isn’t working
localhost didn’t send any data.
ERR_EMPTY_RESPONSE
After getting some online references, I tried refresh_tokens, however, the error still persists.
Code for oauth2 client
const OauthGoogleUrlAPI = "https://www.googleapis.com/oauth2/v2/userinfo?access_token="
func OAuthClient(ctx context.Context, userId string) (client *http.Client, err error) {
var userToken *oauth2.Token
if userToken, err = GetToken(userId); err != nil {
if userToken, err = newToken(ctx, userId); err != nil {
return
}
}
if !userToken.Valid() { // if user token is expired
updatedToken, err := GoogleOauthConfig.TokenSource(context.TODO(), userToken).Token()
if err != nil {
log.Printf("\n\nError refreshing token ; Error : %s\n\n", err)
}
return GoogleOauthConfig.Client(ctx, updatedToken), err
}
return GoogleOauthConfig.Client(ctx, userToken), err
}
Here, GetToken retrieves existing tokens from db, newToken updates them in db.
The Callback Handler Code,
func GoogleCallBackHandler(w http.ResponseWriter, r *http.Request) {
var code string
temp = r.URL.Query()["code"]
for i := range temp {
code = temp[i]
}
token, err := GoogleOauthConfig.Exchange(context.Background(), code)
HandleError(err)
response, err := http.Get(OauthGoogleUrlAPI + token.AccessToken)
HandleError(err)
defer response.Body.Close()
user, err := ioutil.ReadAll(response.Body)
HandleError(err)
session, err := Store.Get(r, "authSession")
HandleError(err)
session.Values["user"] = string(user)
err = session.Save(r, w)
HandleError(err)
//Get user data in struct form
var userJson User
json.Unmarshal(user, &userJson)
//resp, err := client.Get("")
//database Store
err = InsertUserDetails(userJson, token.AccessToken, token.RefreshToken, token.Expiry)
HandleError(err)
w.Write([]byte("<html><head><title>Golang OAuth</title></head><body>" +
"Hi, " + string(user) + "<br>" +
"<a href='/logout'><button>Logout</button></a><br>" +
"</body></html>"))
client, err := UserOAuthClient(oauth2.NoContext, userJson.ID)
//resp, err := client.Get("")
}
the callback URL is of the form
http://localhost:3002/google/callback?state=state-token&code=4%2F0QGia33kbVbx2lsk92KFGEivY240zjnw9pnwmqJX_zZVCLbn_YASIMVcORIfK_ysNNYzCiBVzMc7qFJbjVjgfTM&scope=email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid&authuser=0&prompt=consent#
I'm confused about putting things together and about the flow. It'll be great if you could point out mistakes or make suggestions.
Thanks!
Related
Let's say I have a handler that makes a request and gets the latest data on the selected stock:
func (ss *stockService) GetStockInfo(ctx *gin.Context) {
code := ctx.Param("symbol")
ss.logger.Info("code", code)
url := fmt.Sprintf("URL/%v", code)
ss.logger.Info(url)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
errs.HTTPErrorResponse(ctx, &ss.logger, errs.New(errs.Internal, err))
return
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
errs.HTTPErrorResponse(ctx, &ss.logger, errs.New(errs.Internal, err))
return
}
defer resp.Body.Close()
var chart ChartResponse
err = json.NewDecoder(resp.Body).Decode(&chart)
if err != nil {
errs.HTTPErrorResponse(ctx, &ss.logger, errs.New(errs.Internal, err))
return
}
ctx.JSON(http.StatusOK, chart)
}
And I want to add caching here. Since I don't have a lot of experience right now, I'm interested in proper interaction with the cache.
I think that if, for example, it is not possible to save to the cache for some reason, then you can simply make a request to the api. Then I wonder if it would be right to save to the cache in a separate goroutine and immediately return the response:
func (ss *stockService) GetStockInfo(ctx *gin.Context) {
code := ctx.Param("symbol")
stockInfo, err := ss.cache.Get(code)
if err == nil {
// FIND
...
ctx.JSON(http.StatusOK, chart)
} else {
ss.logger.Info("code", code)
url := fmt.Sprintf("URL/%v", code)
ss.logger.Info(url)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
...
err = json.NewDecoder(resp.Body).Decode(&chart)
// IS IT A GOOD WAY ?
go ss.cache.Save(code,chart,expireAt)
ctx.JSON(http.StatusOK, chart)
}
}
I use redis as a cache.
I will be glad if someone says what is wrong with this approach.
I am new to Golang. I have made a demo app for practice in which i have login register and homepage. When i go to login page it redirects to home page . I am not understanding what is happening.
This is my go code
package main
import (
"database/sql"
"fmt"
"net/http"
_ "github.com/go-sql-driver/mysql"
"golang.org/x/crypto/bcrypt"
)
var db *sql.DB
var err error
func signupPage(res http.ResponseWriter, req *http.Request) {
fmt.Println("entered Signup")
if req.Method != "POST" {
http.ServeFile(res, req, "template/signup.html")
return
}
email := req.FormValue("email")
password := req.FormValue("password")
var user string
err := db.QueryRow("SELECT email FROM users WHERE email=?", email).Scan(&user)
switch {
case err == sql.ErrNoRows:
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
http.Error(res, "Server error, unable to create your account.", 500)
return
}
_, err = db.Exec("INSERT INTO users(email, password) VALUES(?, ?)", email, hashedPassword)
if err != nil {
http.Error(res, "Server error, unable to create your account.", 500)
}
res.Write([]byte("User Registered Successfully"))
return
case err != nil:
http.Error(res, "Server error, unable to create your account.", 500)
return
default:
http.Redirect(res, req, "/", 301)
}
}
func loginPage(res http.ResponseWriter, req *http.Request) {
fmt.Println("Entered login")
if req.Method != "POST" {
http.ServeFile(res, req, "template/login.html")
return
}
email := req.FormValue("email")
password := req.FormValue("password")
var dbemail string
var dbpassword string
err := db.QueryRow("SELECT email, password FORM users WHERE email=?", email).Scan(&dbemail, &dbpassword)
if err != nil {
http.Redirect(res, req, "/login", 301)
return
}
err = bcrypt.CompareHashAndPassword([]byte(dbpassword), []byte(password))
if err != nil {
http.Redirect(res, req, "/login", 301)
return
}
res.Write([]byte("Hello" + dbemail))
}
func homePage(res http.ResponseWriter, req *http.Request) {
http.ServeFile(res, req, "template/landing.html")
}
func main() {
db, err = sql.Open("mysql", "root:password#/golang_demo")
if err != nil {
panic(err.Error())
} else {
fmt.Println("Database connected successfully")
}
defer db.Close()
err = db.Ping()
if err != nil {
panic(err.Error())
}
http.HandleFunc("/login", loginPage)
http.HandleFunc("/", homePage)
http.HandleFunc("/signup", signupPage)
http.ListenAndServe(":9090", nil)
}
When i go to signup page it goes successfully. But when i go to login page it is redirecting me to home page. Please help!
This is just an educated guess, since from the code I am seeing it doesn't look like that should happen. Since you are using 301 redirects (Moved Permanently), I would guess that at some point in writing your function and testing it, you did a redirect to the home page. Since it is "moved permanently", the browser now doesn't even ask the server if it should redirect when it sees that URL, it just does it.
If you're in chrome, open the dev tools, and disabling the cache should resolve it. Or even better, try it in a different browser that you haven't used on this site yet, and see if it works there.
I have built a Backend API in Go, it works however I want refactor the code for the DB access layer into a function - idiomatically.
// Get the form data entered by client; FirstName, LastName, phone Number,
// assign the person a unique i.d
// check to see if that user isn't in the database already
// if they are send an error message with the a 'bad' response code
// if they aren't in db add to db and send a message with success
func CreateStudentAccountEndpoint(response http.ResponseWriter, request *http.Request){
client, err := mongo.NewClient("mongodb://localhost:27017")
if err != nil {
log.Fatalf("Error connecting to mongoDB client Host: Err-> %v\n ", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
err = client.Connect(ctx)
if err != nil {
log.Fatalf("Error Connecting to MongoDB at context.WtihTimeout: Err-> %v\n ", err)
}
response.Header().Set("Content-Type", "application/json")
studentCollection := client.Database(dbName).Collection("students")
_, err = studentCollection.InsertOne(context.Background(),data)
if err != nil {
response.WriteHeader(501)
response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
}
// encoding json object for returning to the client
jsonStudent, err := json.Marshal(student)
if err != nil {
http.Error(response, err.Error(), http.StatusInternalServerError)
}
response.Write(jsonStudent)
}
I understand that I can create a method which returns (*mongoClient, err) as I utilise the client local variable later on in the code.
However I am lost as to how to implement the defer cancel() part because it executes once the method CreateStudenAccountEndpoint is at the end. But I am at a loss on how to implement this defer section in a method that will recognise that I want the defer to happen at the end of the function that calls the DB access layer method e.g CreateStudentAccountEndpoint not the actual db access method itself.
As I understand it, the connection should be long-lived and set up as a part of a constructor, i.e. not part of the request flow.
This will typically look something like this:
type BackendAPI struct {
client *mongo.Client
}
func NewBackendAPI(mongoURI string) (*BackendAPI, error) {
client, err := mongo.NewClient(mongoURI)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
err = client.Connect(ctx)
if err != nil {
return nil, err
}
return &BackendAPI{client}, nil
}
func (api *BackendAPI) func CreateStudentAccountEndpoint(response http.ResponseWriter, request *http.Request) {
response.Header().Set("Content-Type", "application/json")
// note the use of the long-lived api.client, which is connected already.
studentCollection := api.client.Database(dbName).Collection("students")
_, err = studentCollection.InsertOne(context.Background() ,data)
if err != nil {
response.WriteHeader(501)
response.Write([]byte(`{ "message": "` + err.Error() + `" }`))
return // at this point, the method should return
}
// encoding json object for returning to the client
jsonStudent, err := json.Marshal(student)
if err != nil {
http.Error(response, err.Error(), http.StatusInternalServerError)
}
response.Write(jsonStudent)
}
If you worry about losing the connection, you could implement a call to api.client.Ping in there, but in my opinion this should only be attempted if you encounter a failure you believe you can recover from by reconnecting.
I am creating an application which is communicating with google datastore, to fetch the existing data, and perform add, edit and delete operations on that existing data. I am able to fetch the existing data, and delete the data there. But not getting how to edit/update the data there through api in golang.
Giving the code snippet which I am trying to execute for this :
func EditCustomer(w http.ResponseWriter, r *http.Request){
ctx := context.Background()
params := mux.Vars(r)
customer_id :=params["partner_id"]
projectID := util.MustGetenv("GOOGLE_CLOUD_PROJECT")
client, err := datastore.NewClient(ctx, projectID)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
var customer models.Customer
kind := util.MustGetenv("DATA_STORE_KIND")
ds.EditCustomer(client,kind,customer_id,&customer,ctx)
json.NewEncoder(w).Encode(customer)
}
EditCustomer method in dao is as :
func EditCustomer(client *datastore.Client,kind string ,name string,dst interface{},ctx context.Context) {
taskKey := datastore.NameKey(kind, name, nil)
< some methos here to edit and update itin datstorage >
}
Please advise for this. Anybody there who working with api's dev in golang ?
I would update like this:
currentCustomer := &Customer{}
_, err := client.RunInTransaction(ctx, func(tx *datastore.Transaction) error {
if getErr := tx.Get(key, currentCustomer); getErr != nil {
return getErr
}
// edit your object
var putErr error
_, putErr = tx.Put(key, updatedCustomer)
return putErr
})
if err != nil {
return nil, err
}
_, err = client.Put(ctx, key, customer)
I'm trying to make an Handler to update one row each time getting data from a submitt button,
here is my code:
func RowHandler(res http.ResponseWriter, req *http.Request) {
if req.Method != "POST" {
http.ServeFile(res, req, "homepage.html")
return
}
Person_id := req.FormValue("Person_id")
stmt, err := db.Prepare("update Cityes set Status='right' where Person_id=?")
if err != nil {
log.Print("error ", err)
}
_, err = stmt.Exec(&Person_id)
t, err := template.ParseFiles("city_update.html") //hier i just want to show a text in html Page
if err != nil {
log.Fatal(err)
}
err = t.Execute(res, "/city_update")
}
Here instead of following
err = t.Execute(res, "/city_update")
pass data to be used to fill your template as send arguement to Execute not string. link to doc
For example .
err = t.Execute(res,struct{ID string}{Person_id})