I am trying to learn how JSON-RPC works and am testing it in Go language (golang). The Go program works fine. It does what it should do. But when I try to make a raw request via telnet, it gives an error.
The working and super simple JSON-RPC server is described here:
// rpc_json_server.go
package main
import (
"log"
"net"
"net/http"
"net/rpc"
"net/rpc/jsonrpc"
)
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
type Arithmetic int // Used as RPC Service called 'Arithmetic'
type Arguments struct {
A int
B int
}
type Result int
//------------------------------------------------------------------------------
// Methods
//------------------------------------------------------------------------------
func (t *Arithmetic) Multiply(args *Arguments, res *Result) error {
*res = Result(args.A * args.B)
return nil
}
//------------------------------------------------------------------------------
func main() {
var srv *rpc.Server
var err error
var arith *Arithmetic
var listener net.Listener
var codec rpc.ServerCodec
var srv_conntype, srv_host, srv_port, srv_addr, srv_path string
var srv_debugPath string
var connection net.Conn
srv_conntype = "tcp"
srv_host = "0.0.0.0"
srv_port = "3000"
srv_addr = srv_host + ":" + srv_port
srv_path = "/"
srv_debugPath = "/debug"
// Create Server, register Service
srv = rpc.NewServer()
arith = new(Arithmetic)
err = srv.Register(arith)
if err != nil {
log.Fatalf("Error. Service Format is not correct. %s\r\n", err) //dbg
}
// Handle, listen
srv.HandleHTTP(srv_path, srv_debugPath)
listener, err = net.Listen(srv_conntype, srv_addr)
if err != nil {
log.Fatalf("Error. Can not listen on %s. %s\r\n", srv_addr, err) //dbg
}
log.Printf("Started RPC Handler at %s.\r\n", srv_addr) //dbg
// Serve
for {
connection, err = listener.Accept()
if err != nil {
log.Fatal(err)
}
codec = jsonrpc.NewServerCodec(connection)
go srv.ServeCodec(codec)
}
err = http.Serve(listener, nil)
if err != nil {
log.Fatalf("Serve Error. %s\r\n", err) //dbg
}
}
//------------------------------------------------------------------------------
The working and super simple JSON-RPC client's code is following:
// rpc_json_client.go
package main
import (
"fmt"
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
//------------------------------------------------------------------------------
// Types
//------------------------------------------------------------------------------
type Arithmetic int // Used as RPC Service called 'Arithmetic'
type Arguments struct {
A int
B int
}
type Result int
//------------------------------------------------------------------------------
// Methods
//------------------------------------------------------------------------------
func main() {
var err error
var srv_conntype, srv_host, srv_port, srv_addr string
//var srv_path string
var client *rpc.Client
var args Arguments
var result Result
var serviceName, methodName, funcName string
var connection net.Conn
srv_conntype = "tcp"
srv_host = "0.0.0.0"
srv_port = "3000"
srv_addr = srv_host + ":" + srv_port
//srv_path = "/"
// Connect to RPC Server
connection, err = net.Dial(srv_conntype, srv_addr)
if err != nil {
log.Fatalf("Error. Can not connect to %s. %s\r\n", srv_addr, err) //dbg
}
defer connection.Close()
// Client
client = jsonrpc.NewClient(connection)
// Prepare Call
serviceName = "Arithmetic"
methodName = "Multiply"
funcName = serviceName + "." + methodName
args.A = 7
args.B = 8
// Call remote Procedure
err = client.Call(funcName, args, &result)
if err != nil {
log.Fatalf("Error. %s\r\n", err) //dbg
}
// Show Results
fmt.Printf("[%d; %d] -> [%d].\r\n", args.A, args.B, result) //dbg
}
Once again. This golang program works fine.
But the next thing I cannot understand.
telnet localhost 3000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
{
"jsonrpc":"2.0",
"method":"Arithmetic.Multiply",
"params": { "A": 5, "B": 6 },
"id":1
}
{"id":1,"result":null,"error":"json: cannot unmarshal object into Go value of type [1]interface {}"}
Please, give me some advice or the reason for such an error. What is wrong in the raw request?
Thanks in advance!
Your code seems fine.
But in the request, params is expected to be an array containing the actual parameters.
Try with the following payload and it should work:
{
"jsonrpc":"2.0",
"method":"Arithmetic.Multiply",
"params": [ { "A": 5, "B": 6 } ],
"id":1
}
(Note the "[" and "]" enclosing the actual param)
Related
Hello I'm new to Golang and I'm attempting to do a batch POST with Mux. I want to be able to POST multiple "produce" items instead of just a single one.
Here I'm defining what a produce item is
// Define the produce structure
type Produce struct {
Name string `json:"name"`
Code string `json:"code"`
Unit_Price float64 `json:"unit_price"`
}
// Init produce var as a Produce slice
var produce []Produce
Here is my current POST code
func addProduce(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var newProduceItem Produce
_ = json.NewDecoder(r.Body).Decode(&newProduceItem)
re := regexp.MustCompile("^[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}$")
if re.MatchString(newProduceItem.Code) == true && len(newProduceItem.Name) > 0 {
newProduceItem.Unit_Price = math.Round(newProduceItem.Unit_Price*100) / 100 //rounds to the nearest cent
produce = append(produce, newProduceItem)
json.NewEncoder(w).Encode(newProduceItem)
} else {
http.Error(w, fmt.Sprintf("Incorrect produce code sequence or product name. Example code sequence: A12T-4GH7-QPL9-3N4M"), http.StatusBadRequest)
}
}
It is called in the main() function as seen here.
func main() {
router := mux.NewRouter()
router.HandleFunc("/produce", addProduce).Methods("POST")
log.Fatal(http.ListenAndServe(":8000", router))
}
Here an example of JSON data that is working when I POST to it in Postman
{
"name":"Peach",
"code": "TTTT-44D4-A12T-1224",
"unit_price": 5.3334
}
I want to be able to post multiple produce items at once such as....
[
{
"name": "Green Pepper",
"code": "YRT6-72AS-K736-L4AR",
"unit_price": 0.79
},
{
"name": "Gala Apple",
"code": "TQ4C-VV6T-75ZX-1RMR",
"unit_price": 3.59
},
]
Thank you
There are clearly many ways to go about it, here is one
package main
import (
"encoding/json"
"fmt"
"log"
"math"
"net/http"
"regexp"
"github.com/gorilla/mux"
)
type Produce struct {
Name string `json:"name"`
Code string `json:"code"`
Unit_Price float64 `json:"unit_price"`
}
type ProduceList []Produce
// global var where all produce is kept,
// not persistent
var produce ProduceList
func addProduce(w http.ResponseWriter, r *http.Request) {
// we accept a json and decode it into a slice of structs
var newProduceItems ProduceList
err := json.NewDecoder(r.Body).Decode(&newProduceItems)
if err != nil {
log.Panic(err)
}
var tempItems ProduceList
re := regexp.MustCompile("^[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}$")
// iterate over each element in the posted json and validate
// when validated, add to the temporary accumulator
// if not validated, error out and stop
for idx, produceItem := range newProduceItems {
if !re.MatchString(produceItem.Code) || len(produceItem.Name) <= 0 {
errMsg := fmt.Sprintf("Item %d: Incorrect produce code sequence or product name. Example code sequence: A12T-4GH7-QPL9-3N4M", idx)
http.Error(w, errMsg, http.StatusBadRequest)
return
}
produceItem.Unit_Price = math.Round(produceItem.Unit_Price*100) / 100 //rounds to the nearest cent
tempItems = append(tempItems, produceItem)
}
// after validation, append new items to the global accumulator and respond back with added items
produce = append(produce, tempItems...)
w.Header().Set("Content-Type", "application/json")
if err = json.NewEncoder(w).Encode(newProduceItems); err != nil {
log.Panic(err)
}
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/produce", addProduce).Methods("POST")
log.Fatal(http.ListenAndServe(":8000", router))
}
I am writing a stats in a csv file for incoming diameter traffic in my golang server but the file contain a "" character at the start of rach row.
01.;34642222231118599998;21;6588283272|6588283272|300|0|46692|1582611861|,|2001|01.;34642222231118599998;21;6588283272|gytwocsdr.circles.asia|circles.asia|0|1|1582611861
****01.;34642222231118599998;22;6588080153|6588080153|300|0|46692|1582611861|,|2001|01.;34642222231118599998;22;6588080153|gytwocsdr.circles.asia|circles.asia|0|1|1582611861
****01.;34642222231118599998;23;6587508893|6587508893|300|0|46692|1582611861|,|2001|01.;34642222231118599998;23;6587508893|gytwocsdr.circles.asia|circles.asia|0|1|1582611861
Please guide me on how to fix this.
stats.go>>
package main
import (
"encoding/csv"
"os"
)
var (
filename = "stats.csv"
)
// write the request and response to csv file
func updateCSVFile(req string, resp string) error {
file, err := os.OpenFile("/home/gyuser/charging-control-engine/stats/stats.csv", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return err
}
defer file.Close()
// solve the encoding problem
file.WriteString("\xEF\xBB\xBF")
writer := csv.NewWriter(file)
defer writer.Flush()
return writer.Write([]string{req, resp})
}
ccr.go >>>
package main
import (
"log"
"time"
"github.com/fiorix/go-diameter/v4/diam/sm"
"github.com/fiorix/go-diameter/v4/diam"
"github.com/fiorix/go-diameter/v4/diam/avp"
)
const (
// CCRInitial - ccr request type
CCRInitial = 1
// CCRUpdate - ccr request type
CCRUpdate = 2
// CCRTermination - ccr request type
CCRTermination = 3
// VM - flags VM-
VM = avp.Mbit | avp.Vbit
// M - flags M-
M = avp.Mbit
// TGPP - vendor id for TGPP
TGPP = 10415
)
// Decoder for command requests
type Decoder interface {
Decode() (*diam.Message, error)
WriteCSV() error
}
// CCRGxDecoder try to parse the gx requests with ccr
type CCRGxDecoder struct {
msg *diam.Message
gx *GxStruct
r *CCRGxRequest
q *CCRGxResponse
auth bool
start time.Time
}
// handle the Credit Control Request(CCR)
func handleCCR(s *sm.Settings, conf *Config) diam.HandlerFunc {
return func(c diam.Conn, m *diam.Message) {
var decoder Decoder
// application id for Gx and Gy
//if m.Header.ApplicationID == conf.Gx.ID {
// decoder = NewCCRGxDecoder(m, &conf.Gx, conf.Server.Auth)
//} else
if m.Header.ApplicationID == conf.Gy.ID {
decoder = NewCCRGyDecoder(m, &conf.Gy, conf.Server.Auth)
} else {
log.Printf("invalid application id: %v\n", m.Header.ApplicationID)
return
}
// decode the requests and make the answer message
nm, err := decoder.Decode()
if err != nil {
log.Printf("decode failed: %v, %v\n", err, m)
return
}
// write the message back to the peer
if _, err := nm.WriteTo(c); err != nil {
log.Printf("failed to write message: %v\n", err)
return
}
// update the request and response to csv file
if err := decoder.WriteCSV(); err != nil {
log.Printf("write csv: %v\n", err)
}
}
}
file.WriteString("\xEF\xBB\xBF")
Just remove this line from your code. This is the BOM in UTF-8 encoding, which is exactly the same as the <feff> you see.
I'm learning Go at the moment and trying to make a little SQL-toolset:
type DBUtils struct {
User string
Password string
Host string
Database string
Handle *sql.DB
}
func (dbUtil DBUtils) Connect() {
var err error
dbUtil.Handle, err = sql.Open("mysql", dbUtil.User + ":" + dbUtil.Password + "#tcp(" + dbUtil.Host + ")/" + dbUtil.Database)
if err != nil {
panic(err.Error())
}
err = dbUtil.Handle.Ping()
if err != nil {
panic(err.Error())
}
fmt.Printf("%v", dbUtil)
}
func (dbUtil DBUtils) Close() {
dbUtil.Handle.Close()
}
func (dbUtil DBUtils) GetString(what string, from string, where string, wherevalue string) string {
var username string
fmt.Printf("%v", dbUtil)
stmtOut, err := dbUtil.Handle.Prepare("SELECT " + what + " FROM " + from + " WHERE " + where + " = " + wherevalue)
if err != nil {
panic(err.Error()) // proper error handling instead of panic in your app
}
err = stmtOut.QueryRow(1).Scan(&username)
return username
}
So when using this with the following code:
db := databaseutils.DBUtils{"root", "root", "127.0.0.1:3306", "gotest", nil}
db.Connect() // I get: {root root 127.0.0.1:3306 gotest 0xc42019d600}
fmt.Printf("%v", db) // I get {root root 127.0.0.1:3306 gotest <nil>}
x := db.GetString("username", "users", "id", "1") // Doesn't work: panic: runtime error: invalid memory address or nil pointer dereference
fmt.Println(x)
For me it seems like my DB handle isn't saved properly? Does anyone have an idea - I'm pretty new to go and there are many things looking different to PHP, JS, C++ etc.
Thanks in advance!
Your Connect method is not changing the state of the object you're calling the method on. You're working on a copy of the type. If you want a method to change the object itself, you'll have to define it on a pointer:
func (dbUtil *DBUtils) Connect() {
//instead of
func (dbUtil DBUtils) Connect() {
If you're familiar with C or C++, your current method works similarly to something like:
void connect_db(struct db_utils db_util)
{}
When you call a function like that, you're creating a copy of the argument, and push that on to the stack. The connect_db function will work with that, and after it returns, the copy is deallocated.
compare it to this C code:
struct foo {
int bar;
};
static
void change_copy(struct foo bar)
{
bar.bar *= 2;
}
static
void change_ptr(struct foo *bar)
{
bar->bar *= 2;
}
int main ( void )
{
struct foo bar = {10};
printf("%d\n", bar.bar);//prints 10
change_copy(bar);//pass by value
printf("%d\n", bar.bar);//still prints 10
change_ptr(&bar);
printf("%d\n", bar.bar);//prints 20
return 0;
}
The same thing happens in go. The object on which you define the method can only change state of the instance if it has access to the instance. If not, it can't update that part of the memory.
In case you're wondering, this method doesn't need to be defined on the pointer type:
func (dbUtil DBUtils) Close() {
dbUtil.Handle.Close()
}
The reason for this being that DBUtils.Handle is a pointer type. A copy of that pointer will always point to the same resource.
I will say this, though: given that you're essentially wrapping the handle, and you're exposing the Connect and Close methods, the member itself really shouldn't be exported. I'd change it to lower-case handle
You are right about your DB handle not being saved. You are defining your methods on a value receiver. Therefore you receive a copy inside the Connect method and modify said copy. This copy is then dropped at the end of the Connect method.
You have to define your method on a pointer to your structure:
func (dbUtil *DBUtils) Connect() {
var err error
dbUtil.Handle, err = sql.Open("mysql", dbUtil.User + ":" + dbUtil.Password + "#tcp(" + dbUtil.Host + ")/" + dbUtil.Database)
if err != nil {
panic(err.Error())
}
err = dbUtil.Handle.Ping()
if err != nil {
panic(err.Error())
}
fmt.Printf("%v", dbUtil)
}
For further information see https://golang.org/doc/faq#methods_on_values_or_pointers.
Note: I noticed your usage of QueryRow. It accepts arguments for parameters in your prepare statement. You have no parameters there, so I think you should not pass any parameters. You should also check the Scan result for errors (see https://golang.org/pkg/database/sql/#Stmt).
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()
}
let me start by saying these are my first couple days of toying around in Go.
I'm trying to use the Revel framework with Gorm like this:
app/controllers/gorm.go
package controllers
import (
"fmt"
"go-testapp/app/models"
_ "github.com/go-sql-driver/mysql"
"github.com/jinzhu/gorm"
"github.com/revel/revel"
)
var DB gorm.DB
func InitDB() {
var err error
DB, err = gorm.Open("mysql", "root:#/go-testapp?charset=utf8&parseTime=True")
if err != nil {
panic(err)
}
DB.LogMode(true)
DB.AutoMigrate(models.User{})
}
type GormController struct {
*revel.Controller
DB *gorm.DB
}
app/controller/app.go
package controllers
import (
"fmt"
"go-bingo/app/models"
_ "github.com/go-sql-driver/mysql"
"github.com/revel/revel"
)
type App struct {
GormController
}
func (c App) Index() revel.Result {
user := models.User{Name: "Jinzhu", Age: 18}
fmt.Println(c.DB)
c.DB.NewRecord(user)
c.DB.Create(&user)
return c.RenderJson(user)
}
After running it results in:
runtime error: invalid memory address or nil pointer dereference on line 19 c.DB.NewRecord(user)
It successfully creates the datatables with automigrate, but I have no idea how I should use Gorm in my controller.
Any hints in the right direction?
Important note
it's just replacement for GORP of original example of the Revel. And it comes with some pitfalls of the origin. This answer can be used as drop-in replacement for the original one. But it doesn't solve the pitfalls.
Please, take a look comments of this unswer and #MaxGabriel's answer that solves the pitfalls.
I'd recommend to use #MaxGabriel's solution to protect you application against some kinds of slow-* DDoS attacks. And to reduce (in some cases) DB pressure.
Original answer
#rauyran rights, you have to invoke InitDB inside init function (into controllers package).
Full example here (too much):
Tree
/app
/controllers
app.go
gorm.go
init.go
/models
user.go
[...]
user.go
// models/user.go
package models
import "time" // if you need/want
type User struct { // example user fields
Id int64
Name string
EncryptedPassword []byte
Password string `sql:"-"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt time.Time // for soft delete
}
gorm.go
//controllers/gorm.go
package controllers
import (
"github.com/jinzhu/gorm"
_ "github.com/lib/pq" // my example for postgres
// short name for revel
r "github.com/revel/revel"
// YOUR APP NAME
"yourappname/app/models"
"database/sql"
)
// type: revel controller with `*gorm.DB`
// c.Txn will keep `Gdb *gorm.DB`
type GormController struct {
*r.Controller
Txn *gorm.DB
}
// it can be used for jobs
var Gdb *gorm.DB
// init db
func InitDB() {
var err error
// open db
Gdb, err = gorm.Open("postgres", "user=uname dbname=udbname sslmode=disable password=supersecret")
if err != nil {
r.ERROR.Println("FATAL", err)
panic( err )
}
Gdb.AutoMigrate(&models.User{})
// unique index if need
//Gdb.Model(&models.User{}).AddUniqueIndex("idx_user_name", "name")
}
// transactions
// This method fills the c.Txn before each transaction
func (c *GormController) Begin() r.Result {
txn := Gdb.Begin()
if txn.Error != nil {
panic(txn.Error)
}
c.Txn = txn
return nil
}
// This method clears the c.Txn after each transaction
func (c *GormController) Commit() r.Result {
if c.Txn == nil {
return nil
}
c.Txn.Commit()
if err := c.Txn.Error; err != nil && err != sql.ErrTxDone {
panic(err)
}
c.Txn = nil
return nil
}
// This method clears the c.Txn after each transaction, too
func (c *GormController) Rollback() r.Result {
if c.Txn == nil {
return nil
}
c.Txn.Rollback()
if err := c.Txn.Error; err != nil && err != sql.ErrTxDone {
panic(err)
}
c.Txn = nil
return nil
}
app.go
package controllers
import(
"github.com/revel/revel"
"yourappname/app/models"
)
type App struct {
GormController
}
func (c App) Index() revel.Result {
user := models.User{Name: "Jinzhup"}
c.Txn.NewRecord(user)
c.Txn.Create(&user)
return c.RenderJSON(user)
}
init.go
package controllers
import "github.com/revel/revel"
func init() {
revel.OnAppStart(InitDB) // invoke InitDB function before
revel.InterceptMethod((*GormController).Begin, revel.BEFORE)
revel.InterceptMethod((*GormController).Commit, revel.AFTER)
revel.InterceptMethod((*GormController).Rollback, revel.FINALLY)
}
As you can see, it's like Revel's Booking modified for GORM.
Works fine for me. Result:
{
"Id": 5,
"Name": "Jinzhup",
"EncryptedPassword": null,
"Password": "",
"CreatedAt": "2014-09-22T17:55:14.828661062+04:00",
"UpdatedAt": "2014-09-22T17:55:14.828661062+04:00",
"DeletedAt": "0001-01-01T00:00:00Z"
}
This answer is derived from #IvanBlack's answer, at his suggestion. His version is a direct translation of the Revel example code, but we identified some problems with the original code that this answer fixes. The main changes it makes are:
The entire HTTP request is no longer wrapped by a database transaction. Wrapping the entire HTTP request keeps open the transaction far longer than necessary, which could cause a number of problems:
Your transactions will hold locks on the database, and many transactions holding locks could lead to deadlocks
More database resources are used
These problems are magnified if your HTTP requests aren't mostly limited by your SQL database access. E.g. if your HTTP request blocks for 30 seconds on making an external HTTP request or accessing another database, the slowdown on those services could affect your SQL database.
Because the transaction is no longer automatically being checked for errors at the end of the HTTP request, errors are checked as soon as the database insert is made. This is more correct as well: Imagine that after inserting the User struct into a database, you then stored the User.Id in another database like Redis. This would be fine if the database insert worked, but if it failed and you didn't immediately check the error, you would insert the default int64 value of 0 into Redis (before later rolling back only the SQL transaction).
Tree
/app
/controllers
app.go
gorm.go
init.go
/models
user.go
[...]
user.go
// models/user.go
package models
import "time" // if you need/want
type User struct { // example user fields
Id int64
Name string
EncryptedPassword []byte
Password string `sql:"-"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt time.Time // for soft delete
}
gorm.go
//controllers/gorm.go
package controllers
import (
"github.com/jinzhu/gorm"
_ "github.com/lib/pq" // my example for postgres
// short name for revel
r "github.com/revel/revel"
)
// type: revel controller with `*gorm.DB`
type GormController struct {
*r.Controller
DB *gorm.DB
}
// it can be used for jobs
var Gdb *gorm.DB
// init db
func InitDB() {
var err error
// open db
Gdb, err = gorm.Open("postgres", "user=USERNAME dbname=DBNAME sslmode=disable")
Gdb.LogMode(true) // Print SQL statements
if err != nil {
r.ERROR.Println("FATAL", err)
panic(err)
}
}
func (c *GormController) SetDB() r.Result {
c.DB = Gdb
return nil
}
init.go
package controllers
import "github.com/revel/revel"
func init() {
revel.OnAppStart(InitDB) // invoke InitDB function before
revel.InterceptMethod((*GormController).SetDB, revel.BEFORE)
}
app.go
package controllers
import(
"github.com/revel/revel"
"yourappname/app/models"
)
type App struct {
GormController
}
func (c App) Index() revel.Result {
user := models.User{Name: "Jinzhup"} // Note: In practice you should initialize all struct fields
if err := c.DB.Create(&user).Error; err != nil {
panic(err)
}
return c.RenderJSON(user)
}
Result:
{
"Id": 5,
"Name": "Jinzhup",
"EncryptedPassword": null,
"Password": "",
"CreatedAt": "2014-09-22T17:55:14.828661062+04:00",
"UpdatedAt": "2014-09-22T17:55:14.828661062+04:00",
"DeletedAt": "0001-01-01T00:00:00Z"
}
Your error is caused because you haven't initialised your c.DB database variable, it's still nil.
In your controllers/init.go file make sure you are calling revel.OnAppStart(InitDB). It should look something like this:
package controllers
import "github.com/revel/revel"
func init() {
revel.OnAppStart(InitDB)
// maybe some other init things
}
OR you need to pass in a pointer to AutoMigrate?
DB.AutoMigrate(&models.User{})