マック ハッピーセット おトク情報がいっぱい! プラレール 2021 スペシャルDVD 150円 マック ハッピーセット プラレール スペシャルDVD 2021 本・音楽・ゲーム DVD ブルーレイ キッズ ファミリー 150円,本・音楽・ゲーム , DVD , ブルーレイ , キッズ , ファミリー,スペシャルDVD,animalventana.farm,2021,マック ハッピーセット プラレール,/coxofemoral965079.html 150円,本・音楽・ゲーム , DVD , ブルーレイ , キッズ , ファミリー,スペシャルDVD,animalventana.farm,2021,マック ハッピーセット プラレール,/coxofemoral965079.html マック ハッピーセット おトク情報がいっぱい! プラレール 2021 スペシャルDVD 150円 マック ハッピーセット プラレール スペシャルDVD 2021 本・音楽・ゲーム DVD ブルーレイ キッズ ファミリー

最大12%OFFクーポン マック ハッピーセット おトク情報がいっぱい プラレール 2021 スペシャルDVD

マック ハッピーセット プラレール スペシャルDVD 2021

150円

マック ハッピーセット プラレール スペシャルDVD 2021

商品の状態新品、未使用
配送料の負担送料込み(出品者負担)
配送の方法らくらくメルカリ便
発送元の地域群馬県
発送までの日数1~2日で発送

新品未使用

マック ハッピーセット プラレール スペシャルDVD 2021

Node.js FileStreamの読み込みを一時停止しつつ、一括登録を実行する

郵便番号データをTedious BulkLoadで一括登録してみました。
Node.js Tedious でBulkLoadを使用して郵便番号データを一括登録

このときは郵便番号約12万件をすべて読みこんで一括登録しましたが、
登録するデータ件数が増えた場合を考慮し、1万件毎にBulkLoadするよう修正してみます。


最初のサンプル



1万件読み込んだらBulkLoadを実行すればよいだろうと修正したソースがこちら。


  1. const fs = require('fs');
  2. const readline = require('readline')
  3. const { Connection, TYPES } = require('tedious')
  4. // 設定に従いデータベースへ接続
  5. function create_connection(config) {
  6.     
  7.     const connection = new Connection(config)
  8.     // Promiseをnewした時点で引数のfunctionが実行される
  9.     const p = new Promise(function(resolve, reject) {
  10.         connection.on('connect', err => {
  11.             if (err) {
  12.                 reject(err)
  13.             } else {
  14.                 resolve(connection)
  15.             }
  16.         });
  17.         connection.connect()
  18.     });
  19.     return p
  20.     
  21. }
  22. // BulkLoadの実行
  23. function execBulkLoad(connection, rows) {
  24.     const p = new Promise(function(resolve, reject) {
  25.         // BulkLoad用の設定
  26.         const options = {}
  27.         const bulkLoad = connection.newBulkLoad('postal_code', options, function (error, rowCount) {
  28.             resolve('inserted ' + rowCount + ' rows');
  29.         })
  30.         bulkLoad.addColumn('code', TYPES.Char, { nullable: false })
  31.         bulkLoad.addColumn('address', TYPES.NVarChar, { length: 100, nullable: false })
  32.         rows.forEach((row) => {
  33.             // { code: '郵便番号', address: '住所'} の形式のデータをaddRow
  34.             bulkLoad.addRow(row)
  35.         })
  36.         // バルクロード実行
  37.         connection.execBulkLoad(bulkLoad)
  38.     });
  39.     return p
  40. }
  41. async function main() {
  42.     // データベースに接続
  43.     const config = {
  44.         authentication: {
  45.             options: {
  46.                 userName: 'sa',
  47.                 password: 'P@ssw0rd'
  48.             },
  49.             type: 'default'
  50.         },
  51.         server: 'localhost',
  52.         options: {
  53.             database: 'sample',
  54.             encrypt: false
  55.         }
  56.     }
  57.     const connection = await create_connection(config);
  58.     const stream = fs.createReadStream('./KEN_ALL_UTF8.CSV', 'utf8')
  59.     const reader = readline.createInterface({ input: stream })
  60.     let rows = []
  61.     let rowCount = 0
  62.     reader.on('line', async (data) => {
  63.         // 郵便番号情報を取得
  64.         const item = data.split(',').map((value) => { return value.replace(/^"+|"+$/g,'') })
  65.         const code = item[2]
  66.         const address = item[6] + item[7] + item[8]
  67.         rows.push({code: code, address: address})
  68.         if (rows.length == 10000) {
  69.             const msg = await execBulkLoad(connection, rows)
  70.             console.log(msg)
  71.             rows = []
  72.         }
  73.         rowCount++
  74.     })
  75.     reader.on('close', async () => {
  76.         // 登録実行
  77.         if (rows.length) {
  78.             const msg = await execBulkLoad(connection, rows)
  79.             console.log(msg)
  80.         }
  81.         console.log(rowCount)
  82.         connection.close()
  83.     })
  84. }
  85. main()




