見出し画像

axiosについてまとめました。

フロントをReact、バックエンドをRailsを指定した時に、バックエンドのデータをどうやって渡すのー?とか、フロントのデータをどうやってバックエンドに渡すのー?と困ったことはありませんか?(僕は困りました、そしてRailsで優秀なんだなと気付かされる)

ReactとRailsの箸渡すのがaxiosです。この記事ではaxiosの使いかたを紹介します。

What is Axios?

Axiosはnode.jsとbrowserのためのプロミスベースHTTP Clientです。それはブラウザとnodejsで同じコードで動く(isomorphic)。サーバーサイドではnode.jsのネイティブなhttpモジュールを使用し、クライアント(ブラウザ)ではXMLHttpRequestsを使用します。

特徴

・ブラウザからXMLHttpRequestsを作成する
・node.jsからhttp requestsを作成する
Promise APIをサポートする
・リクエストとレスポンスを遮断する
・リクエストデータとレスポンスデータを変換する
・リクエストをキャンセルする
・JSONデータに対する自動変換
・クライアントサイドはXSRFを防ぐことをサポートする。

Example

GET Requests

const axios = require('axios');

// IDを指定してgetリクエストを作成する
axios.get('/user?ID=12345')
  .then(function (response) {
    // handle success
    console.log(response);
  })
  .catch(function (error) {
    // handle error
    console.log(error);
  })
  .then(function () {
    // always executed
  });

// 上のオプションを使用した例
axios.get('/user', {
    params: {
      ID: 12345
    }
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  })
  .then(function () {
    // always executed
  });  

// async/awaitを使用した場合の例
async function getUser() {
  try {
    const response = await axios.get('/user?ID=12345');
    console.log(response);
  } catch (error) {
    console.error(error);
  }
}

POST Requests

axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });

// 複数の同時リクエスト実行

function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

Promise.all([getUserAccount(), getUserPermissions()])
  .then(function (results) {
    const acct = results[0];
    const perm = results[1];
  });

Axios API

リクエストは、該当するコンフィグをaxiosに渡すことで行うことができます。
axios(config)

// Send a POST request
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});

Request method aliases=get,post, deleteなど基本的なメソッドがあります。
The Axios Instance
インスタンスもエイリアスメソッドを持っています。指定されたコンフィグはインスタンスコンフィグにマージされます。(実行時にコンフィグを書き換えられるね!)

const instance = axios.create({
  baseURL: '<https://some-domain.com/api/>',
  timeout: 1000,
  headers: {'X-Custom-Header': 'foobar'}
});

Request Config

リクエストを作成するためのコンフィグオプションがあります。urlは必須です。methodが指定されていない場合はgetリクエストになります。

