よくあるWebサービスのAPIを作ったらいろいろ勉強になった話/WebApplicationAPITips

Cf7c364c6463eb8a1f1bb5c4a2e583a6?s=47 decafe09
October 05, 2018

 よくあるWebサービスのAPIを作ったらいろいろ勉強になった話/WebApplicationAPITips

Cf7c364c6463eb8a1f1bb5c4a2e583a6?s=128

decafe09

October 05, 2018
Tweet

Transcript

  1. よくあるWeb サー ビスのAPI を作ったらいろいろ勉強になった話 Umeda.go 2018 Autumn 2018.10.03 1

  2. 自己紹介 @decafe09 小野寺 俊也 シナジー マー ケティング株式会社 2

  3. 今日の話の背景 メー ル広告の入稿ツー ル SI 開発をしている部署がRails で開発 Rails デキない人( 私)

    がいざ運用をしようとするといろいろ躓く 躓きながらも運用をしてビジネスが回るようになってくる → 次のステップへ 3
  4. 次の選択肢 Rails ・ 精通しているメンバー がいない React + Go ・Go を使いたいという強い気持ち

    ・Go のテンプレー トエンジンを使ってみたが厳しい → Go はAPI で。 4
  5. 入稿物 件名 本文( テキスト) 本文(HTML) 画像(HTML で使用する画像) 5

  6. RESTish なAPI G E T / c o n t

    e n t s { " c o n t e n t s " : [ { " i d " : 1 , " s u b j e c t " : " x x x " , " t e x t " : " x x x " , " h t m l " : " x x x " , } , . . . ] } P O S T / c o n t e n t s { " s u b j e c t " : " x x x " , " t e x t " : " x x x " , " h t m l " : " x x x " , } 6
  7. 画像とかファイルはどうやって送られているのだろう? Content‑Type: multipart/form‑data MDN のリクエスト例を参考に見てみます。 https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content‑Disposition 7

  8. P O S T / t e s t .

    h t m l H T T P / 1 . 1 H o s t : e x a m p l e . o r g C o n t e n t - T y p e : m u l t i p a r t / f o r m - d a t a ; b o u n d a r y = " b o u n d a r y " - - b o u n d a r y C o n t e n t - D i s p o s i t i o n : f o r m - d a t a ; n a m e = " f i e l d 1 " v a l u e 1 - - b o u n d a r y C o n t e n t - D i s p o s i t i o n : f o r m - d a t a ; n a m e = " f i e l d 2 " ; f i l e n a m e = " e x a m p l e . t x t " v a l u e 2 - - b o u n d a r y - - boundary: Part の境界 Content‑Dispositoin: ブラウザでインラインか添付ファイルかを判断するのに使用。 直接ダウンロー ドさせたいときは、 このヘッダー を付ける。 リクエストでは、form‑data を先頭に name( フィー ルド名) と filename( 元のファイル名) 8
  9. Go でどう受け取るか どういう違いがあるでしょうか。 net/http/#Request.FormFile net/http/#Request.MultipartReader 9

  10. Go でどう受け取るか どういう違いがあるでしょうか。 net/http/#Request.FormFile ← 1 つだけ net/http/#Request.MultipartReader ← 複数

    10
  11. net/http/#Request.FormFile f u n c ( r * R e

    q u e s t ) F o r m F i l e ( k e y s t r i n g ) ( m u l t i p a r t . F i l e , * m u l t i p a r t . F i l e H e a d e r , e r r o r ) { i f r . M u l t i p a r t F o r m = = m u l t i p a r t B y R e a d e r { r e t u r n n i l , n i l , e r r o r s . N e w ( " h t t p : m u l t i p a r t h a n d l e d b y M u l t i p a r t R e a d e } i f r . M u l t i p a r t F o r m = = n i l { e r r : = r . P a r s e M u l t i p a r t F o r m ( d e f a u l t M a x M e m o r y ) i f e r r ! = n i l { r e t u r n n i l , n i l , e r r } } i f r . M u l t i p a r t F o r m ! = n i l & & r . M u l t i p a r t F o r m . F i l e ! = n i l { i f f h s : = r . M u l t i p a r t F o r m . F i l e [ k e y ] ; l e n ( f h s ) > 0 { f , e r r : = f h s [ 0 ] . O p e n ( ) r e t u r n f , f h s [ 0 ] , e r r } } r e t u r n n i l , n i l , E r r M i s s i n g F i l e } 11
  12. net/http/#Request.ParseMultipartForm f u n c ( r * R e

    q u e s t ) P a r s e M u l t i p a r t F o r m ( m a x M e m o r y i n t 6 4 ) e r r o r { . . . m r , e r r : = r . m u l t i p a r t R e a d e r ( f a l s e ) i f e r r ! = n i l { r e t u r n e r r } f , e r r : = m r . R e a d F o r m ( m a x M e m o r y ) i f e r r ! = n i l { r e t u r n e r r } . . . r e t u r n n i l } 12
  13. net/http/#Request.MultipartReader f u n c ( r * R e

    q u e s t ) M u l t i p a r t R e a d e r ( ) ( * m u l t i p a r t . R e a d e r , e r r o r ) { i f r . M u l t i p a r t F o r m = = m u l t i p a r t B y R e a d e r { r e t u r n n i l , e r r o r s . N e w ( " h t t p : M u l t i p a r t R e a d e r c a l l e d t w i c e " ) } i f r . M u l t i p a r t F o r m ! = n i l { r e t u r n n i l , e r r o r s . N e w ( " h t t p : m u l t i p a r t h a n d l e d b y P a r s e M u l t i p a r t F o r m " } r . M u l t i p a r t F o r m = m u l t i p a r t B y R e a d e r r e t u r n r . m u l t i p a r t R e a d e r ( t r u e ) } 13
  14. net/http/#Request.multipartReader f u n c ( r * R e

    q u e s t ) m u l t i p a r t R e a d e r ( a l l o w M i x e d b o o l ) ( * m u l t i p a r t . R e a d e r , e r r o r ) { v : = r . H e a d e r . G e t ( " C o n t e n t - T y p e " ) i f v = = " " { r e t u r n n i l , E r r N o t M u l t i p a r t } d , p a r a m s , e r r : = m i m e . P a r s e M e d i a T y p e ( v ) i f e r r ! = n i l | | ! ( d = = " m u l t i p a r t / f o r m - d a t a " | | a l l o w M i x e d & & d = = " m u l t i p a r t r e t u r n n i l , E r r N o t M u l t i p a r t } b o u n d a r y , o k : = p a r a m s [ " b o u n d a r y " ] i f ! o k { r e t u r n n i l , E r r M i s s i n g B o u n d a r y } r e t u r n m u l t i p a r t . N e w R e a d e r ( r . B o d y , b o u n d a r y ) , n i l } 14
  15. Go のコー ドを読めば仕様がわかる! 15

  16. 勉強になった面白いところ 32MB 以下ならメモリで処理、32MB 超なら一時ファイルを作成する。 c o n s t (

    d e f a u l t M a x M e m o r y = 3 2 < < 2 0 ) mime/multipart/formdata.go i f n > m a x M e m o r y { / / t o o b i g , w r i t e t o d i s k a n d f l u s h b u f f e r f i l e , e r r : = i o u t i l . T e m p F i l e ( " " , " m u l t i p a r t - " ) i f e r r ! = n i l { r e t u r n n i l , e r r } s i z e , e r r : = i o . C o p y ( f i l e , i o . M u l t i R e a d e r ( & b , p ) ) i f c e r r : = f i l e . C l o s e ( ) ; e r r = = n i l { e r r = c e r r } . . . } 16
  17. ちょっと話はそれますが strconv.Itoa の 99 まではslice で切り出すだけの処理。 c o n s

    t s m a l l s S t r i n g = " 0 0 0 1 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9 " + " 1 0 1 1 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 " + " 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 " + " 3 0 3 1 3 2 3 3 3 4 3 5 3 6 3 7 3 8 3 9 " + " 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 " + " 5 0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5 8 5 9 " + " 6 0 6 1 6 2 6 3 6 4 6 5 6 6 6 7 6 8 6 9 " + " 7 0 7 1 7 2 7 3 7 4 7 5 7 6 7 7 7 8 7 9 " + " 8 0 8 1 8 2 8 3 8 4 8 5 8 6 8 7 8 8 8 9 " + " 9 0 9 1 9 2 9 3 9 4 9 5 9 6 9 7 9 8 9 9 " 17
  18. アップロー ドされた内容のバリデー ション 入稿規定というものがありまして。。。 例) HTML golang.org/x/net/html でパー ス NG(

    ワー ド、 文字、 タグ、 属性、 属性値) ひたすら正規表現 画像 のちほど触れます 18
  19. Regexp のとてもためになるTips 参考:regexp との付き合い方‑go 言語標準の正規表現ライブラリのパフォー マンスとアルゴリズム‑ https://medium.com/eureka‑engineering/regexp との付き合い方‑go 言語標準の正規表現ライブラリ のパフォー

    マンスとアルゴリズム‑984b6cbeeb2b c o n s t L i n k P a t t e r n = ` h t t p s ? : / / ( ? : [ a - z 0 - 9 \ - ] + \ . ) + [ a - z 0 - 9 \ - ] + ( ? : / [ a - z A - Z 0 - 9 _ \ - ] * ) ? ( ? : \ ? ? v a r L i n k R e g e x p = r e g e x p . M u s t C o m p i l e ( L i n k P a t t e r n ) f u n c g e t L i n k R e g e x p ( ) * r e g e x p . R e g e x p { r e t u r n L i n k R e g e x p . C o p y ( ) } 19
  20. 画像 大きさ、 デー タサイズ、GIF アニメー ション Rails ではimagemagick で画像情報を取得 →

    image パッケー ジへ バイナリの判定は最初の数バイトで。 i m p o r t _ " i m a g e / g i f " f u n c i n i t ( ) { i m a g e . R e g i s t e r F o r m a t ( " g i f " , " G I F 8 ? a " , D e c o d e , D e c o d e C o n f i g ) } 20
  21. GIF の仕様もわかりやすい https://golang.org/pkg/image/gif/#GIF t y p e G I F

    s t r u c t { I m a g e [ ] * i m a g e . P a l e t t e d / / T h e s u c c e s s i v e i m a g e s . D e l a y [ ] i n t / / T h e s u c c e s s i v e d e l a y t i m e s , o n e p e r f r a m e , i n 1 0 0 t h / / L o o p C o u n t c o n t r o l s t h e n u m b e r o f t i m e s a n a n i m a t i o n w i l l b e / / r e s t a r t e d d u r i n g d i s p l a y . / / A L o o p C o u n t o f 0 m e a n s t o l o o p f o r e v e r . / / A L o o p C o u n t o f - 1 m e a n s t o s h o w e a c h f r a m e o n l y o n c e . / / O t h e r w i s e , t h e a n i m a t i o n i s l o o p e d L o o p C o u n t + 1 t i m e s . L o o p C o u n t i n t D i s p o s a l [ ] b y t e C o n f i g i m a g e . C o n f i g B a c k g r o u n d I n d e x b y t e } 21
  22. その他使用したパッケー ジ PDF github.com/signintech/gopdf xlsx https://github.com/tealeg/xlsx xlsx はxml でカー ソルの位置情報も持ってる。

    空セルの判定 → カー ソルが乗ってるとデー タありの判定に。。。 日付型などの情報 → エクセル上では、 文字列とか日付型とか。。。 zip defer でClose すると、 空のzip になってしまう。 参考: golang 標準ライブラリから学ぶzip ファイルの構造 https://blog.freedom‑man.com/zip‑structure‑golang/ 22
  23. まとめ Go のパッケー ジを読むと仕様理解ができる。 なんとなくできていたものが、 一つ一つ理解して積み重ねていける。 23

  24. ありがとうございました 24

  25. おまけ 懇親会で質問してくださった方がいたので、 他にも使ったパッケー ジを紹介します。 github.com/gocraft/dbr : クエリビルダー(JST を扱うときはdialect を作りましょう) github.com/gorilla/mux

    : ルー ティング github.com/rs/cors :CORS github.com/rs/xid :id 生成 github.com/shogo82148/go‑sql‑proxy :SQL ログの出力で利用 25