実行すると、半分程度しか登録されません。


$ node app.js
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 876 rows
124517



読み込んだ件数12万件に対し、インサートログは60,874件。
実際にデータベースへ登録されていたのは、60,876件でした。
なんでだ?



pause / resume



BulkLoadを実行中もファイル読み込みが実行されるのでデータ件数が合わないのでは?

ドキュメントを見てみると、streamにはpauseとresumeというメソッドがあります。
https://nodejs.org/api/stream.html#stream_readable_pause

BulkLoad前にpauseを呼び出し。
終わったらresumeを呼び出して処理再開としてみます。

修正箇所の抜粋です。


  1.     reader.on('line', async (data) => {
  2.         // 郵便番号情報を取得
  3.         const item = data.split(',').map((value) => { return value.replace(/^"+|"+$/g,'') })
  4.         const code = item[2]
  5.         const address = item[6] + item[7] + item[8]
  6.         rows.push({code: code, address: address})
  7.         if (rows.length == 10000) {
  8.             reader.pause() // 読み込みを一旦停止
  9.             const msg = await execBulkLoad(connection, rows)
  10.             console.log(msg)
  11.             rows = []
  12.             reader.resume() // 読み込み再開
  13.         }
  14.         rowCount++
  15.     })





$ node app.js
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 10000 rows
inserted 2091 rows
124517



読み込んだレコード数と登録件数がかなり近づきましたが、まだ漏れがあります。



await pause



pauseが実行されるまでに読み込んでしまうデータがあるのでは?と思い、pauseにawaitをつけてみました。


  1.     reader.on('line', async (data) => {
  2.         // 郵便番号情報を取得
  3.         const item = data.split(',').map((value) => { return value.replace(/^"+|"+$/g,'') })
  4.         const code = item[2]
  5.         const address = item[6] + item[7] + item[8]
  6.         rows.push({code: code, address: address})
  7.         if (rows.length == 10000) {
  8.             await reader.pause() // 読み込みを一旦停止
  9.             const msg = await execBulkLoad(connection, rows)
  10.             console.log(msg)
  11.             rows = []
  12.             reader.resume() // 読み込み再開
  13.         }
  14.         rowCount++
  15.     })




$ node app.js
inserted 10376 rows
inserted 10311 rows
inserted 10123 rows
inserted 10291 rows
inserted 10219 rows
inserted 10029 rows
inserted 10024 rows
inserted 10316 rows
inserted 10190 rows
inserted 10161 rows
inserted 10221 rows
inserted 10165 rows
inserted 2091 rows
124517




これで読み込んだ件数と登録件数が一致してくれました。
・・・しかし、指定した件数でのデータ登録とはならず、分割境界値付近での挙動が気になります。
エディタにもawaitの意味がないという警告が表示されますし。



pause event



