Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

decafe09
October 05, 2018

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

decafe09

October 05, 2018
Tweet

More Decks by decafe09

Other Decks in Programming

Transcript

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

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

    ・Go のテンプレー トエンジンを使ってみたが厳しい → Go はAPI で。 4
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 勉強になった面白いところ 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
  10. ちょっと話はそれますが 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
  11. アップロー ドされた内容のバリデー ション 入稿規定というものがありまして。。。 例) HTML golang.org/x/net/html でパー ス NG(

    ワー ド、 文字、 タグ、 属性、 属性値) ひたすら正規表現 画像 のちほど触れます 18
  12. 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
  13. 画像 大きさ、 デー タサイズ、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
  14. 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
  15. その他使用したパッケー ジ 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