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

最終更新日

Nuxt JS Logo

こんにちは。フロントエンドやっていますか?

今回は郵便番号を入れたら住所を自動入力する機能についてです。
フロントエンドからxhrでリクエストしたら多くのサイトでCORSのエラーで引っかかってしまったのでその対応方法です。

Nuxtとタイトルしていますが、Vueでも応用可能です。
また、NextやReactでもaxiosを使っていれば、内容を応用して実装することが可能だと思います。

サンプルの内容を利用していただければ、郵便番号検索機能作成が1,2時間で終わるかと思うので参考になれば幸いです。

※ 2021/05/14時点では無料で外部の郵便検索サービスが使えています。


— 重要 —

JSONPを利用しているため、利用する外部サイトは十分信頼できるサイトにしてください。
また、CORSを回避するためになんでもかんでもJSONPでやりとりするのは非常に危険です。
秘匿情報を含む場合は十分に注意のうえご利用ください。

本記事の場合では

  • 郵便番号での住所情報取得という公開情報である
  • 利用する外部サイトが信頼できる

と判断した上で利用しています。


目次

  1. 環境
  2. 前提
  3. 実装方法
  4. まとめ

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)は広がっていいと思います。

この記事が誰かの助けになれば幸いです。
ではまた!

stmon19

遊びが一番 人生遊び 好きにまみれてます