pauseしたときに発生するイベント内でデータ登録を行うよう修正しました。


  1. const fs = require('fs');
  2. const readline = require('readline')
  3. const { Connection, TYPES } = require('tedious')
  4. // 設定に従いデータベースへ接続
  5. function create_connection(config) {
  6.     
  7.     const connection = new Connection(config)
  8.     // Promiseをnewした時点で引数のfunctionが実行される
  9.     const p = new Promise(function(resolve, reject) {
  10.         connection.on('connect', err => {
  11.             if (err) {
  12.                 reject(err)
  13.             } else {
  14.                 resolve(connection)
  15.             }
  16.         });
  17.         connection.connect()
  18.     });
  19.     return p
  20.     
  21. }
  22. // BulkLoadの実行
  23. function execBulkLoad(connection, rows) {
  24.     const p = new Promise(function(resolve, reject) {
  25.         // BulkLoad用の設定
  26.         const options = {}
  27.         const bulkLoad = connection.newBulkLoad('postal_code', options, function (error, rowCount) {
  28.             resolve('inserted ' + rowCount + ' rows (input:' + rows.length+')');
  29.         })
  30.         bulkLoad.addColumn('code', TYPES.Char, { nullable: false })
  31.         bulkLoad.addColumn('address', TYPES.NVarChar, { length: 100, nullable: false })
  32.         rows.forEach((row) => {
  33.             // { code: '郵便番号', address: '住所'} の形式のデータをaddRow
  34.             bulkLoad.addRow(row)
  35.         })
  36.         // バルクロード実行
  37.         connection.execBulkLoad(bulkLoad)
  38.     });
  39.     return p
  40. }
  41. async function main() {
  42.     // データベースに接続
  43.     const config = {
  44.         authentication: {
  45.             options: {
  46.                 userName: 'sa',
  47.                 password: 'P@ssw0rd'
  48.             },
  49.             type: 'default'
  50.         },
  51.         server: 'localhost',
  52.         options: {
  53.             database: 'sample',
  54.             encrypt: false
  55.         }
  56.     }
  57.     const connection = await create_connection(config);
  58.     const stream = fs.createReadStream('./KEN_ALL_UTF8.CSV', 'utf8')
  59.     const reader = readline.createInterface({ input: stream })
  60.     let rows = []
  61.     let bulkRows = []
  62.     let rowCount = 0
  63.     reader.on('line', (data) => {
  64.         // 郵便番号情報を取得
  65.         const item = data.split(',').map((value) => { return value.replace(/^"+|"+$/g,'') })
  66.         const code = item[2]
  67.         const address = item[6] + item[7] + item[8]
  68.         rows.push({code: code, address: address})
  69.         
  70.         if (rows.length == 10000) {
  71.             bulkRows.push(rows)
  72.             rows = []
  73.             reader.pause() // 読み込みを一旦停止
  74.         }
  75.         rowCount++
  76.     })
  77.     reader.on('pause', async () => {
  78.         // reader.pauseに加え、closeイベントの前にも呼び出される
  79.         // 登録内容が存在しない場合はスキップ
  80.         if (bulkRows.length == 0) {
  81.             return
  82.         }
  83.         const msg = await execBulkLoad(connection, bulkRows.pop())
  84.         console.log(msg)
  85.         reader.resume() // 読み込み再開
  86.     })
  87.     reader.on('close', async () => {
  88.         // 登録実行
  89.         if (rows.length > 0) {
  90.             const msg = await execBulkLoad(connection, rows)
  91.             console.log(msg)
  92.         }
  93.         console.log(rowCount)
  94.         connection.close()
  95.     })
  96. }
  97. main()




狙い通りの実行結果です。


$ node app.js
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 10000 rows (input:10000)
inserted 4517 rows (input:4517)
124517


関連記事

fastify データベースを検索した結果をhtmlで表示する

fastifyでデータベースの検索やhtmlの表示を試してみました。
fastifyで郵便番号検索APIのサンプル
fastifyでhtmlビューを表示する(point-of-view, ejs)

2つを組み合わせて、データベースを検索した結果をhtmlで表示してみます。


サンプル



