Ginのリクエストボディはストリーム,,,,,らしい

Ginで受け取れるリクエストボディはストリームなので、一度Readメソッドなどで読み出してしまうと空っぽになってしまう。従って、BindJSONなどのメソッドを読み出した後に使用するとEOFエラーが発生することがある。たとえば以下。

 
package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.New()

    router.POST("hoge", func(c *gin.Context) {
        buf := make([]byte, 2048)
        n, _ := c.Request.Body.Read(buf) // ここでRequest.Bodyを読み切ってしまっている
        b := string(buf[0:n])
        fmt.Println(b)

        var params RequestParams

        // BindJSONは内部でRequest.Bodyを再び読み出そうとするが
        // Request.Bodyはすでに読み出されて空になっているため、ここでEOFエラーになる
        if err := c.BindJSON(&params); err != nil {
            uc.Warningf("Failed binding request parameters: %s", err.Error())
            c.Status(http.StatusBadRequest)
            return
        }

        c.JSON(200, params)
    })

    router.Run()
}

対策としては、ioutil.NopCloserメソッドを使って読み出してしまったリクエストボディを書き戻すなど。

 
 buf := make([]byte, 2048)
 n, _ := c.Request.Body.Read(buf)
 b := string(buf[0:n])
 c.Request.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(s)))
 fmt.Println(b)

stackoverflow.com

 

You can only read the Body from the client once,The data is streaming from the user, and they're not going to send it again

とのことなのでデータはそのまま流れていってしまうそうです

 

PHP foreachで多重連想配列の時に、最初の列だけスキップ

たまにしか使わないんだけど、調べる時間がもったいないのでここに記載 csvでヘッダーを無視したい時に使うかなあ、、?

    foreach ($data as $key => $val)
    {
        if ($key === array_key_first($data))
        { 
            echo '最初の列ですー';
        }
    }

因みにarray_key_lastってのもある。

    foreach ($data as $key => $val)
    {
        if ($key === array_key_last($data))
        { 
            echo '最後の列ですー';
        }
    }

さようならSequel Pro

独学当初からずっとお世話になっていたSequel Pro

仕事ではCUIでコマンド叩いてますが、個人開発でやる分には便利だしずっと使ってました

最近ローカルのMysqlを8にアップデートしたらSequel Proが使えなくなった

まあいいやと個人開発も直接叩いて確認してたがやっぱめんどくさい

調べるとversion8には対応してないしそ予定もなさそうなのね、、、

ということで代替えのがないのか探してたらあったSequel Ace

GitHub - Sequel-Ace/Sequel-Ace: MySQL/MariaDB database management for macOS

これからはこれを使おう

 

go gin multipart 画像保存

公式通りに進めていけばそんなに難しくなかったけど、忘れないうちにアウトプット

今回はreactでaxios使用してサーバー側に送りました。 商品を名前や詳細と同時に画像を保存する処理です。(validationとかはまだ) ginにはSaveUploadFileという関数が用意されています。引数にそのファイルとディレクトリを指定あげればapp内に保存されます。

ItemController

func Store(c *gin.Context) {
    var item domain.Item
    err := c.Bind(&item)
        if err != nil {
            c.String(http.StatusBadRequest, "Request is failed: "+err.Error())
        return
    }
        //商品画像とリレーション紐づけるのでidの返り値を貰う。
    id := repository.SaveItem(&item)

        //同じファイル名が重複しないようにランダム値に変更
    file, _ := c.FormFile("image")
    fileName := filepath.Ext(file.Filename)
        newFileName := uuid.New().String() + fileName
    filePath := "./public/images/"

  //ここはいらないかも
    if f, exist := os.Stat(filePath); os.IsNotExist(exist) || 
    !f.IsDir() {
        c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
                    "message": "error. could not find dir",
                })
        }

        //ここでディレクトリに保存、公式参照
    er := c.SaveUploadedFile(file, filePath + newFileName)
    if er != nil {
        c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
                "message": "Unable to save the file",
        })
        return
  }
  
    //ファイル情報をDBに保存
    repository.SaveItemImage(newFileName, id)

    c.JSON(http.StatusCreated, gin.H{
        "name": item.Name,
        "detail": item.Detail,
        })
}

ItemImageRepository

//ここは結構シンプルです
func SaveItemImage(fileName string, id uint) {
    fmt.Println()
        filePath := "/public/images/"
    db := database.GormConnect()

    var data domain.ItemImage

    data.ItemId = id
    data.File_name = fileName
    data.Path = filePath + fileName

    now := time.Now()
        data.CreatedAt = now
        data.UpdatedAt = now  
    db.Create(&data)
}

とりあえず動くことは動きます。 まだまだこれで運用には程遠いと思いますので、この後は例外処理とか入れてハンドリング予定

goで論理削除するためにdeleted_atにデフォルト値設定のやり方

構造体のところでdefaultを設定すれば良い

type Item struct {

  Deleted_at time.Timte `json:"deleted_at" gorm:"default:'null'"`
}

こうすれば初期値にnullとされ、削除の判定時は現在日付を入れて判定すれば良い。

けどこれlaravelとかやってるからこの方法をとっているので gorm等の作法にあっってるかはよくわかってない、、、

Material-ui cant resolve iconsの呪い

テンプレートのダッシュボードを使おうとコピーしたのは良いものの、

Can't resolve '@mui/icons-material/SettingsEthernet'とエラーが出た。

はいはいアイコンのモジュールがないからinstallねと行ったのだが、ずっと同じエラー、、

どこも同じ解決策しか書いてないしimportもできてるはずとハマること小一時間

@mui/icons-material/SettingsEthernet

じゃなくて

@material-ui/icons/SettingsEthernet

こうなのね、、、

公式通りに沿ってのエラーだから他にもハマる人いるのではないでしょうか、、?汗

記述の違いはバージョンによるものなのかは、そこまで調べてないです。