I have a back-end app and a front-end, both in Go. I am trying to make them talk via rpc.
back-end main.go
package main
import (
"fmt"
"log"
"net"
"net/http"
"net/rpc"
"time"
)
type Application struct {
config struct {
server struct {
port int
network string
}
}
}
type MusicProject struct {
Id string
CreatedTime time.Time
LastEditedTime time.Time
Title string
Year int
Status string
Description string
ChoirRollup string
}
func main() {
var app Application
app.config.server.port = 5002
app.config.server.network = "tcp"
if err := rpc.Register(new(Application)); err != nil {
log.Fatal(err)
}
rpc.HandleHTTP()
// start the rpc server
log.Println("Starting server port", app.config.server.port)
l, err := net.Listen("tcp", fmt.Sprintf(":%v", app.config.server.port))
if err != nil {
log.Fatal(err)
}
if err = http.Serve(l, nil); err != nil {
log.Fatal(err)
}
}
func (app *Application) GetMusicProjectById(id string, model *MusicProject) error {
musicProject := MusicProject{
Id: id,
CreatedTime: time.Now(),
LastEditedTime: time.Now(),
Title: "Test Project",
Year: 2020,
Status: "Completed",
Description: "Short project Description",
ChoirRollup: "Best Choir",
}
model = &musicProject
return nil
}
front-end rpc call
package main
import (
"log"
"net/rpc"
"time"
)
type MusicProject struct {
Id string
CreatedTime time.Time
LastEditedTime time.Time
Title string
Year int
Status string
Description string
ChoirRollup string
}
func main() {
dial, err := rpc.Dial("tcp", "localhost:5002")
if err != nil {
log.Println(err)
}
projectID := "some_id_123"
var musicProject MusicProject
err = dial.Call("Application.GetMusicProjectById", projectID, &musicProject)
if err != nil {
log.Println(err)
}
log.Println(musicProject)
}
Once the client method is call, the call hanged indefinite with out sending back any error, so quite difficult to debug.
Do you have any suggestion for debugging? Do you see anything wrong in my code?
Thank you for your help!
Your server is serving HTTP:
if err = http.Serve(l, nil); err != nil {
log.Fatal(err)
}
But your client is using straight TCP (HTTP runs over TCP but adds another layer):
err = dial.Call("Application.GetMusicProjectById", projectID, &musicProject)
So to fix your issue you need to change one side or the other. For example change the server to:
rpc.Accept(l)
(alternatively you could use rpc.DialHTTP in the client)
There are a couple of other issues with your code (the main one being the way you set the response in the back-end). The following works for me (have not put much effort into making it tidy or testing!).
server
package main
import (
"fmt"
"log"
"net"
"net/rpc"
"time"
)
type MusicProject struct {
Id string
CreatedTime time.Time
LastEditedTime time.Time
Title string
Year int
Status string
Description string
ChoirRollup string
}
type Application struct {
}
func (app *Application) GetMusicProjectById(id string, model *MusicProject) error {
*model = MusicProject{
Id: id,
CreatedTime: time.Now(),
LastEditedTime: time.Now(),
Title: "Test Project",
Year: 2020,
Status: "Completed",
Description: "Short project Description",
ChoirRollup: "Best Choir",
}
return nil
}
func main() {
const port = 5002
if err := rpc.Register(new(Application)); err != nil {
log.Fatal(err)
}
// start the rpc server
log.Println("Starting server on port:", port)
l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
log.Fatal(err)
}
rpc.Accept(l)
}
client
package main
import (
"log"
"net/rpc"
"time"
)
type MusicProject struct {
Id string
CreatedTime time.Time
LastEditedTime time.Time
Title string
Year int
Status string
Description string
ChoirRollup string
}
func main() {
dial, err := rpc.Dial("tcp", "localhost:5002")
if err != nil {
log.Fatal(err)
}
projectID := "some_id_123"
var musicProject MusicProject
err = dial.Call("Application.GetMusicProjectById", projectID, &musicProject)
if err != nil {
log.Fatal("error:", err)
}
log.Println(musicProject)
}
Related
I want to return the string returned from this link, and build a JSON object based on the returned data, I wrote the below code that is reading the url and getting the string correctly, but it fails in building the JSON object, and do not return anything!
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
type Product struct {
ProductId string `json:"id"`
ProductName string `json:"description"`
}
func main() {
url := "https://script.googleusercontent.com/macros/echo?user_content_key=WBSJPDNSN6X1FCYeXsR6TDaDval0vdvmSoMmXFhGbt5sfK0ia80Dp7kPD27GLpZbYz8vrwfDiUecI2oGMjEtgfL5o8Da25T1m5_BxDlH2jW0nuo2oDemN9CCS2h10ox_1xSncGQajx_ryfhECjZEnGb6k9xaGtOX6M1tIiG811CRpk9nXl8ZKS7UJTno1dvQXMe1kqfAj8WxsSkLor-EqzOmbnRGq-tk&lib=M0B6GXYh0EOYMkP7qr1Xy9xw8GuJxFqGH"
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
htmlData, err := ioutil.ReadAll(resp.Body) //<--- here!
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// print out
fmt.Println(string(htmlData))
var product Product
json.Unmarshal([]byte(string(htmlData)), &product)
fmt.Printf("ID: %s, Description: %s", product.ProductId, product.ProductName)
}
Output:
{"user":[{"ProductId":1,"ProductName":"Helmet"},{"ProductId":2,"ProductName":"Glove"},{"ProductId":3,"ProductName":"Detecttor"}]}
ID: , Description:
1- The unmarshal target struct must match the data,
type Data struct {
User []Product `json:"user"`
}
2- The fields types should be matching, so ProductID should be uint
3- The json OP got in output should match the tags used, json:"id" should be replaced by json:"ProductId" same for description
type Product struct {
ProductId uint `json:"ProductId"`
ProductName string `json:"ProductName"`
}
So, a working code is:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
type Data struct {
User []Product `json:"user"`
}
type Product struct {
ProductId uint `json:"ProductId"`
ProductName string `json:"ProductName"`
}
func main() {
url := "https://script.googleusercontent.com/macros/echo?user_content_key=WBSJPDNSN6X1FCYeXsR6TDaDval0vdvmSoMmXFhGbt5sfK0ia80Dp7kPD27GLpZbYz8vrwfDiUecI2oGMjEtgfL5o8Da25T1m5_BxDlH2jW0nuo2oDemN9CCS2h10ox_1xSncGQajx_ryfhECjZEnGb6k9xaGtOX6M1tIiG811CRpk9nXl8ZKS7UJTno1dvQXMe1kqfAj8WxsSkLor-EqzOmbnRGq-tk&lib=M0B6GXYh0EOYMkP7qr1Xy9xw8GuJxFqGH"
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
htmlData, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// print out
fmt.Println(string(htmlData))
//var product Product
var data Data
json.Unmarshal([]byte(string(htmlData)), &data)
fmt.Println(data)
fmt.Println(data.User)
fmt.Println(data.User[0])
fmt.Printf("id: %v, description: %s", data.User[0].ProductId, data.User[0].ProductName)
}
I am using below code for an API to find ip address for a given domain:
func IPFinder(c *gin.Context) {
var domain models.Domain
c.BindJSON(&domain)
addr, err := net.LookupIP(domain.DomainName)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} else {
c.JSON(http.StatusOK, gin.H{"ip_address": addr})
}
return
}
For the below request:
{
"domain_name": "google.com"
}
getting response as :
{
"ip_address": [
"2404:6800:4002:80a::200e",
"172.217.167.46"
]
}
Here this LookupIP method is giving the slice containing ipv4 and ipv6 address of that domain. Is there any other way of any other 3rd party lib in GoLang using which I can get output containing only the ip address like below :
{
"ip_address": "172.217.167.46"
}
If you're only interested in IPv4 addresses, you can get it like this:
package main
import (
"fmt"
"net"
)
func main(){
ips, _ := net.LookupIP("google.com")
for _, ip := range ips {
if ipv4 := ip.To4(); ipv4 != nil {
fmt.Println("IPv4: ", ipv4)
}
}
}
You could use DNS-over-HTTPS service from cloudflare-dns.com, see here.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
)
type DnsJson struct {
Status int `json:"Status"`
TC bool `json:"TC"`
RD bool `json:"RD"`
RA bool `json:"RA"`
AD bool `json:"AD"`
CD bool `json:"CD"`
Question []struct {
Name string `json:"name"`
Type int `json:"type"`
} `json:"Question"`
Answer []struct {
Name string `json:"name"`
Type int `json:"type"`
TTL int `json:"TTL"`
Data string `json:"data"`
} `json:"Answer"`
}
func main() {
req, err := http.NewRequest("GET", "https://cloudflare-dns.com/dns-query", nil)
if err != nil {
log.Fatal(err)
}
q := req.URL.Query()
q.Add("ct", "application/dns-json")
q.Add("name", "cloudflare.com") // Insert here your domain
req.URL.RawQuery = q.Encode()
client := &http.Client{}
if err != nil {
log.Fatal(err)
}
res, err := client.Do(req)
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var dnsResult DnsJson
if err := json.Unmarshal(body, &dnsResult); err != nil {
log.Fatal(err)
}
for _, dnsAnswer := range dnsResult.Answer {
fmt.Println("IP:", dnsAnswer.Data)
}
// Output
// IP: 104.17.175.85
// IP: 104.17.176.85
}
I am trying to create an API with Go, gorm and gin. However, using a direct SQL query did work, but it became too cumbersome and I decided to leverage gorm which is fantastic. However after my implementation, I am unable to get the project up again and keep getting error
panic: runtime error: invalid memory address or nil pointer
dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0xb0
pc=0x137b606]
I have pasted my code with packages here. I would be glad if someone could point me to the issue.
PostgresDoa.go
package postgres
import (
"fmt"
"log"
"net/http"
"github.com/AdieOlami/bookstore_users-api/domain/users"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
type Sever struct {
DB *gorm.DB
Router *gin.Engine
}
func (server *Sever) Initialize(Dbdriver, DbUser, DbPassword, DbPort, DbHost, DbName string) {
var err error
if Dbdriver == "mysql" {
DBURL := fmt.Sprintf("%s:%s#tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local", DbUser, DbPassword, DbHost, DbPort, DbName)
server.DB, err = gorm.Open(Dbdriver, DBURL)
if err != nil {
fmt.Printf("Cannot connect to %s database", Dbdriver)
log.Fatal("This is the error:", err)
} else {
fmt.Printf("We are connected to the %s database", Dbdriver)
}
}
if Dbdriver == "postgres" {
DBURL := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=disable password=%s", DbHost, DbPort, DbUser, DbName, DbPassword)
server.DB, err = gorm.Open(Dbdriver, DBURL)
if err != nil {
fmt.Printf("Cannot connect to %s database", Dbdriver)
log.Fatal("This is the error:", err)
} else {
fmt.Printf("We are connected to the %s database", Dbdriver)
}
}
server.DB.Debug().AutoMigrate(&users.User{}) //database migration
server.Router = gin.Default()
}
func (server *Sever) Run(addr string) {
fmt.Println("Listening to port 8080")
log.Fatal(http.ListenAndServe(addr, server.Router))
}
UserDto.go
package users
import (
"strings"
"github.com/AdieOlami/bookstore_users-api/domain/base"
"github.com/AdieOlami/bookstore_users-api/utils/errors"
"github.com/jinzhu/gorm"
uuid "github.com/satori/go.uuid"
)
type User struct {
base.Base
UserID uuid.UUID `gorm:"type:uuid;column:userId;not null;" json:"userId"`
FirstName string `gorm:"size:255;not null;unique;column:firstName" json:"firstName"`
LastName string `gorm:"size:255;not null;unique;column:lastName" json:"lastName"`
Email string `gorm:"size:100;not null;unique;column:email" json:"email"`
}
func (user *User) Validate() *errors.Error {
user.Email = strings.TrimSpace(strings.ToLower(user.Email))
if user.Email == "" {
return errors.NewBadRequestError("invalid email address")
}
return nil
}
func (user *User) SaveUser(db *gorm.DB) *errors.Error {
var err error
err = db.Debug().Create(&user).Error
if err != nil {
return errors.NewInteralServerError(err.Error())
}
return nil
}
UserService.go
package services
import (
"github.com/AdieOlami/bookstore_users-api/domain/users"
"github.com/AdieOlami/bookstore_users-api/utils/errors"
)
func (server *Server) CreateUser(user users.User) (*users.User, *errors.Error) {
if err := user.Validate(); err != nil {
return nil, err
}
if err := user.SaveUser(server.DB); err != nil {
return nil, err
}
return &user, nil
}
UserController.go
package users
import (
"net/http"
"strconv"
"github.com/AdieOlami/bookstore_users-api/domain/users"
"github.com/AdieOlami/bookstore_users-api/services"
"github.com/AdieOlami/bookstore_users-api/utils/errors"
"github.com/gin-gonic/gin"
)
var (
server = services.Server{}
)
// CREATE USER
func CreateUser(c *gin.Context) {
var user users.User
if err := c.ShouldBindJSON(&user); err != nil {
// TODO: hnadle json error return bad request
err := errors.NewBadRequestError("invalid json body")
c.JSON(err.Status, err)
// fmt.Println(err.Error())
return
}
result, saveErr := server.CreateUser(user)
if saveErr != nil {
// TODO: handle user createing error
c.JSON(saveErr.Status, saveErr)
return
}
c.JSON(http.StatusCreated, result)
}
Routes.go
package app
import (
"github.com/AdieOlami/bookstore_users-api/controllers/users"
"github.com/AdieOlami/bookstore_users-api/database/postgres"
)
var (
server = postgres.Sever{}
)
func initializeRoutes() {
server.Router.POST("/users", users.CreateUser)
}
Application.go
package app
import (
"os"
"github.com/AdieOlami/bookstore_users-api/seed"
_ "github.com/jinzhu/gorm/dialects/mysql" //mysql database driver
_ "github.com/jinzhu/gorm/dialects/postgres" //postgres database driver
)
func StartApplication() {
server.Initialize(os.Getenv("DB_DRIVER"), os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_PORT"), os.Getenv("DB_HOST"), os.Getenv("DB_NAME"))
seed.Load(server.DB)
server.Run(":8088")
initializeRoutes()
}
in my Main.go
func main() {
app.StartApplication()
}
As far as I can understand from your code. You declared below function in Services package whereas Server object is declared in the Postgres package. This causes dereferencing of pointer server *Server to an invalid address. you have to declare this function in posgres package.
func (server *Server) CreateUser(user users.User) (*users.User, *errors.Error) {
if err := user.Validate(); err != nil {
return nil, err
}
if err := user.SaveUser(server.DB); err != nil {
return nil, err
}
return &user, nil
}
I have this JSON API that returns the delivery date as a UNIX timestamp. I would prefer using Time in the rest of the application. The following works but doesn't feel very Go like.
type Delivery struct {
Time string `json:"time"`
}
func timestampToTime(s string) time.Time {
i, _ := strconv.ParseInt(s, 10, 64)
returns time.Unix(i, 0)
}
fmt.Println(timestampToTime(Delivery.Time))
// 2019-02-17 11:55:00 +0100 CET
Is there a way to cast an incoming value in a struct?
You can do something very similar to the custom JSON Unmarshal method described here:
http://choly.ca/post/go-json-marshalling/
Assuming that the JSON contains a string in your case this would look like this:
package main
import (
"encoding/json"
"fmt"
"os"
"strconv"
"time"
)
const input = `{"time": "946684799"}`
type Delivery struct {
Time time.Time `json:"time"`
}
func (d *Delivery) UnmarshalJSON(data []byte) error {
type Alias Delivery
aux := &struct {
Time string `json:"time"`
*Alias
}{
Alias: (*Alias)(d),
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
i, err := strconv.ParseInt(aux.Time, 10, 64)
if err != nil {
return err
}
d.Time = time.Unix(i, 0)
return nil
}
func main() {
var delivery Delivery
err := json.Unmarshal([]byte(input), &delivery)
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
fmt.Println(delivery.Time)
}
https://play.golang.org/p/mdOmUO2EDIR
My question is similar to How to create singleton DB class in GoLang but I'm having trouble getting it to work with separate models and services packages.
project/lib/database/mysql.go:
package database
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
)
type Manager struct {
*gorm.DB
}
var Mgr *Manager
func init() {
dsn := MysqlConnectionString("parseTime=true")
tablePrefix := "demo"
var err error
gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
return fmt.Sprintf("%v_%v", tablePrefix, defaultTableName)
}
db, err := gorm.Open("mysql", dsn)
if err != nil {
panic(err)
}
Mgr = &Manager{db}
}
project/lib/models/retailer_keys.go
package models
import (
"fmt"
"project/lib/database"
"time"
)
type RetailerKeysInterface interface {
RetailerKeys() ([]*RetailerKey, error)
}
type DB struct {
database.Manager
}
type RetailerKey struct {
Id int `json:"id"`
RetailerId int `json:"retailer_id"`
Key string `json:"key"`
Enabled *bool `json:"enabled"`
CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"`
}
func (db *DB) RetailerKeys() ([]*RetailerKey, error) {
var keys []*RetailerKey
if err := db.Find(&keys).Error; err != nil {
return nil, err
}
return keys, nil
}
project/lib/services/retailer_keys.go
import (
"fmt"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/apigateway"
"gopkg.in/volatiletech/null.v6"
"project/lib/models"
"project/lib/services/api_keys"
)
func GetKeys() ([]*models.RetailerKey, error) {
var q models.RetailerKeysInterface
keys, err := q.RetailerKeys()
if err != nil {
return nil, err
}
return keys, nil
}
func CreateKey(id int) (models.RetailerKey, error) {
...
}
Then be able to use it in my main package like:
package main
import (
"context"
"encoding/json"
// "reflect"
"fmt"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
_ "project/lib/config"
"project/lib/services"
)
func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
statusCode := 200
keys, err := services.GetKeys()
if err != nil {
statusCode = 400
}
body, _ := json.Marshal(keys)
return events.APIGatewayProxyResponse{
Body: string(body),
StatusCode: statusCode,
}, nil
}
...
I'd like to be able to embed the relevant subset of the Manager type in my models.
EDIT:
Edited the question/code based on feedback in comments.
This gives me an error: runtime error: invalid memory address or nil pointer dereference.
I was definitely misunderstanding interfaces in go. After going through A Tour of Go it started to become clearer how it all fits together.
This is that I ended up doing for anyone that is going through the same thing. I'll leave the original question up so you can see the differences.
project/lib/database/mysql.go:
package database
import (
"fmt"
"log"
_ "github.com/go-sql-driver/mysql" // Needed for gorm
"github.com/jinzhu/gorm"
)
var Manager *gorm.DB
func init() {
var err error
dsn := MysqlConnectionString("parseTime=true")
tablePrefix := "qcommerce"
gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
return fmt.Sprintf("%v_%v", tablePrefix, defaultTableName)
}
Manager, err = gorm.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
if err := Manager.DB().Ping(); err != nil {
log.Fatal(err)
}
}
project/lib/models/retailer_keys.go
package models
import (
"project/lib/database"
"time"
)
type QRetailerKeys interface {
Insert() error
Find() error
}
type RetailerKey struct {
ID int `json:"id"`
RetailerID int `json:"retailer_id"`
Retailer Retailer `json:"retailer"`
Key string `json:"key"`
Enabled bool `json:"enabled" gorm:"DEFAULT:true"`
CreatedAt *time.Time `json:"created_at"`
UpdatedAt *time.Time `json:"updated_at"`
}
// RetailerKeys returns a slice of all keys in table
func RetailerKeys() ([]*RetailerKey, error) {
var keys []*RetailerKey
if err := database.Manager.Find(&keys).Error; err != nil {
return nil, err
}
return keys, nil
}
func (r *RetailerKey) Find() error {
...
}
// Create a new key
func (r *RetailerKey) Create() error {
return database.Manager.Create(&r).Error
}
project/lib/services/retailer_keys.go
package services
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
// "github.com/aws/aws-sdk-go/service/apigateway"
"partners.drinks.com/lib/models"
"partners.drinks.com/lib/services/api_keys"
)
func sessionBuilder() *session.Session {
config := &aws.Config{
Region: aws.String("us-west-2"),
}
session := session.Must(session.NewSession(config))
return session
}
func GetKeys() ([]*models.RetailerKey, error) {
keys, err := models.RetailerKeys()
if err != nil {
return nil, err
}
return keys, nil
}
func CreateKey(id int) (models.RetailerKey, error) {
apikeys := &api_keys.ApiKeyBuilder{}
base64Key := apikeys.GenUUID().GenKey().Base64
var key = models.RetailerKey{
RetailerID: id,
Key: base64Key,
Enabled: func(b bool)
}
if err := key.Create(); err != nil {
return models.RetailerKey{}, err
}
...
return key, nil
}
I use it like:
package main
import (
"context"
"encoding/json"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
_ "partners.drinks.com/lib/config"
"partners.drinks.com/lib/services"
)
func Handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
statusCode := 200
keys, err := services.GetKeys()
if err != nil {
statusCode = 400
}
body, _ := json.Marshal(keys)
return events.APIGatewayProxyResponse{
Body: string(body),
StatusCode: statusCode,
}, nil
}
...
Thanks to #mkopriva for the linked resources in the comments.