サーバー部分は以下のようになりました。

・server.js


  1. const fastify = require('fastify')({ logger: true })
  2. const postal = require('./module/postal')()
  3. fastify.register(require('point-of-view'), {
  4.     engine: {
  5.         ejs: require('ejs')
  6.     }
  7. })
  8. fastify.get('/', async (req, reply) => {
  9.     // 検索結果をviewに渡す
  10.     const rows = await postal.search('銀座')
  11.     reply.view('/views/index.ejs', { rows: rows })
  12. })
  13. fastify.listen(3000, '0.0.0.0', err => {
  14.     if (err) throw err
  15.     console.log(`server listening on ${fastify.server.address().port}`)
  16. })




データベースを検索する処理です。

・module/postal.js


  1. const { Connection, Request } = require('tedious')
  2. module.exports = () => {
  3.     // 接続情報
  4.     const config = {
  5.         authentication: {
  6.             options: {
  7.                 userName: 'sa',
  8.                 password: 'P@ssw0rd'
  9.             },
  10.             type: 'default'
  11.         },
  12.         server: 'localhost',
  13.         options: {
  14.             database: 'sample',
  15.             encrypt: false,
  16.             rowCollectionOnRequestCompletion : true
  17.         }
  18.     }
  19.     // 設定に従いデータベースへ接続
  20.     this.create_connection = (config) => {
  21.     
  22.         const connection = new Connection(config)
  23.         // Promiseをnewした時点で引数のfunctionが実行される
  24.         const p = new Promise(function(resolve, reject) {
  25.             connection.on('connect', err => {
  26.                 if (err) {
  27.                     reject(err)
  28.                 } else {
  29.                     resolve(connection)
  30.                 }
  31.             })
  32.             connection.connect()
  33.         })
  34.         return p
  35.         
  36.     }
  37.     // SQLの実行
  38.     this.execute = (connection, sql) => {
  39.         const p = new Promise(function(resolve, reject) {
  40.             const request = new Request(sql, (err, rowCount, columns) => {
  41.                 if (err) {
  42.                     reject(err)
  43.                     return
  44.                 }
  45.                 let rows = []
  46.                 columns.forEach(column => {
  47.                     let row = {}
  48.                     column.forEach(field => {
  49.                         row[field.metadata.colName] = field.value
  50.                     });
  51.                     rows.push(row)
  52.                 });
  53.                 
  54.                 resolve(rows)
  55.             })
  56.             connection.execSql(request)
  57.         });
  58.         return p
  59.     }
  60.     // 住所の一部でデータを検索
  61.     this.search = async (address) => {
  62.         const connection = await this.create_connection(config)
  63.         const rows = await this.execute(connection, "SELECT * FROM postal_code WHERE address LIKE '%" + address + "%' ORDER BY code")
  64.         connection.close()
  65.         return rows
  66.     }
  67.     return this
  68. }




表示部分はこのようになりました。

・views/index.ejs


  1. <html lang="ja">
  2. <head>
  3. <meta charset="UTF-8">
  4. <title>fastifyサンプル</title>
  5. <style>
  6. table {
  7.     border: solid 2px;
  8.     border-collapse: collapse;
  9. }
  10. th, td {
  11.     border: solid 2px;
  12.     padding: 4px;
  13. }
  14. </style>
  15. </head>
  16. <body>
  17.     <table>
  18.         <thead>
  19.             <tr>
  20.                 <th>郵便番号</th>
  21.                 <th>住所</th>
  22.             </tr>
  23.         </thead>
  24.         <tbody>
  25.             <% rows.forEach(row => { %>
  26.                 <tr>
  27.                     <td><%= row.code %></td>
  28.                     <td><%= row.address %></td>
  29.                 </tr>
  30.             <% }) %>
  31.         </tbody>
  32.     </table>
  33. </body>
  34. </html>




サーバーを起動してブラウザで表示してみると、ちゃんと住所に「銀座」を含む結果が表示されました。



関連記事

fastify 入力データの検証(validation)

