Below is the whole code of the program. It is a service that forwards requests. Currently working. What I am trying to do is get rid of the yml file that is currently storing all configs and move them to db. I don't want to mess with the code much, so my idea was to simply store the db data in the same structs.
// Config contains configuration for this service
type Instance struct {
User string `json:"user"`
Password string `json:"password"`
InstanceId string `json:"instance_id"`
InstanceType string `json:"instance_type"`
InstanceMode string `json:"instance_mode"`
ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"`
PublicKey string `json:"pubkey"`
Apis Api `json:"apis"`
}
// API struct
type Api struct {
Name string `json:"name"`
Url string `json:"url"`
Version string `json:"version"`
IsBhdGw bool `json:"isBhdGw"`
Key string `json:"key"`
}
// API struc
type InfoResponse struct {
InstanceId string `json:"instance_id"`
InstanceType string `json:"instance_type"`
InstanceMode string `json:"instance_mode"`
ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"`
PublicKey string `json:"pubkey"`
Apis Api `json:"apis"`
}
type Settings struct {
Port int `json:"port"`
User string `json:"user"`
Secret string `json:"secret"`
Mode string `json:"mode"`
}
type Instances struct {
Instances []Instance
}
var payloadLength int = 3
var instances Instances
var settings Settings
var db *sql.DB
func fetchInstances() []Instance {
rows, err := db.Query("SELECT Instances.InstanceId, Instances.User, Instances.Password, Instances.InstanceType, Instances.InstanceMode, Instances.ClientId, Instances.ClientSecret, Instances.PublicKey, Api.Name, Api.Url, Api.Version, Api.IsBhdGw, Api.Key FROM Instances, Api")
if err != nil {
panic(err.Error())
}
defer rows.Close()
instances := []Instance{}
for rows.Next() {
var instance Instance
var apis Api
err := rows.Scan(&instance.InstanceId, &instance.User, &instance.Password,
&instance.InstanceType, &instance.InstanceMode, &instance.ClientId, &instance.ClientSecret, &instance.PublicKey,
&apis.Name, &apis.Url, &apis.Version, &apis.IsBhdGw, &apis.Key)
if err != nil {
panic(err.Error())
}
instance.Apis = apis
instances = append(instances, instance)
}
return instances
}
When I run this I get a "panic: runtime error: invalid memory address or nil pointer dereference"
You are attempting to connect to an uninitiated database connection. This is due to variable shadowing.
Although you've defined a global db variable:
var db *sql.DB
Your main() method creates a new one when it connects:
db, err := sql.Open(dbDriver, dbUser+":"+dbPass+"#tcp(db:3306)/"+dbName)
So then when you call fetchInstances, which uses the global variable, the global one is still unset.
The easiest solution is to change your code in main() as follows:
var err error
db, err = sql.Open(dbDriver, dbUser+":"+dbPass+"#tcp(db:3306)/"+dbName)
But the better solution is to never use a global variable. Use the locally-defined variable in main, then pass that to fetchInstances.
As a side note: ALWAYS, ALWAYS check your errors. Twice in your code, you fail to check db connection errors, as in:
db, err := sql.Open(dbDriver, dbUser+":"+dbPass+"#tcp(db:3306)/"+dbName)
err = db.Ping()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
If the connection fails, your db.Ping() call will likely panic. Instead, you should do:
db, err := sql.Open(dbDriver, dbUser+":"+dbPass+"#tcp(db:3306)/"+dbName)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = db.Ping()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
Related
func MakeMap(w http.ResponseWriter, r *http.Request) {
// userInfo := context.Get(r, "userInfo").(model.User)
type _getData struct {
Title string `json:"title"`
Tag []string `json:"tag"`
}
var getData _getData
err := json.NewDecoder(r.Body).Decode(&getData)
if err != nil {
panic(err.Error())
}
fmt.Print(getData)
}
When I run the above code, I get the following error
2021/08/24 13:56:54 http: panic serving 127.0.0.1:50619: runtime error: invalid memory address or nil pointer dereference
goroutine 23 [running]:
net/http.(*conn).serve.func1(0x140001e9180)
/usr/local/go/src/net/http/server.go:1824 +0x108
panic(0x10505b860, 0x10522f240)
/usr/local/go/src/runtime/panic.go:971 +0x3f4
traveling/controller/mapController.MakeMap(0x1050b5630, 0x140001f40e0, 0x1400018aa00)
/Users/choeyunseog/traveling/traveling/controller/mapController/mapController.go:20 +0x3c
I've just started studying, I'm not sure why I'm having this problem, please help
err := json.NewDecoder(r.Body).Decode(&getData)
I get the following error when i change code line 20 like above
2021/08/24 14:16:44 http: panic serving 127.0.0.1:51396: invalid character '-' in numeric literal
goroutine 23 [running]:
net/http.(*conn).serve.func1(0x140001e9360)
/usr/local/go/src/net/http/server.go:1824 +0x108
panic(0x100d85d00, 0x14000206070)
/usr/local/go/src/runtime/panic.go:971 +0x3f4
traveling/controller/mapController.MakeMap(0x100df1630, 0x140001f40e0, 0x1400018aa00)
/Users/choeyunseog/traveling/traveling/controller/mapController/mapController.go:24 +0x194
net/http.HandlerFunc.ServeHTTP(0x100de75d8, 0x100df1630, 0x140001f40e0, 0x1400018aa00)
/usr/local/go/src/net/http/server.go:2069 +0x40
To get the multipart form data from a POST/PUT/PATCH request's body you can use the ParseMultipartForm method to parse the body and then access the data through the PostForm field. Or you can use FormValue to get just the first value associated with the form's field.
maxMemory := 32<<20
if err := r.ParseMultipartForm(maxMemory); err != nil {
panic(err)
}
fmt.Println(_getData{
Title: r.FormValue("title"), // FormValue returns string
Tag: r.PostForm["tag[]"], // PostForm is a map of []string
})
You can use to parse form data into json like annotated struct using package github.com/senpathi/paramex. Struct fields must be annotated with param keyword and tag name is key of the form data.
your struct should be as below.
type _getData struct {
Title string `param:"title"`
Tag []string `param:"tag[]"`
}
This is the updated MakeMap handler function for your postman request mentioned in the question
func MakeMap(w http.ResponseWriter, r *http.Request) {
// userInfo := context.Get(r, "userInfo").(model.User)
type _getData struct {
Title string `param:"title"`
Tag []string `param:"tag[]"`
}
// this needed because u send data from Postman as multipart/form-data
maxMemory := 32<<20
if err := r.ParseMultipartForm(int64(maxMemory)); err != nil {
panic(err)
}
var getData _getData
extractor := paramex.NewParamExtractor()
err := extractor.ExtractForms(&getData, r)
if err != nil {
panic(err.Error())
}
fmt.Print(getData)
//Output: {defaultMap [travelling travelling2]}
}
my code
func HostStats() (*host.InfoStat, error) {
infoStat, err := host.Info()
fmt.Printf("All Host info: ", infoStat)
return infoStat, err
}
output
All Host info: %!(EXTRA string= {"hostname":"UDAY-PC","uptime":536323,"bootTime":1559911444,"procs":248,"os":"windows","platform":"Microsoft Windows 10 Pro","platformFamily":"Standalone Workstation","platformVersion":"10.0.17134 Build 17134","kernelVersion":"","virtualizationSystem":"","virtualizationRole":"","hostid":"0b324295-3631-47db-b6e8-83cdba2a1af9"})
I want to parse and show the below value from above:
hostname
Platform
HostId
I tried and below has the additional code:
func HostStats() (*host.InfoStat, error) {
infoStat, err := host.Info()
type Information struct {
Name string
Platform string
HostId string
}
var info []Information
info, err := json.Unmarshal(infoStat, &info)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("ok: %+v", info)
//almost every return value is a struct
fmt.Printf("All Host info: ", infoStat)
return infoStat, err
}
As Adrian mentioned above, you are having problems with your Variables.
In Go you can initialize a variable like you did:
var info string
// And assign a value to it by:
info = "foo"
The Json is unmarshaled into your info variable. The return value of the json.Unmarshal is only an error. So the correct syntax would be:
var info []Information
err := json.Unmarshal(infoStat, &info)
So remember the different ways to initialize vars and assign values to those vars.
You can look at GoDocs for Variables for more Info :)
I have a server that successfully opens a connection with a second server. The second server performs an action and I am trying to get it to reply to the first server with a JSON on the same connection.
package main
import (
"fmt"
"net"
"encoding/json"
)
type NewContainerJSON struct {
Action string `json:"Action"`
ContainerName string `json:"ContainerName"`
BaseServer string `json:"BaseServer"`
CMS string `json:"CMS"`
WebsiteName string `json:"WebsiteName"`
DBrootPWD string `json:"DBrootPWD"`
DBadminUname string `json:"DBadminUname"`
DBadminPWD string `json:"DBadminPWD"`
}
func main() {
service := "127.0.0.1:8081"
tcpAddr, err := net.ResolveTCPAddr("tcp", service)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
conn, err := listener.Accept()
checkError(err)
decoder := json.NewDecoder(conn)
encoder := json.NewEncoder(conn)
var b NewContainerJSON
err = decoder.Decode(&b)
checkError(err)
fmt.Println(b.Action)
if b.Action == "createNew" {
fmt.Println("This works")
resp := []byte("And here's our repomse")
conn.Write(resp)
c := NewContainerJSON {
Action: "createdNewContainer",
ContainerName: "Test",
BaseServer: "Test",
CMS: "Test",
WebsiteName: "Test",
DBrootPWD: "Test",
DBadminUname: "Test",
DBadminPWD: "Test",
}
encoder := json.NewEncoder(conn)
if err := encoder.Encode(c); err != nil {
fmt.Println("encode.Encode error: ", err)
}
conn.Write(c)
}
}
func checkError(err error) {
if err != nil {
fmt.Println("An error occurred: ", err.Error())
}
}
I get following error on the line conn.Write(c)
cannot use c (type NewContainerJSON) as type []byte in argument to conn.Write
Two questions:
1: What exactly is this error saying? It seems to be complaining that 'c' cannot be used as a Byte when using the conn.Write function but shouldn't the json.Encoder convert the JSON to a format the conn.Write can use?
2: How exactly can I return a JSON back to the first server using the open connection?
The encoder writes the JSON encoding of c to conn on this line:
if err := encoder.Encode(c); err != nil {
That's all you need to do. Delete the call to conn.Write(c).
The error message is telling you that the value of c cannot be used as the argument to Write because of a type mismatch. A NewContainerJSON is not a []byte.
You first write a string to the connection by
resp := []byte("And here's our repomse")
conn.Write(resp)
This will make it error-prone on the client side. You'll need to read exactly the same amount of data before employ the json decoder on this connection.
If a connection is used for json communication, all the messages on this stream should be json.
So if you want send a message to notify, encode that message too:
encoder.Encode(string(resp))
and need your help.
Wanted to build simple api and stuck with some problem.
I've choose gin and database/sql with postgres driver
package main
import (
"database/sql"
"fmt"
"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
)
func main() {
router := gin.Default()
router.GET("/search/:text", SearchWord)
router.Run(":8080")
}
I need to make query to DB and make json out of this request.
func checkErr(err error) {
if err != nil {
panic(err)
}
}
type Message struct {
ticket_id int `json:"ticket_id"`
event string `json:"event"`
}
func SearchWord(c *gin.Context) {
word := c.Params.ByName("text")
db, err := sql.Open("postgres", "host=8.8.8.8 user= password= dbname=sample")
defer db.Close()
checkErr(err)
rows, err2 := db.Query("SELECT ticket_id,event FROM ....$1, word)
checkErr(err)
for rows.Next() {
var ticket_id int
var event string
err = rows.Scan(&ticket_id, &event)
checkErr(err)
fmt.Printf("%d | %s \n\n", ticket_id, event)
}
}
This coda working nice, but when i need to make json.
I need to make struct of a row
type Message struct {
ticket_id int `json:"ticket_id"`
event string `json:"event"`
}
an then i need to create slice , and append every rows.Next() loop an than answer to browser with Json...
c.JSON(200, messages)
But how to do that...don't know :(
disclaimer: I am brand new to go
Since you Scanned your column data into your variables, you should be able to initialize a structure with their values:
m := &Message{ticket_id: ticket_id, event: event}
You could initialize a slice with
s := make([]*Message, 0)
And then append each of your message structs after instantiation:
s = append(s, m)
Because I'm not too familiar with go there are a couple things i'm not sure about:
after copying data from query to your vars using rows.Scan does initializing the Message struct copy the current iterations values as expected??
If there is a way to get the total number of rows from your query it might be slighlty more performant to initialize a static length array, instead of a slice?
I think #inf deleted answer about marshalling your Message to json down the line might need to be addressed, and Message field's might need to be capitalized
copied from #inf:
The names of the members of your struct need be capitalized so that
they get exported and can be accessed.
type Message struct {
Ticket_id int `json:"ticket_id"`
Event string `json:"event"` }
I'm going to cheat a little here and fix a few things along the way:
First: open your database connection pool once at program start-up (and not on every request).
Second: we'll use sqlx to make it easier to marshal our database rows into our struct.
package main
var db *sqlx.DB
func main() {
var err error
// sqlx.Connect also checks that the connection works.
// sql.Open only "establishes" a pool, but doesn't ping the DB.
db, err = sqlx.Connect("postgres", "postgres:///...")
if err != nil {
log.Fatal(err)
}
router := gin.Default()
router.GET("/search/:text", SearchWord)
router.Run(":8080")
}
// in_another_file.go
type Message struct {
TicketID int `json:"ticket_id" db:"ticket_id"`
Event string `json:"event" db:"event"`
}
func SearchWord(c *gin.Context) {
word := c.Params.ByName("text")
// We create a slice of structs to marshal our rows into
var messages []*Message{}
// Our DB connection pool is safe to use concurrently from here
err := db.Select(&messages, "SELECT ticket_id,event FROM ....$1, word)
if err != nil {
http.Error(c.Writer, err.Error(), 500)
return
}
// Write it out using gin-gonic's JSON writer.
c.JSON(200, messages)
}
I hope that's clear. sqlx also takes care of calling rows.Close() for you, which will otherwise leave connections hanging.
I'm new to Golang and am using the "Server" code here as a starting point: http://www.golang-book.com/13/index.htm#section7
I've attempted to use JSON instead of Gob decoding (since I am required to write the client in C#), and I'm sending the JSON TCP data client data in a separate script from the code below.
I'm stuck on the part where I'm actually receiving the JSON TCP data and storing it in a variable for it to be decoded. It looks like I can decode it with json.Unmarshal, but I can't find any examples where json.Unmarshal is being used to decode TCP data. I can only find examples where json.Unmarshal is being used to decode JSON strings.
My code is below:
package main
import (
"encoding/json"
"fmt"
"net"
)
type coordinate struct {
X float64 `json:"x"`
Y float64 `json:"y"`
Z float64 `json:"z"`
}
func server() {
// listen on a port
ln, err := net.Listen("tcp", ":9999")
if err != nil {
fmt.Println(err)
return
}
for {
// accept a connection
c, err := ln.Accept()
if err != nil {
fmt.Println(err)
continue
}
// handle the connection
go handleServerConnection(c)
}
}
func handleServerConnection(c net.Conn) {
// receive the message
var msg coordinate
Stuck on the line below. What could I set the rawJSON variable equal to?
err := json.Unmarshal([]byte(rawJSON), &msg)
if err != nil {
fmt.Println(err)
} else {
fmt.Println("Received", msg)
}
c.Close()
}
func main() {
go server()
//let the server goroutine run forever
var input string
fmt.Scanln(&input)
}
You can patch a json.Decoder directly to the connection:
func handleServerConnection(c net.Conn) {
// we create a decoder that reads directly from the socket
d := json.NewDecoder(c)
var msg coordinate
err := d.Decode(&msg)
fmt.Println(msg, err)
c.Close()
}