{
  // urlはリクエストのために使用されるサーバーURLです。
  url: '/user',

  // methodはリクエストを作成する時に使用されるリクエストメソッドです。
  method: 'get', // default

  // baseURLはurlが絶対的なものでない限り、urlの前に付加されます。
  // axiosのインスタンスにbaseURLを設定すると、そのインスタンスのメソッドに相対URLを渡すことができ、便利です。
  baseURL: '<https://some-domain.com/api>',

  // transformRequestは常にサーバーに送信する前にリクエストデータを変更する
  // これはPUT, POST, PATCH, DELETEメソッドのリクエストに該当します。
  // Arrayのlast関数は必ず文字列,Buffer, ArrayBufferのインスタンス, FormData, Streamのインスタンスを返す必要があります。
  // ヘッダーオブジェクトを変更することができます・
  transformRequest: [function (data, headers) {
    // データを変換するために必要なことを行う

    return data;
  }],

  // transformResponseはレスポンスデータがthen/catchに渡される前に常にレスポンスデータを変更できる。
  transformResponse: [function (data) {
    // データを変換するために必要なことを行う

    return data;
  }],

  // headersは送信されるカスタムヘッダーです。
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // paramsはリクエストと一緒に送信されるURLパラメータです。
  // プレーンオブジェクトかURLSearchParamsオブジェクトです。
  // NOTE: NULLまたは未定義のパラメータは、URLでレンダリングされない
  params: {
    ID: 12345
  },

  // paramsSerializerはparamsのシリアライズを担当するオプションの関数です。
  // (e.g. <https://www.npmjs.com/package/qs>, <http://api.jquery.com/jquery.param/>)
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // dataはリクエストボディーとして送信されるデータです。
  // リクエストメソッドが該当する(PUT, POST, DELETE, PATCH)
  // transformRequestが設定されていない場合は、以下のいずれかの型である必要がある。
  // string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - Browser only: FormData, File, Blob
  // - Node only: Stream, Buffer  
  data: {
    firstName: 'Fred'
  },
  
  // ボディにデータを送信するための代替構文
  // POSTメソッド
  // 値のみを送信し、キーは送信しない
  data: 'Country=Brasil&City=Belo Horizonte',

  // timeoutはリクエストがタイムアウトするまでのミリ秒数を指定する。
  // もしリクエストがtimeoutよりも長くかかる場合はリクエストは中断される。
  timeout: 1000, // default is `0` (no timeout)

  // withCredentialsはクレデンシャルを使ってcross-site Access-Controlリクエストをするかどうかを示す。
  withCredentials: false, // default

  // adapterはテストを容易にするために、リクエストのカスタムハンドリングを可能にします。
  // プロミスと渡された有効なレスポンスを返す(see lib/adapters/README.md)
  adapter: function (config) {
    /* ... */
  },

  // authはHTTP Basic authを使用することを示し、クレデンシャルを提供します。
 // これはAuthorizationヘッダを設定し、headersを使用して設定した既存のAuthorizationカスタムヘッダを上書きします。
  // このパラメータで設定できるのはHTTP Basic auth認証のみであることに注意してください
  // Bearerトークンなどの場合は、代わりにAuthorizationカスタムヘッダを使用してくだい
  auth: {
    username: 'janedoe',
    password: 's00pers3cret'
  },

  // responseTypeはサーバーが応答するデータの種類を示します。
  // オプションは'arraybuffer', 'document', 'json', 'text', 'stream'
  // browser only: 'blob'
  responseType: 'json', // default

  // responseEncodingはレスポンスをデコードするエンコーディンを指定する(Node.jsのみ)
  // Note: respnseTypeがstreamかクライアントサイドリクエストの場合は無視される
  responseEncoding: 'utf8', // default

  // xsrfCookieNameはxsrfトークンとして使用されうクッキーの名前
  xsrfCookieName: 'XSRF-TOKEN', // default

  // xsrfHeaderNameはxsrfトークンの値を格納するhttpヘッダーの名前
  xsrfHeaderName: 'X-XSRF-TOKEN', // default

  
 // onUploadProgressはアップロードイベント進捗をハンドリングを可能にする
  // browser only
  onUploadProgress: function (progressEvent) {
    // Do whatever you want with the native progress event
    プログレスイベントで好きなことをする
  },

 // onDownloadProgressはダウンロードイベント進捗をハンドリングを可能にする
  // browser only
  onDownloadProgress: function (progressEvent) {
    プログレスイベントで好きなことをする
  },

 // maxContentLengthはnode.jsで許可されるhttpレスポンスのコンテンツの最大サイズをバイト数で定義します。
  maxContentLength: 2000,

  // maxBodyLengthは許可されるhttpリクエストコンテンツの最大サイズをバイス数で定義します。
  maxBodyLength: 2000,

 // validateStatusは与えられたhttpレスポンスステータスコードに対してプロミスにresolveかrejectかを定義します。
  // validateStatusがtrueをかえした場合はプロミスはresolvedになる。他はrejectedになる。
  validateStatus: function (status) {
    return status >= 200 && status < 300; // default
  },

 // maxRedirectsはnode.jsで追いかけるリダイレクトの最大数を定義する。
  // 0を設定すると、リダイレクトをたどらない。
  maxRedirects: 5, // default

  // socketPathはnode.jsで使用されるUNIX Socketを定義する
  // 例:'/var/run/docker.sock' で docker デーモンにリクエストを送信します。
  // socketPathかProxyを指定します。両方指定した場合はsocketPathが使用されます。
  socketPath: null, // default

 // デフォルトでは有効になっていないkeepAliveのようなオプションを追加することができます。
  httpAgent: new http.Agent({ keepAlive: true }),
  httpsAgent: new https.Agent({ keepAlive: true }),

  // procyはプロキシサーバーのホスト名、ポート、プロトコルを定義する。
  proxy: {
    protocol: 'https',
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

 // cancelTokenはリクエストをキャンセルするために使用するキャンセルトークンを指定します。
  cancelToken: new CancelToken(function (cancel) {
  }),

 // decompressはレスポンスボディを自動的に伸張するかどうかを示す。
  // 自動的に解凍されます。true設定すると、解凍されたレスポンスのレスポンスオブジェクトからcontent-encodingヘッダも削除される。
  decompress: true // default

}

Response Schema

リクエストのレスポンスは以下の情報を含んでいます。

{
  // dataはサーバーによって返されたレスポンス
  data: {},

 // statusはサーバーレスポンスからのHTTPステータスコード
  status: 200,

  // statusTextはサーバーレスポンスからのHTTPステータスメッセージ
  statusText: 'OK',

 // headersはサーバーが応答したHTTPヘッダ
 // 全てのヘッダ名は小文字でブラケット記法でアクセスできます。
  // Example: `response.headers['content-type']`
  headers: {},

 // configはリクエストのためにaxiosに提供されたコンフィグである。
  config: {},
 // requestはこのレスポンスを生成したリクエストです。
 // node.jsの最後のClientRequestインスタンス
 // ブラウザはXMLHttpRequestインスタンス
  request: {}
}

レスポンスをthenで受け取ることができます。

axios.get('/user/12345')
  .then(function (response) {
    console.log(response.data);
    console.log(response.status);
    console.log(response.statusText);
    console.log(response.headers);
    console.log(response.config);
  });

Config Defaults

あなたは全てのリクエストに適応するコンフィグデフォルトを明記します。
Global axios defaults

axios.defaults.baseURL = '<https://api.example.com>';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded

Custom instance defaults

// インスタンスが作成された時、コンフィグデフォルトを設定します。
const instance = axios.create({
  baseURL: '<https://api.example.com>'
});

// インスタンスが作成された後にデフォルトを変更する
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;

configは優先順位をつけてマージされます。
優先順位
1lib/defaults.js にあるライブラリのデフォルト
2インスタンスのデフォルト
3リクエストの config 引数

// ライブライから渡されたコンフィグデフォルトを使用してインスタンスを作成する
// この時点のtimeoutは0になります。
const instance = axios.create();

// timeoutをオーバーライドします。
// このインスタンスを使用している全てのリクエストはタイムアウトする前に2.5秒待つ。
// instance.defaults.timeout = 2500;

// このリクエストはtimeoutを5000にしています。
instance.get('/longRequest', {
  timeout: 5000
});

Interceptors

あなたはthenかcatchをハンドリングする前にリクエストかレスポンスを遮ることができる。

// リクエスト遮断を追加する
axios.interceptors.request.use(function (config) {
    // 送られたリクエスト前に何かをする
    return config;
  }, function (error) {
    // リクエストエラーに対して何かを行う。
    return Promise.reject(error);
  });

// Add a response interceptor
// レスポンス遮断を追加する
axios.interceptors.response.use(function (response) {
    // ステータスコードが2xxの範囲にある場合、この関数が起動する。
    // レスポンスデータに対して何かを行う。
    return response;
  }, function (error) {
    // ステータスコードが2xxの範囲外にある場合、この関数が起動する。
    // レスポンスエラーに対して何かを行う
    return Promise.reject(error);
  });

後でinterceptorを取り外す必要がある場合は可能です。

const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);