fastifyで入力データの検証を試してみます。

id:数値
address:文字列 必須

というパターンだとこんな感じになりました。
受け取ったデータをそのまま送り返しています。


  1. const fastify = require('fastify')({ logger: true })
  2. const opts = {
  3.     schema: {
  4.         body: {
  5.             type: 'object',
  6.             required: [ // addressを必須項目に
  7.                 'address'
  8.             ],
  9.             properties: {
  10.                 id: { type: 'number'},
  11.                 address: { type: 'string' }
  12.             }
  13.         }
  14.     }
  15. }
  16. fastify.post('/', opts, async (req, reply) => {
  17.     // jsonデータはreq.bodyに設定される
  18.     return req.body
  19. })
  20. fastify.listen(3000, '0.0.0.0', err => {
  21.     if (err) throw err
  22.     console.log(`server listening on ${fastify.server.address().port}`)
  23. })




正常系

$ curl 'http://192.168.11.104:3000' --data '{"id": 123, "address":"住所"}' -X POST -H 'Content-Type:application/json'
{"id":123,"address":"住所"}



idが文字列(数値へ変換可能)

$ curl 'http://192.168.11.104:3000' --data '{"id": "123", "address":"住所"}' -X POST -H 'Content-Type:application/json'
{"id":123,"address":"住所"}


自動的に数値へ変換されました。

idが文字列(数値へ変換不可)

$ curl 'http://192.168.11.104:3000' --data '{"id": "abc", "address":"住所"}' -X POST -H 'Content-Type:application/json'
{"statusCode":400,"error":"Bad Request","message":"body.id should be number"}


ちゃんとエラーになりました。

idがなし

$ curl 'http://192.168.11.104:3000' --data '{"address":"住所"}' -X POST -H 'Content-Type:application/json'
{"address":"住所"}



addressが空白文字列

$ curl 'http://192.168.11.104:3000' --data '{"id": 123, "address":""}' -X POST -H 'Content-Type:application/json'
{"id":123,"address":""}



addressが数値

$ curl 'http://192.168.11.104:3000' --data '{"id": 123, "address":456}' -X POST -H 'Content-Type:application/json'
{"id":123,"address":"456"}



addressがなし

$ curl 'http://192.168.11.104:3000' --data '{"id": 123}' -X POST -H 'Content-Type:application/json'
{"statusCode":400,"error":"Bad Request","message":"body should have required property 'address'"}




このvalidationは、「Ajv」を使用しているとのことで、詳しい使い方はこちらのドキュメントを見たほうが良さそうです。
https://ajv.js.org/json-schema.html

関連記事

fastify json形式のPOSTデータ受け取り

fastifyはPOSTデータはjson形式を想定しており、x-www-form-urlencoded形式のデータ受信にはひと工夫必要でした。
fastifyでPOSTデータ受信時、FST_ERR_CTP_INVALID_MEDIA_TYPE

POSTデータがjson形式の場合について見てみます。

プログラム修正




  1. {"address":"銀座"}



という形式で検索パラメーターを送信することにします。
json形式のPOSTデータは自動的にパースされ、req.bodyに設定されました。


  1. const fastify = require('fastify')({ logger: true })
  2. const postal = require('./module/postal')()
  3. fastify.post('/', async (req, reply) => {
  4.     // jsonデータはreq.bodyに設定される
  5.     const address = req.body.address
  6.     const rows = await postal.search(address)
  7.     return rows
  8. })
  9. fastify.listen(3000, '0.0.0.0', err => {
  10.     if (err) throw err
  11.     console.log(`server listening on ${fastify.server.address().port}`)
  12. })




動作確認


$ curl 'http://192.168.11.104:3000' --data '{"address":"銀座"}' -X POST -H 'Content-Type:application/json'
[
    {"code":"0691331","address":"北海道夕張郡長沼町銀座"},
    {"code":"1040061","address":"東京都中央区銀座"}
...
]

