MySQL - invalid memory address or nil pointer dereference
10 May 2023在 Go 連線資料庫之後做操作,卻出現以下錯誤:
runtime error: invalid memory address or nil pointer dereference
後來發現是 db 在程式中有重複宣告,所以以後出現這類型錯誤,先檢查物件是否是 nil。
在 Go 連線資料庫之後做操作,卻出現以下錯誤:
runtime error: invalid memory address or nil pointer dereference
後來發現是 db 在程式中有重複宣告,所以以後出現這類型錯誤,先檢查物件是否是 nil。
在 Netlify 部署好 React 網站後,卻發現某些頁面點擊會顯示 Page Not Found. Looks like you’ve followed a broken link 這樣的錯誤。
這是因為 Netlify 不知道 root route 之外的 route 要怎麼去 locate。以下是我的解決方式:
public
資料夾底下建立檔案 _redirects
/* /index.html 200
在執行 source xx.sql
執行 SQL 指令的時候,一直出現以下錯誤:
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '一些SQL指令'
後來發現是 CREATE 指令結束、 INSERT 指令前沒有加上分號……特地打一篇警惕自己!
上次學了怎麼用 Go 和 SQL 交互,接著要利用 gin package 來寫 api!下面範例沒有用到資料庫,只是先用陣列存一些資料做存取與新增。真正用到資料庫的程式碼在文章最下面,是延續使用Go - mysql 學習記錄這篇架的資料庫。
mkdir example && cd example
go mod init example
main.go
touch main.go
main.go
貼上:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
main.go
貼上下面程式碼
// album 結構代表會與之後從資料庫取回的專輯資料互相 match
type album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
// 資料
var albums = []album{
{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99},
{ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99},
{ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99},
}
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
getAlbums
handler handle 到 /albums
path 的 GET 請求:
func main() {
router := gin.Default()
router.GET("/albums", getAlbums)
router.Run("localhost:8080") // start the server
}
go mod tidy
或 go get .
來取得需要的 dependencygo run .
啟動剛剛寫好的 http servercurl
指令和 server 交互:
curl http://localhost:8080/albums
func postAlbums(c *gin.Context) {
var newAlbum album
// 把接收到的 json body 轉換成 struct
if err := c.BindJSON(&newAlbum); err != nil {
return
}
// 添加新專輯到 albums 陣列
albums = append(albums, newAlbum)
c.IndentedJSON(http.StatusCreated, newAlbum)
}
main.go
的 router.GET("/albums", getAlbums)
下加上一行:
router.POST("/albums", postAlbums)
go run .
重新執行 server curl http://localhost:8080/albums \
--include \
--header "Content-Type: application/json" \
--request "POST" \
--data '{"id": "4","title": "The Modern Sound of Betty Carter","artist": "Betty Carter","price": 49.99}'
curl http://localhost:8080/albums \
--header "Content-Type: application/json" \
--request "GET"
func getAlbumByID(c *gin.Context) {
id := c.Param("id") // 從 path 裡面取出 id parameter
// 用迴圈來找這個 id 是對應哪張專輯
for _, a := range albums {
if a.ID == id {
c.IndentedJSON(http.StatusOK, a)
return
}
}
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "album not found"})
}
main.go
的 router.GET("/albums", getAlbums)
下面貼上:
router.GET("/albums/:id", getAlbumByID)
curl
打打看這個 api:
curl http://localhost:8080/albums/2
之前建立的資料庫還在,裡面的資料也和前面的 album 一樣,如果 handler 與 path 都不變,將資料從陣列改成真正的資料庫資料的話,程式如下:
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"os"
"strconv"
"github.com/go-sql-driver/mysql"
"github.com/gin-gonic/gin"
)
var db *sql.DB
// album represents data about a record album.
type Album struct {
ID string `json:"id"`
Title string `json:"title"`
Artist string `json:"artist"`
Price float64 `json:"price"`
}
func main() {
// db config
cfg := mysql.Config{
User: os.Getenv("DBUSER"), //export DBUSER=你的 MySQL 用戶名
Passwd: os.Getenv("DBPASS"), //export DBPASS=你的 MySQL password
Net: "tcp",
Addr: "127.0.0.1:3306",
DBName: "recordings",
}
// 連接資料庫
var err error
db, err = sql.Open("mysql", cfg.FormatDSN())
if err != nil {
log.Fatal(err)
}
pingErr := db.Ping() // 確認是否真的連接上資料庫
if pingErr != nil {
log.Fatal(pingErr)
}
fmt.Println("Connected!")
// router 的部分
router := gin.Default()
router.GET("/albums", getAlbums)
router.POST("/albums", postAlbums)
router.GET("/albums/:id", getAlbumByID)
router.Run("localhost:8080")
}
// 取得所有專輯資料
func getAlbums(c *gin.Context) {
var albums []Album
rows, err := db.Query("SELECT * FROM album")
if err != nil {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "getAlbums error"})
}
defer rows.Close()
for rows.Next() {
var alb Album
if err := rows.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "getAlbums error"})
}
albums = append(albums, alb)
}
if err := rows.Err(); err != nil {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "getAlbums error"})
}
c.IndentedJSON(http.StatusOK, albums)
}
// 新增專輯
func postAlbums(c *gin.Context) {
var newAlbum Album
// 把接收到的 json body 轉換成 struct
if err := c.BindJSON(&newAlbum); err != nil {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "getAlbums error"})
}
result, err := db.Exec("INSERT INTO album (title, artist, price) VALUES (?, ?, ?)", newAlbum.Title, newAlbum.Artist, newAlbum.Price)
if err != nil {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "getAlbums error"})
}
id, err := result.LastInsertId()
if err != nil {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": "getAlbums error"})
}
c.IndentedJSON(http.StatusCreated, "新專輯的 id 是:"+strconv.Itoa(int(id)))
}
// 使用 id 取得特定專輯
func getAlbumByID(c *gin.Context) {
id := c.Param("id")
var alb Album
row := db.QueryRow("SELECT * FROM album WHERE id = ?", id)
if err := row.Scan(&alb.ID, &alb.Title, &alb.Artist, &alb.Price); err != nil {
if err != nil {
c.IndentedJSON(http.StatusNotFound, gin.H{"message": err})
}
}
c.IndentedJSON(http.StatusOK, alb)
}
錯誤處理的部分是亂寫的,統一使用 http.StatusNotFound
敷衍過去,但實際上應該有優雅又精確的處理辦法,之後研究完再來更新這篇!
Tutorial: Developing a RESTful API with Go and Gin Tutorial: Accessing a relational database
方法如下:
<a href="某個網址" target="_blank" rel="noreferrer noopenner">
New link
</a>
根據 google chrome 官方說明:
rel="noopener"
prevents the new page from being able to access the window.opener property and ensures it runs in a separate process.rel="noreferrer"
has the same effect but also prevents the Referer header from being sent to the new page. See Link type “noreferrer”.簡而言之,後面的 rel="noreferrer noopenner"
是安全性考量,避免新開的頁面如果是 malicious page 會影響到原本的頁面。不過新版的瀏覽器目前都默認 target="_blank"
link 使用 rel=noopener
。
Links to cross-origin destinations are unsafe target=”_blank” 的安全性風險