axiosのカスタムインスタンスにinterceptorsを追加する。

const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});

Handling Errors

axios.get('/user/12345')
  .catch(function (error) {
    if (error.response) {
      // リクエストが行われて、サーバは2xxの範囲から外れるステータスコードで応答しました。
      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);
    } else if (error.request) {
      // リクエストは行ったが、レスポンスを受信しなかった。
      // error.requestはブラウザではXMLHttpRequestのインスタンス、node.jsではhttp.ClientRequestのインスタンス
      console.log(error.request);
    } else {
      // リクエストのセットアップ中にエラーが発生しました。
      console.log('Error', error.message);
    }
    console.log(error.config);
  });

validateStatus
validateStatus設定オプションを使用すると、エラーを投げるべきHTTPコードを定義することができます。

axios.get('/user/12345', {
  validateStatus: function (status) {
    return status < 500; // ステータスコードが500未満の場合のみ解決する。
  }
})

toJSON
toJSONを使用すると、HTTPエラーの詳細情報を持つオブジェクトを取得できます。

axios.get('/user/12345')
  .catch(function (error) {
    console.log(error.toJSON());
  });

Cancellation

AbortController
v0.22.0以降、AxiosはAbortControllerをサポートし、fetch APIの方法でリクエストをキャンセルできるようになりました。