ユニクロ パンツ 2021 ↓ 10日前後での発送となります 10本コーディネートします フルオーダー ご自身の出品画面に として A +1000円 特急便✈✈入金確認後翌日発送 未使用配送料の負担送料込み 下段中指 あれば 出品者負担 色味 ⑤ご希望のイメージ お式後も ➛1つは料金内 S マック ベリーショートorショートオーバル 世界に1つだけのオリジナルネイルチップ ↓ その後 #振袖似合わせネイル例 #ネイル クリアとキラキラ1つずつ ⑤全体的に優しめで  ↓↓↓ 専用ページのコメント欄にて といったお花柄というより お花キラキラ系 #ネイルチップ #成人式ネイル#振袖ネイル#振袖オーダーネイル ネイルチップの制作開始 ──記入例── ①ベリーショート ②右14548 3枚目上段親指 ⚠️ご購入後のサイズ変更 をお作り致します♡ ❁⃘ぷっくりフラワー等を含むデザインを提案致します❁⃘ 配送の方法らくらくメルカリ便発送元の地域石川県発送までの日数4~7日で発送ーご利用いただく皆様へー を必ずご一読お願い致しますm 3前後となりますm など m ぷっくりフラワー 振袖ネイルチップ ハッピーセット オプション追加は基本お受け出来ませんので _ 粘着グミ2つ ④ぷっくりフラワー有 2021 ①チップの形 商品の状態新品 ↓ デザイン決定後⇒価格の最終決定 雰囲気を再現するデザインです✿ 肌馴染みの良いデザインを提案します 画像確認用 ②チップのサイズ 普段使いしていただけるよう デザイン追加 左14558 ③お急ぎ便 B 専用ページをお作り致します 良くご検討されてからご購入お願い致します プラレール ※発送は1 ⚠️デザインに関して いかにも振袖和柄 スペシャルDVD 生地の強調したい色の部分に○印などで書き込んでいただけると分かりやすいです 打ち合わせをします タイプと個数 インパクトのあるアートやパーツを取り入れつつ m ☻ご購入購前にコメントをお願いします☻ ☆こちらはネイリストが お客様の振袖写真をもとに M .゜ +2000円 粘着グミ 2200円 ˊ˘ˋ お花クリア系 ↓ ✎ 100円 ④ぷっくりフラワー有無 振袖写真を数枚を投稿して下さい ↓ お支払いの確認後 華やかに かつ 1回分 ③オプション お急ぎ便✈入金確認後3日以内の発送 ➛2つまで料金内 ✿price✿ ‎4300円 ✈ご購入の流れ✈ まずはこちらのページに下記の①②③④⑤を コメントしていただきARIA The BENEDIZIONE ムビチケ 使用済み 5枚セットマック 27×37×17㎝ #かごバッグ #バスケット #籐 #トランク #旅行 #リゾート また コップやお皿を詰めてピクニックに持っていくのもいいと思います 籐のトランク 出品者負担 2021 中古品であることをご理解のうえご購入いただきたいと思います ドライブにもオススメ リゾートに持っていったらオシャレだと思って購入しました プラレール スペシャルDVD クローゼットに眠っていました 商品の状態傷や汚れあり配送料の負担送料込み 経年による素材の傷みがあります 逆に味があっていい感じに古びています 配送の方法らくらくメルカリ便発送元の地域東京都発送までの日数2~3日で発送籐のトランク型かごバッグです ただ籐なので 写真ご確認ください 傷や汚れというより 10年以上昔に購入し ハッピーセット 日帰りの旅行にもぴったりです 1950円プリーツスカート ロングスカート2021 マック プラレール 商品の状態新品 経年劣化等ご理解いただける方ご購入お願いいたします ハッピーセット 未使用配送料の負担送料込み Kappa 出品者負担 27㎝ ⚠︎数年前に購入し箱に入れたまま自宅保管しておりました シューズ 配送の方法らくらくメルカリ便発送元の地域大阪府発送までの日数1~2日で発送サイズ 即購入OKです 1000円 スペシャルDVD 保管時の匂いデュワーズ12年2本オリジナルグラス2個 値下げ!早いものがち!配送料の負担送料込み スニーカーでもブーツでもなんでも履きこなせる ひとつは持っていたいスカートです ハッピーセット ^^ どなたか大切に来てくださる方よろしくお願いします☆ 自宅保管とusedのため デニムミニスカート サイズアウトの為泣く泣くの出品です SLY FOREVER21 神経質な方はご遠慮ください ; 32cm 長さ 出品者負担 発送時の畳みジワはご了承くださいませ マック 2021 XS デニムスカート めちゃくちゃミニでとーってもかわいいです♡ 年中履ける活躍するスカートです SS 商品の状態目立った傷や汚れなし商品のサイズXS moussy o^^o 500円 値下げ 配送の方法らくらくメルカリ便発送元の地域京都府発送までの日数1~2日で発送これからの季節に大活躍間違いなしです♫ サイズXS 平置きにて ウエスト スペシャルDVD プラレール 多少の誤差はご了承ください 33cm 素人採寸のため FOREVER21 Bershka HMマルチバスケット 収納 バッグインバッグ 収納力抜群 整理整頓 カラー豊富配送の方法ゆうゆうメルカリ便発送元の地域東京都発送までの日数1~2日で発送オプティミスティックのフレグランスミスト です ハッピーセット スウィートエメラルドの香り フレグランスミストSE Optimistic 出品者負担 中古品ですのでご理解いただける方のみご購入をお願い致します 目立った傷等はございませんが 50ml 商品の状態未使用に近い配送料の負担送料込み 400円 プラレール 2021 マック スペシャルDVD 数回のみの使用で 残量は写真の通りですadidas アディダス ミュールENJOI 横 約5.7cm その他のサイズは写真をご参考ください 約20.3cm 商品の状態新品 マック SPITFIRE ご覧頂きありがとうございます 最安値で出品しております他のスケートボード用品とのまとめ買い以外は値下げやバラ売りは申し訳ございませんが断らせていただきます スケートボード用品一覧 #walnutskate よろしくお願いします CRUZ 2021 ALIEN PERALTA THRASHER 防水 スケートボードブランドのステッカー34枚 正規シュプリーム特大ボックスステッカー1枚 防水pvc素材 ひとます1センチが目安です WORKSHOP 10K ぜひチェックしてみてくださいね BONES : POWELL ⚠️注意⚠️初めにバラ売りは不可ですご了承ください 未使用配送料の負担送料込み SANTA #walnutskate ステッカー BAKER 出品者負担 395円 BLIND ハッピーセット シュプリームボックスステッカー サイズ プラレール スケートボード用品一覧は 配送の方法未定発送元の地域千葉県発送までの日数1~2日で発送スケートボード 35枚 スケートボード 2ボード ブランド シュプリーム スペシャルDVD 縦 SUPREME シールフランフラン クッションカバーマック 配送の方法ゆうゆうメルカリ便発送元の地域北海道発送までの日数2~3日で発送数年前に譲り受けた物になります ハッピーセット ラジコンのプロポ 2021 出品者負担 2250円 スペシャルDVD プラレール 商品の状態目立った傷や汚れなし配送料の負担送料込みCOACH コーチ 二つ折り財布外す時はマイナスドライバー等でテコで外す形になります 275円 マック 多少のウエイト調整も可能かと思います ワッシャーはガッチリハマるので バランス調整意外にも高額なロッドの保護にも良いようです 大ワッシャーのみ と大体同じくらいのバランス位置になります 中にはポジティブなレビューをいただけた方もいらっしゃいます 未使用配送料の負担送料込み プラレール ハッピーセット 極端に大きなグリップデザインになっていないロッドであれば適合するかと思いますが ゴムは多少伸縮しますので スペシャルDVD ワッシャーを抜き差ししていただく事で 好評 よろしくお願いします 定形 このゴムキャップを付けると ワールドシャウラ1652R-3 2.5gのワッシャーと6.5gのワッシャーをお付けします ※2020年夏に販売を開始し ステンレスフレームの大径ガイドが9個付いていて先重り感が強いロッドですが エンドキャップ 定形外 配送の方法普通郵便 1年程で50名以上の方に購入いただけました 商品の状態新品 本体重量は14gです 2021 内径は24mm 参考までに写真ではスコーピオン1703R-2に付けています 発送元の地域東京都発送までの日数1~2日で発送ロッドのバランス調整に最適なゴムキャップです 適合の可否については恐縮ですが自己責任でお願い致します 別途 出品者負担 1703でなく申し訳ありません ロッドのバランス調整に最適なゴムキャップ 全て良い評価をいただいており

