gin はシンプルで高速なgolang
フレームワークです。この記事では主にgin
のルーティング設定と使い方 (主に post メソッド) を紹介します。
golang >= 1.18
# development
go run main.go
# run development
# https://github.com/cosmtrek/air Live reload for Go apps
air
# build
go build
# or
make build
# run production
# export GIN_MODE=release
./gin-router-web
# server 8080
http://localhost:8080/
# file chunk upload
http://localhost:8080/upload_chunks
# docker deploy
make serve
# #
go mod tidy
[ POST ] /api/form_post
[ POST ] /api/json_post
[ POST ] /api/urlencoded_post
[ POST ] /api/json_and_form_post
[ POST ] /api/xml_post
[ POST ] /api/file_upload
[ POST ] /api/file_chunk_upload
[ GET ] /api/query
func setStaticFS ( r * gin. Engine ) {
// set html template
r . LoadHTMLGlob ( "views/*" )
// set server static
r . StaticFile ( "favicon.ico" , "./public/favicon.ico" )
r . StaticFS ( "/static" , http . Dir ( "public/static" ))
r . StaticFS ( "/upload" , http . Dir ( "upload" ))
}
func (engine *Engine) LoadHTMLGlob(pattern string)
関数は、グローバル パターンの HTML ファイル識別子をロードし、結果を HTML レンダラーに関連付けます。
func (group *RouterGroup) StaticFS(relativePath string, fs http.FileSystem) IRoutes
相対パスの静的リソースを設定します
APIルーティンググループ
api := r . Group ( "/api" )
{
api . POST ( "/form_post" , formPost )
api . POST ( "/json_post" , jsonPost )
api . POST ( "/urlencoded_post" , urlencodedPost )
api . POST ( "/json_and_form_post" , jsonAndFormPost )
api . POST ( "/xml_post" , xmlPost )
api . POST ( "/file_upload" , fileUpload )
api . GET ( "/list" , func ( c * gin. Context ) {
name := c . Query ( "name" )
message := c . Query ( "message" )
nick := c . DefaultQuery ( "nick" , "anonymous" )
c . JSON ( http . StatusOK , helper . BuildResponse (gin. H {
"name" : name ,
"message" : message ,
"nick" : nick ,
}))
})
}
リクエストHeaders
で一般的に使用されるContent-Type
タイプには、 text/plain
、 text/html
、 application/json
、 application/x-www-form-urlencoded
、 application/xml
、 multipart/form-data
などが含まれます。
text/plain
プレーンプレーンテキストtext/html
HTMLドキュメントapplication/json
JSON形式のデータapplication/x-www-form-urlencoded
フォームは HTTP POST メソッドを使用して送信されましたapplication/xml
XML形式のデータapplication/form-data
主にファイルのアップロードに使用されますマイム
ジンルーティングの実装
// User user struct
type User struct {
Name string `json:"name" form:"name" xml:"name"`
Message string `json:"message" form:"message" xml:"message"`
Nick string `json:"nick" form:"nick" xml:"nick"`
}
// FormPost 表单提交
func FormPost ( c * gin. Context ) {
message := c . PostForm ( "message" )
nick := c . DefaultPostForm ( "nick" , "default nick" )
name := c . DefaultPostForm ( "name" , "default name" )
user := User {
Name : name ,
Nick : nick ,
Message : message ,
}
// This way is better
// 下面这种方式 会自动和定义的结构体进行绑定
// user := &User{}
// c.ShouldBind(user)
c . JSON ( http . StatusOK , helper . BuildResponse ( user ))
}
html実装
< form method =" post " action =" /api/form_post " id =" form " >
< div class =" form-item " >
< label for =" name " > name </ label >
< input type =" text " id =" name " name =" name " />
</ div >
< div class =" form-item " >
< label for =" message " > message </ label >
< input type =" text " id =" message " name =" message " />
</ div >
< div class =" form-item " >
< label for =" name " > nick </ label >
< input type =" text " id =" nick " name =" nick " />
</ div >
< button type =" submit " >提交</ button >
</ form >
application/json
タイプのデータを送信しますジンルーティングの実装
// JSONPost json
func JSONPost ( c * gin. Context ) {
var user User
if err := c . BindJSON ( & user ); err != nil {
c . AbortWithStatusJSON ( http . StatusOK , helper . BuildErrorResponse ( http . StatusBadRequest , "invalid parameter" ))
return
}
c . JSON ( http . StatusOK , helper . BuildResponse ( user ))
}
js実装
axios ( {
method : "post" ,
url : "/api/json_post" ,
headers : {
"Content-Type" : "application/json" ,
} ,
data ,
} ) . then ( ( res ) => {
console . log ( res . data ) ;
$ ( ".json-msg" ) . text ( `success ${ new Date ( ) } ` ) ;
} ) ;
application/x-www-form-urlencoded
タイプのデータを送信しますジンの実装
// UrlencodedPost application/x-www-form-urlencoded
func UrlencodedPost ( c * gin. Context ) {
limit := c . Query ( "limit" )
name := c . PostForm ( "name" )
message := c . PostForm ( "message" )
nick := c . DefaultPostForm ( "nick" , "1231412" )
user := User {
Name : name ,
Nick : nick ,
Message : message ,
}
// This way is better
// 下面这种方式 会自动和定义的结构体进行绑定
// user := &User{}
// c.ShouldBind(user)
log . Printf ( "request query limit: %s n " , limit )
c . JSON ( http . StatusOK , helper . BuildResponse ( user ))
}
js実装
axios ( {
method : "post" ,
url : "/api/urlencoded_post?name=shineshao" ,
headers : {
"Content-Type" : "application/x-www-form-urlencoded" ,
} ,
data : $ . param ( data ) ,
} ) . then ( ( res ) => {
console . log ( res . data ) ;
$ ( ".urlencoded-msg" ) . text ( `success ${ new Date ( ) } ` ) ;
} ) ;
application/x-www-form-urlencoded
またはapplication/json
タイプのデータを送信しますジン
//JSONAndFormPost application/json application/x-www-form-urlencoded
func JSONAndFormPost ( c * gin. Context ) {
var user User
if err := c . ShouldBind ( & user ); err != nil {
c . AbortWithStatusJSON ( http . StatusOK , helper . BuildErrorResponse ( http . StatusBadRequest , "invalid parameter" ))
return
}
c . JSON ( http . StatusOK , helper . BuildResponse ( user ))
}
js実装
// json
axios ( {
method : "post" ,
url : "/api/json_and_form_post" ,
headers : {
"Content-Type" : "application/json" ,
} ,
data ,
} ) . then ( ( res ) => {
console . log ( res . data ) ;
$ ( ".jsonandform-msg" ) . text ( `success application/json data, ${ new Date ( ) } ` ) ;
} ) ;
// x-www-form-urlencoded
axios ( {
method : "post" ,
url : "/api/json_and_form_post" ,
headers : {
"Content-Type" : "application/x-www-form-urlencoded" ,
} ,
data : $ . param ( data ) ,
} ) . then ( ( res ) => {
console . log ( res . data ) ;
$ ( ".jsonandform-msg" ) . text (
`success application/x-www-form-urlencoded data ${ new Date ( ) } `
) ;
} ) ;
application/xml
タイプのデータ ( application/xml
) を送信しますジンの実装
//XMLPost xml
func XMLPost ( c * gin. Context ) {
var user User
// c.ShouldBind(&user)
// c.Bind(&user)
if err := c . BindXML ( & user ); err != nil {
c . AbortWithStatusJSON ( http . StatusOK , helper . BuildErrorResponse ( http . StatusBadRequest , "invalid parameter" ))
return
}
c . JSON ( http . StatusOK , helper . BuildResponse ( user ))
}
js実装
axios ( {
method : "post" ,
url : "/api/xml_post" ,
headers : {
"Content-Type" : "application/xml" ,
} ,
data : `<xml><name> ${ data . name } </name><message> ${ data . message } </message><nick> ${ data . nick } </nick></xml>` ,
} ) ;
multipart/form-data
タイプのデータ ( multipart/form-data
) を送信しますgin はファイルアップロード (api/upload.go) を実装します。
func fileUpload ( c * gin. Context ) {
filesUrl := make ([] string , 0 )
form , err := c . MultipartForm ()
if err != nil {
log . Println ( "postMultipleFile error: %s" )
}
files := form . File [ "file" ]
_ , err = os . Stat ( "upload" )
if err != nil {
os . Mkdir ( "upload" , os . ModePerm )
}
for _ , file := range files {
log . Println ( file . Filename )
// Upload the file to specific dst.
if err = c . SaveUploadedFile ( file , "upload/" + file . Filename ); err != nil {
log . Println ( "SaveUploadedFile error: %s" )
return
}
filesUrl = append ( filesUrl , "upload/" + file . Filename )
}
c . JSON ( http . StatusOK , models . BuildResponse (gin. H {
"urls" : filesURL ,
}))
}
html実装
< div >
< form id =" multipleForm " >
< input
type =" file "
name =" file "
id =" file "
multiple =" multiple "
accept =" image/* "
/>
</ form >
< button class =" file_upload " >开始上传文件</ button >
</ div >
js実装
// 单个文件上传
// var fd = new FormData()
// var file = document.getElementById('file')
// fd.append('file', file.files[0])
axios ( {
method : "post" ,
url : "/api/file_upload" ,
headers : {
"Content-Type" : "application/form-data" ,
} ,
// data:fd // 单个文件上传
data : new FormData ( $ ( "#multipleForm" ) [ 0 ] ) ,
} ) . then ( ( res ) => {
console . log ( res . data ) ;
const urls = res . data . data . urls || [ ] ;
let imgHtml = "" ;
for ( let i = 0 ; i < urls . length ; i ++ ) {
imgHtml += `<div><img style="width: 200px" src="/ ${ urls [ i ] } " /> <div>/ ${ urls [ i ] } </div></div>` ;
}
$ ( ".file_upload-msg" ) . html (
`<div> ${ new Date ( ) } <div>
${ imgHtml }
</div>
</div>`
) ;
} ) ;
公式ファイルアップロードデモ
クライアントは、ファイル サイズとユーザーが断片化したいサイズに基づいてファイルの断片数を計算します。クライアントは、ファイルのすべてのフラグメントをサーバーにアップロードするようにインターフェースに 1 つずつ要求します。
サーバーは、クライアントによってアップロードされたファイル フラグメントを受け入れ、キャッシュするかファイルを作成して、最後のフラグメントが正常にアップロードされるまでフラグメントを読み取ります。
http://localhost:8080/upload_chunks
サーバー側は go 言語の gin フレームワークを使用します。
type ChunkFile struct {
Name string `json:"name" form:"name"`
Chunk int `json:"chunk" form:"chunk"`
Chunks int `json:"chunks" form:"chunks"`
}
func PathExists ( path string ) ( bool , error ) {
_ , err := os . Stat ( path )
if err == nil {
return true , nil
}
if os . IsNotExist ( err ) {
return false , nil
}
return false , err
}
// 文件分片上传handler
func fileChunkUpload ( c * gin. Context ) {
var chunkFile ChunkFile
r := c . Request
c . Bind ( & chunkFile )
var Buf = make ([] byte , 0 )
// in your case file would be fileupload
file , _ , _ := r . FormFile ( "file" )
log . Println ( "this is " , chunkFile . File )
Buf , _ = ioutil . ReadAll ( file )
filePath := "upload/" + chunkFile . Name
fd , _ := os . OpenFile ( filePath , os . O_RDWR | os . O_CREATE | os . O_APPEND , 0644 )
fd . Write ( Buf )
fd . Close ()
if chunkFile . Chunk + 1 == chunkFile . Chunks {
c . JSON ( http . StatusOK , gin. H {
"state" : "SUCCESS" ,
"url" : "/" + filePath ,
})
} else {
contentType := strings . Split ( c . GetHeader ( "Content-Type" ), "boundary=" )
c . String ( http . StatusOK , contentType [ 1 ])
}
}
サーバーインターフェースの完全なコード
クライアントは plupload ファイル アップロード プラグインを使用します。利点は、オブジェクトの作成時にchunk_size
属性を構成することでマルチパート アップロードを実現できることです (プラグインの最下位レイヤーがフラグメントの数を計算します)。ファイル サイズとchunk_size
に基づきます)。
var uploader = new plupload . Uploader ( {
runtimes : "html5,flash,silverlight,html4" ,
browse_button : "pickfiles" , // you can pass an id...
container : document . getElementById ( "container" ) , // ... or DOM Element itself
url : "/api/file_chunk_upload" ,
flash_swf_url : "/static/js/Moxie.swf" ,
silverlight_xap_url : "/static/js/Moxie.xap" ,
chunk_size : "100kb" ,
filters : {
max_file_size : "10mb" ,
mime_types : [
{ title : "Image files" , extensions : "jpg,gif,png,jpeg" } ,
{ title : "Zip files" , extensions : "zip" } ,
] ,
} ,
init : {
PostInit : function ( ) {
document . getElementById ( "filelist" ) . innerHTML = "" ;
document . getElementById ( "uploadfiles" ) . onclick = function ( ) {
uploader . start ( ) ;
return false ;
} ;
} ,
FilesAdded : function ( up , files ) {
plupload . each ( files , function ( file ) {
document . getElementById ( "filelist" ) . innerHTML +=
'<div id="' +
file . id +
'">' +
file . name +
" (" +
plupload . formatSize ( file . size ) +
") <b></b></div>" ;
} ) ;
} ,
UploadProgress : function ( up , file ) {
document . getElementById ( file . id ) . getElementsByTagName ( "b" ) [ 0 ] . innerHTML =
"<span>" + file . percent + "%</span>" ;
} ,
Error : function ( up , err ) {
document
. getElementById ( "console" )
. appendChild (
document . createTextNode ( "nError #" + err . code + ": " + err . message )
) ;
} ,
} ,
} ) ;
uploader . bind ( "ChunkUploaded" , function ( up , file , info ) {
// do some chunk related stuff
console . log ( info ) ;
} ) ;
uploader . init ( ) ;
クライアント ファイル アップロードの完全なコード
デモ