const controller = new AbortController();

axios.get('/foo/bar', {
   signal: controller.signal
}).then(function(response) {
   //...
});
// cancel the request
// リクエストをキャンセルします。
controller.abort()

CancelToken 非推奨
CancelTokenを使用して、リクエストをキャンセルすることができます。
axios cancel token APIは、取り下げられたcancelable promisesの提案に基づくものです。
このAPIはv0.22.0から非推奨となり、新しいプロジェクトでは使用しないでください。
CancelToken.sourceファクトリーを使用してキャンセルトークンを作成します。

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function (thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // エラーハンドリング
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// cancel the request (the message parameter is optional)
// リクエストをキャンセルします(メッセージパラメータはオプションです)
source.cancel('Operation canceled by the user.');

CancelTokenコンストラクターに実行関数を渡すことで、キャンセルトークンを作成することもできます。

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // 実行関数はパラメータとしてキャンセル関数を受け取ります。
    cancel = c;
  })
});

// cancel the request
// リクエストをキャンセルする
cancel();

NOTE:同じキャンセルトークン・シグナルで複数のリクエストをキャンセルすることができます。
移行期間中は、同じリクエストであっても、両方のキャンセルAPIを使用することができます。

const controller = new AbortController();

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token,
  signal: controller.signal
}).catch(function (thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // エラーハンドリング
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

source.cancel('Operation canceled by the user.');
// もしくは
controller.abort(); // メッセージパラメータはサポートされていません。

URL-Encoding Bodies

デフォルトでaxiosはJavaScriptオブジェクトをJSONにシリアライズする。
代わりにapplication/x-www-form-urlencoded形式でデータを送信するには、以下のオプションのいずれかを使用することができます。
Browser
URLSearchParams APIを使用することができます。

const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/foo', params);

URLSearchParamsはすべてのブラウザでサポートされているわけではありませんが、polyfillが用意されています。
あるいはqaライブラリを使用してデータをエンコードする

const qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 }));

もしくは、他の方法(ES6)

import qs from 'qs';
const data = { 'bar': 123 };
const options = {
  method: 'POST',
  headers: { 'content-type': 'application/x-www-form-urlencoded' },
  data: qs.stringify(data),
  url,
};
axios(options);

Node.js
Query string
Query stringモジュールを使用しています。

const querystring = require('querystring');
axios.post('<http://something.com/>', querystring.stringify({ foo: 'bar' }));

URLSearchParams
url moduleからURLSearchParamsを使用する。

const url = require('url');
const params = new url.URLSearchParams({ foo: 'bar' });
axios.post('<http://something.com/>', params.toString());

qs
ネストされたオブジェクトを文字列化する必要がある場合は、qsライブラリを使用することをお勧めします。
Form data
node.jsの場合はform-dataライブラリを使用できます。

const FormData = require('form-data');
 
const form = new FormData();
form.append('my_field', 'my value');
form.append('my_buffer', new Buffer(10));
form.append('my_file', fs.createReadStream('/foo/bar.jpg'));

axios.post('<https://example.com>', form, { headers: form.getHeaders() })

あるいは、interceptorを使用する。

axios.interceptors.request.use(config => {
  if (config.data instanceof FormData) {
    Object.assign(config.headers, config.data.getHeaders());
  }
  return config;
});

この記事が気に入ったらサポートをしてみませんか?