fastifyでPOSTデータ受信時、FST_ERR_CTP_INVALID_MEDIA_TYPE

fastifyで郵便番号検索APIを作ってみました。
fastifyで郵便番号検索APIのサンプル

検索する住所をGETのクエリーパラメーターではなく、POSTで送信するよう変更してみます。


最初の実装



「fastify.getをfastify.postに変えれば動くだろう」と思い変更してみました。

・server.js


  1. const fastify = require('fastify')({ logger: true })
  2. const postal = require('./module/postal')()
  3. /*
  4. fastify.get('/', async (req, reply) => {
  5.     // クエリーパラメーター取得
  6.     const address = req.query.address
  7.     const rows = await postal.search(address)
  8.     return rows
  9. })
  10. */
  11. // postに変更
  12. fastify.post('/', async (req, reply) => {
  13.     const address = req.query.address
  14.     const rows = await postal.search(address)
  15.     return rows
  16. })
  17. fastify.listen(3000, '0.0.0.0', err => {
  18.     if (err) throw err
  19.     console.log(`server listening on ${fastify.server.address().port}`)
  20. })




curlで動作を確認すると、FST_ERR_CTP_INVALID_MEDIA_TYPE
Unsupported Media Type: application/x-www-form-urlencoded
というエラーになります。


