Nuxtでaxiosを使ってCORSエラーを出さずに無料で郵便番号検索をする

こんにちは。フロントエンドやっていますか?
今回は郵便番号を入れたら住所を自動入力する機能についてです。
フロントエンドからxhrでリクエストしたら多くのサイトでCORSのエラーで引っかかってしまったのでその対応方法です。
Nuxtとタイトルしていますが、Vueでも応用可能です。
また、NextやReactでもaxiosを使っていれば、内容を応用して実装することが可能だと思います。
サンプルの内容を利用していただければ、郵便番号検索機能作成が1,2時間で終わるかと思うので参考になれば幸いです。
※ 2021/05/14時点では無料で外部の郵便検索サービスが使えています。
— 重要 —
JSONPを利用しているため、利用する外部サイトは十分信頼できるサイトにしてください。
また、CORSを回避するためになんでもかんでもJSONPでやりとりするのは非常に危険です。
秘匿情報を含む場合は十分に注意のうえご利用ください。
本記事の場合では
- 郵便番号での住所情報取得という公開情報である
- 利用する外部サイトが信頼できる
と判断した上で利用しています。
目次
- 環境
- 前提
- 実装方法
- まとめ
1. 環境
- Nuxt: 2.15.4 // あまり関係ない
- vue: 2.6.12 // あまり関係ない
- @nuxtjs/axios: 5.13.1 // あまり関係ない
- axios-jsonp: ^1.0.4 // 今回のメイン
- 外部郵便番号API提供サイト: zipcloud
2. 前提
- 郵便番号を入力し、「郵便番号検索」ボタンを押下で都道府県や市区町村を取得する
- CORSをスルーするためにJSONPを利用しています
- セキュリティリスクをご理解の上、信頼に足るサイトに対してのみご利用ください
- わかりやすくサンプル記述しているつもりですが、部分部分で切り取っているので適宜補完してください
3. 実装方法
1. axios-jsonpをインストールとimport
まずは今回のメインライブラリをインストールします。
axiosでのjsonp利用をサポートしてくれます。
$ npm install axios-jsonp
続いて郵便番号検索したいファイルに、インストールしたライブラリを読み込みます。
<script lang="ts">
const jsonpAdapter = require('axios-jsonp')
...略
</script>
typescriptだと@typesがなかったので、requireしてしまっています。
適宜修正してください。
2. formの設置
郵便番号のinputと検索ボタンを用意します。
また、検索結果の都道府県と市区町村を入れるフォームも用意します。
※ サンプルのため、レイアウトは考慮していません
※ bootstrap-vueのコンポーネントを利用しています
<template>
...略
// 郵便番号入力
<b-form-input
v-model="postalCode"
name="postalCode"
rules="numeric|length:7"
/>
<b-btn
:disabled="!isValidPostalCode() || searching"
@click="onSearchPostalCode"
>
郵便番号検索
</b-btn>
// 都道府県(検索結果を入れる先)
<b-form-input
v-model="prefecture"
name="prefecture"
/>
// 市区町村(検索結果を入れる先)
<b-form-input
v-model="city"
name="city"
/>
...略
</template>
3. 各種メソッドの実装
まず全貌
ざっと全体を御覧ください。後ほど各部の説明です。
<script>
export default {
data: {
postalCode: '',
prefecture: ''.
city: '',
},
...略
methods: {
onSearchPostalCode() {
if (this.isValidPostalCode()) {
this.searching = true // 2度押し対策です。ASP側に迷惑かけないように…
this.$axios({
url: `https://zipcloud.ibsnet.co.jp/api/search?zipcode=${this.postalCode}`,
adapter: jsonpAdapter, // ここがポイントです。requireしたアダプタを利用しています。
})
.then((response) => {
this.prefecture = response.data.results[0].address1 // 都道府県
this.city = response.data.results[0].address2 // 市区町村
})
.catch((_error) => {
console.log('エラーが発生しました') //
})
.finally(() => {
setTimeout(() => {
this.searching = false // 2度押し対策です。ASP側に迷惑かけないように…
}, 3000)
})
}
},
isValidPostalCode() {
return (
this.postalCode &&
this.postalCode.length === 7 &&
this.postalCode.match(/^[\d]*$/)
)
},
},
...略
</script>
今回のキモ部分
this.$axios({
url: `https://zipcloud.ibsnet.co.jp/api/search?zipcode=${this.postalCode}`,
adapter: jsonpAdapter, // ここがポイントです。requireしたアダプタを利用しています。
})
this.$axiosを利用しつつ、adapterに先程requireしたaxios-jsonpを設定しています。
これを設定することで?xhrではない以下のリクエストが送信されます。
Request URL: https://zipcloud.ibsnet.co.jp/api/search?zipcode=3020004&_=1621002650808&callback=axiosJsonpCallback1
※ このcallback名にscriptが埋め込まれる可能性があるみたいですね。
レスポンス
今回の利用しているサービスでは以下の内容に都道府県情報が入っています。
.then((response) => {
this.prefecture = response.data.results[0].address1 // 都道府県(北海道、東京都など)
this.city = response.data.results[0].address2 // 市区町村(取手市、入間市など)
})
レスポンス内容はAPIの仕様によるので、詳細は利用しているAPIサイトでご確認ください。
上記の場合はaddress1にもaddress2にもテキストで名前が返ってきます。
都道府県や市区町村をid管理している場合は何らかの方法で変換が必要です。
ASPへの配慮
郵便番号検索ボタンを連打されるケースもあるので、連打対策は十分に行っておきましょう。
せっかくサービスを提供してくれているASP側に迷惑をかけてしまいます。
今回は以下の実装で一定時間ボタンが押せないようにしています。
.finally(() => {
setTimeout(() => {
this.searching = false // 2度押し対策です。ASP側に迷惑かけないように…
}, 3000)
})
まとめ
いかがだったでしょうか?
非常に簡単に郵便番号検索が実装できたかと思います。
xhrでのリクエストではなくなるので、CORSのエラーに引っかからなくなるということですね。
今回はボタンクリックでの検索でしたが、onBlurやonInputのイベントでも同様に実装できるかと思います。
あとがき
私はフロントエンドとバックエンドとアーキテクチャを分ける考え方、とても好きです。
最初はフロントエンドのベースを作成するのに時間がかかりますが、後々の運用保守のしやすさや再利用性の高さが光りますよね。
個人的にはNextよりもNuxtがシンプルで好きです。
Nextのほうが世界シェアが圧倒的なんですけどね…
もっとNuxt(vue.js)は広がっていいと思います。
この記事が誰かの助けになれば幸いです。
ではまた!