I am having issues setting sessions on one request and getting them on another page request.
I am using the mysqlstore package which was recommended on the gorilla sessions github page under the "Store Implementations" section in there readme file.
https://github.com/srinathgs/mysqlstore
I setup a simple request to test it out and it wasn't working. Here is the code:
HomeController
func (this *HomeController) SetCookie(ctx types.AppContext) web.HandlerType {
return func(c web.C, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Printf("%v\n\n", r)
ctx.SetCookies(r, w)
}
}
func (this *HomeController) GetCookie(ctx types.AppContext) web.HandlerType {
return func(c web.C, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
fmt.Printf("%v\n\n", r)
ctx.GetCookies(r)
}
}
types.AppContext
func (this *AppContext) SetCookies(r *http.Request, w http.ResponseWriter) {
fmt.Println("Setting cookies")
session, _ := this.store.Get(r, "session-name")
// Set some session values.
session.Values["foo"] = "bar"
session.Values[42] = 43
// Save it before we write to the response/return from the handler.
err := session.Save(r, w)
if err != nil {
fmt.Println("Problem Saving session data")
}
}
func (this *AppContext) GetCookies(r *http.Request) {
session, _ := this.store.Get(r, "session-name")
fmt.Println("Getting cookies")
// get some session values
for k, v := range session.Values {
fmt.Println("Key:", k)
fmt.Println("Value:", v)
}
}
main.go
ctx.SetStore("sessions", "/", 3600, []byte("somesecret"))
....
goji.Get("/set-cookie", homeCtrl.SetCookie(ctx))
goji.Get("/get-cookie", homeCtrl.GetCookie(ctx))
When I visit both pages I get no errors and I can see in the database a session was saved. However, when I try to retrieve the saved cookie, the session.Values map is empty.
When I print out the request I get the following (formatted for easy viewing):
&{
GET /get-cookie HTTP/1.1 1 1
map[
Connection:[keep-alive]
User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.107 Safari/537.36]
Cookie:[session-name=MTQ0MDQ4MjU0M3xCUXdBQWpJM3x3xeRstTXbooJerGOhzP2pvEmqIkisE1XjS76zI365pA==]
Accept-Language:[en,en-CA;q=0.8,en-US;q=0.6,ja;q=0.4]
Cache-Control:[max-age=0]
Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8]
Upgrade-Insecure-Requests:[1]
Accept-Encoding:[gzip, deflate, sdch]
]
0x66a9a0 0 []
false localhost:8000 map[]
map[]
<nil>
map[]
[::1]:62375
/get-cookie
<nil>
}
As you can see I am sending a cookie and it is the "session-name" one. In Chrome when I look at the cookies I can see it locally as well.
Name: session-name
Value: MTQ0MDQ4MjU0M3xCUXdBQWpJM3x3xeRstTXbooJerGOhzP2pvEmqIkisE1XjS76zI365pA
Domain: localhost
Path: /
Expires/Max-Age: 2015-08-25T07:02:23.347Z (it was about 10:50pm - 11:00pm when I tested this out)
I have been looking at this and can't figure out why it doesn't work. This is as simple as I can make it and according to the examples this should work but it doesn't. Is there something I am missing in my code to make it work?
So I figured it out with some digging around. When I was logging some errors within the mysqlstore package I found this little error which led me to the solution:
panic: sql: Scan error on column index 2: unsupported driver -> Scan pair: []uint8 -> *time.Time
I then googled around a bit and found a discussion on the issue here:
https://github.com/go-sql-driver/mysql/issues/9
Basically I didn't read the Examples too well. This was in the packages Readme file the whole time. I forgot to add the query string ?parseTime=true&loc=Local at the end of my mysql connection credentials.
So basically the solution was this when setting up my database:
db, err := sql.Open("mysql", "username:password#/dbname?parseTime=true&loc=Local")
Related
As the title says, I have an error when importing go-mysql-driver package. I have installed the go-my-sql driver in my machine but the error still persists. I use XAMPP for local hosting and here’s the block of program.
package model
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
type Table interface {
Name() string
Field() ([]string, []interface{})
}
func Connect(username string, password string, host string, database string) (*sql.DB, error) {
conn := fmt.Sprintf("%s:%s#tcp(%s:3306)/%s", username, password, host, database)
db, err := sql.Open("mysql", conn)
return db, err
}
func CreateDB(db *sql.DB, name string) error {
query := fmt.Sprintf("CREATE DATABASE %v", name)
_, err := db.Exec(query)
return err
}
func CreateTable(db *sql.DB, query string) error {
_, err := db.Exec(query)
return err
}
func DropDB(db *sql.DB, name string) error {
query := fmt.Sprintf("DROP DATABASE %v", name)
_, err := db.Exec(query)
return err
}
could not import github.com/go-sql-driver/mysql (no required modules provides package "github.com/go-sql-driver/mysql")
screenshot of what's happening
It seems that you read the tutorial for an older go version.
Go 1.17 requires dependencies must be explicitly in go.mod.
Maybe you could try go module first (https://go.dev/blog/using-go-modules)
Your IDE is not showing you the whole picture. By running go run main.go (or whatever main file you have) on the command line, you can see the same error as you're seeing on your IDE with some extra:
$ go run main.go
main.go:7:5: no required module provides package github.com/go-sql-driver/mysql; to add it:
go get github.com/go-sql-driver/mysql
By issuing the suggested command go get github.com/go-sql-driver/mysql, you'll get the dependency added to your go.mod file and the contents of the package will be downloaded to your machine.
Your next execution will work:
$ go run main.go
Hello world
I've made some small modifications to your code to work, I'll add them here for completeness:
I've used the same source, but changed the package name to main.
I've added a main function to the bottom of the file:
func main() {
fmt.Println("Hello world")
_, err := Connect("username", "password", "localhost", "db")
if err != nil {
panic(err)
}
}
I've saved to a file named main.go
I've initialized the go.mod file by running go mod init test and go mod tidy, then I took the steps described on the beginning of the answer.
Although the output setting has been set to text
~/.aws/config
[default]
output=text
the aws-sdk-go returns json. The question is whether the output could be switched to text.
When:
aws route53 get-hosted-zone --id some-id
is run, the output looks as follows:
NAMESERVERS some-ns
NAMESERVERS some-ns1
NAMESERVERS some-ns2
NAMESERVERS some-ns3
According to the this AWS documentation one could set the configuration:
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-east-2")},
)
One attempt was to consult this Config struct, but an Output option seems to be omitted.
How to set the output to text?
Note: an issue has added to the github page of the aws-sdk-go as well.
Example
package main
import (
"fmt"
"log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
)
func main() {
session, err := session.NewSession()
if err != nil {
log.Fatal(err)
}
r53 := route53.New(session)
listParams := &route53.ListResourceRecordSetsInput{
HostedZoneId: aws.String("some-id"),
}
records, err := r53.ListResourceRecordSets(listParams)
if err != nil {
log.Fatal(err)
}
fmt.Println(records)
}
returns:
{
IsTruncated: false,
MaxItems: "100",
ResourceRecordSets: [
{
Name: "some-domain.",
ResourceRecords: [{
Value: "some-ip"
}],
TTL: 7200,
Type: "A"
}
}
while aws route53 list-resource-record-sets --hosted-zone-id some-id, results in:
RESOURCERECORDSETS some-domain. 7200 A
RESOURCERECORDS some-ip
Problem
While it is possible to set the format of the aws-cli to output, it does not seem to be possible to do the same for the SDK.
Question
How to let the go-aws-sdk return text rather than json?
I have all of the information you need, you just have to unravel it from the response (records).
To get similar results from the last cli command:
for _, recordSet := range records.ResourceRecordSets {
log.Println("RESOURCERECORDSETS " + *recordSet.Name + strconv.Itoa(int(*recordSet.TTL)) + *recordSet.Type)
for _, record := range recordSet.ResourceRecords {
log.Println("RESOURCERECORDS " + *record.Value)
}
log.Println("")
}
I am using the following sample program:
func getEnv(appName string, env string) {
svc := elasticbeanstalk.New(session.New(), &aws.Config{Region: aws.String("us-east-1")})
params := &elasticbeanstalk.DescribeConfigurationSettingsInput{
ApplicationName: aws.String(appName), // Required
EnvironmentName: aws.String(env),
}
resp, err := svc.DescribeConfigurationSettings(params)
if err != nil {
fmt.Println(err.Error())
return
}
v := resp.ConfigurationSettings
fmt.Printf("%s", v)
}
It's printing out the following response; this looks like a valid json except for the missing quote makes. ex: ApplicationName and not "ApplicationName".
How do I parse this? or get a valid json from AWS?
ConfigurationSettings: [{
ApplicationName: "myApp",
DateCreated: 2016-01-12 00:10:10 +0000 UTC,
DateUpdated: 2016-01-12 00:10:10 +0000 UTC,
DeploymentStatus: "deployed",
Description: "Environment created from the EB CLI using \"eb create\"",
EnvironmentName: "stag-myApp-app-s1",
OptionSettings: [
...
resp.ConfigurationSettings is not in JSON format any more, the aws-sdk-go package handled that for you. When you do,
v := resp.ConfigurationSettings
v contains an instance []*ConfigurationSettingsDescription that was parsed from the JSON response, and you don't have to parse it yourself. What you are seeing when you print it out is the Go struct representation. You can just go ahead and use it:
if len(v) > 0 {
log.Println(v[0].ApplicationName)
}
This should print out myApp
I can connect to the RDS instance using mysql -h ... command so I know it's not a security group problem.
I've tried to use:
sql.Open("mysql", "id:password#tcp(your-amazonaws-uri.com:3306)/dbname")
in the readme file of go-sql-driver(https://github.com/go-sql-driver/mysql), but it doesn't seem to work.
I'm using my username under the RDS instance instead of id here though.
Edit:
The error returned is: panic runtime error: invalid memory address or nil pointer deference [signal 0xb code=0x1 addr=0x20 pc=0x5b551e]
goroutine 16 [running]
runtime.panic(0x7d4fc0, 0xa6ca73)...database/sql.(*Rows).Next...
It works fine with my local DB.
The connection string for sql.Open() is in DSN format.
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
db, err := sql.Open("mysql", "<username>:<password>#tcp(<AWSConnectionEndpoint >:<port>)/<dbname>")
if err != nil {
fmt.Print(err.Error())
}
defer db.Close()
Make sure the actual error isn't related to an import issue (as in issues 266)
Check (to be sure you are using the latest versions, as in this issue):
your Go-MySQL-Driver version (or git SHA)
your Go version (run go version in your console)
If the error isn't directly in the Open step, but when accessing the Rows, check this comment out:
Use either a for loop (for rows.Next() { ... }) or something like this:
if rows.Next() {
// whatever
} else {
// catch error with rows.Err()
}
rows.Close() // <- don't forget this if you are not iterating over ALL results
I'm trying to send a JSON message with Go.
This is the server code:
func (network *Network) Join(
w http.ResponseWriter,
r *http.Request) {
//the request is not interesting
//the response will be a message with just the clientId value set
log.Println("client wants to join")
message := Message{-1, -1, -1, ClientId(len(network.Clients)), -1, -1}
var buffer bytes.Buffer
enc := json.NewEncoder(&buffer)
err := enc.Encode(message)
if err != nil {
fmt.Println("error encoding the response to a join request")
log.Fatal(err)
}
fmt.Printf("the json: %s\n", buffer.Bytes())
fmt.Fprint(w, buffer.Bytes())
}
Network is a custom struct. In the main function, I'm creating a network object and registering it's methods as callbacks to http.HandleFunc(...)
func main() {
runtime.GOMAXPROCS(2)
var network = new(Network)
var clients = make([]Client, 0, 10)
network.Clients = clients
log.Println("starting the server")
http.HandleFunc("/request", network.Request)
http.HandleFunc("/update", network.GetNews)
http.HandleFunc("/join", network.Join)
log.Fatal(http.ListenAndServe("localhost:5000", nil))
}
Message is a struct, too. It has six fields all of a type alias for int.
When a client sends an http GET request to the url "localhost:5000/join", this should happen
The method Join on the network object is called
A new Message object with an Id for the client is created
This Message is encoded as JSON
To check if the encoding is correct, the encoded message is printed on the cmd
The message is written to the ResponseWriter
The client is rather simple. It has the exact same code for the Message struct. In the main function it just sends a GET request to "localhost:5000/join" and tries to decode the response. Here's the code
func main() {
// try to join
var clientId ClientId
start := time.Now()
var message Message
resp, err := http.Get("http://localhost:5000/join")
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.Status)
dec := json.NewDecoder(resp.Body)
err = dec.Decode(&message)
if err != nil {
fmt.Println("error decoding the response to the join request")
log.Fatal(err)
}
fmt.Println(message)
duration := time.Since(start)
fmt.Println("connected after: ", duration)
fmt.Println("with clientId", message.ClientId)
}
I've started the server, waited a few seconds and then ran the client. This is the result
The server prints "client wants to join"
The server prints "the json: {"What":-1,"Tag":-1,"Id":-1,"ClientId":0,"X":-1,"Y":-1}"
The client prints "200 OK"
The client crashes "error decoding the response to the join request"
The error is "invalid character "3" after array element"
This error message really confused me. After all, nowhere in my json, there's the number 3. So I imported io/ioutil on the client and just printed the response with this code
b, _ := ioutil.ReadAll(resp.Body)
fmt.Printf("the json: %s\n", b)
Please note that the print statement is the same as on the server. I expected to see my encoded JSON. Instead I got this
"200 OK"
"the json: [123 34 87 104 97 116 ....]" the list went on for a long time
I'm new to go and don't know if i did this correctly. But it seems as if the above code just printed the slice of bytes. Strange, on the server the output was converted to a string.
My guess is that somehow I'm reading the wrong data or that the message was corrupted on the way between server and client. But honestly these are just wild guesses.
In your server, instead of
fmt.Fprint(w, buffer.Bytes())
you need to use:
w.Write(buffer.Bytes())
The fmt package will format the Bytes() into a human-readable slice with the bytes represented as integers, like so:
[123 34 87 104 97 116 ... etc
You don't want to use fmt.Print to write stuff to the response. Eg
package main
import (
"fmt"
"os"
)
func main() {
bs := []byte("Hello, playground")
fmt.Fprint(os.Stdout, bs)
}
(playground link)
Produces
[72 101 108 108 111 44 32 112 108 97 121 103 114 111 117 110 100]
Use the Write() method of the ResponseWriter instead
You could have found this out by telneting to your server as an experiment - always a good idea when you aren't sure what is going on!