$ curl 'http://192.168.11.104:3000' --data 'address=%E9%8A%80%E5%BA%A7' -X POST
{"statusCode":415,"code":"FST_ERR_CTP_INVALID_MEDIA_TYPE","error":"Unsupported Media Type","message":"Unsupported Media Type: application/x-www-form-urlencoded"





POST対応



調べてみると、デフォルトでPOSTはjson形式のみを受け付け、x-www-form-urlencodedなデータはエラーになるようです。
multipartも同様の模様。
Giving error with node/fastify Unsupported Media Type: application/x-www-form-urlencoded

fastify-formbodyを使うといいよとのこと。
fastify-formbody

インストールします。


$ npm install fastify-formbody




インストールしたfastify-formbodyを登録し、POSTデータをreq.bodyから取得するよう修正します。


  1. const fastify = require('fastify')({ logger: true })
  2. const postal = require('./module/postal')()
  3. // POST対応
  4. fastify.register(require('fastify-formbody'))
  5. // postに変更
  6. fastify.post('/', async (req, reply) => {
  7.     // req.bodyからPOSTデータを取得するよう変更
  8.     //const address = req.query.address
  9.     const address = req.body.address
  10.     const rows = await postal.search(address)
  11.     return rows
  12. })
  13. fastify.listen(3000, '0.0.0.0', err => {
  14.     if (err) throw err
  15.     console.log(`server listening on ${fastify.server.address().port}`)
  16. })




これでPOSTデータを受け取り、検索が実行できるようになりました。


$ curl 'http://192.168.11.104:3000' --data 'address=%E9%8A%80%E5%BA%A7' -X POST
[
     {"code":"0691331","address":"北海道夕張郡長沼町銀座"},
     {"code":"1040061","address":"東京都中央区銀座"}
...
]


関連記事

プロフィール

Author:symfo
blog形式だと探しにくいので、まとめサイト作成中です。
Symfoware まとめ

PR




検索フォーム

月別アーカイブ