かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JavaScript axios Content-Type の設定にはハマる

axios で express のAPI にリクエストを投げていて何故かうまく値が取れなくてハマってしまったのでメモ。

Express のAPI

const express = require('express');
const app = express();
const Joi = require('@hapi/joi');

const validation = (data) => {
  const schema = Joi.object({
    email: Joi.string().required().email(),
    password: Joi.string().min(6).required(),
  });
  return schema.validate(data, { abortEarly: false });
};

app.post('/api/login', (req, res) => {
  const { error } = validation(data);
  if( error ) {
    res.json({
      message: 'welcome to the API'
    });
  } else {
    const errorMessages = error.details.map((data) => data.message);
    res.status(400).send(errorMessages.join("\n"));
  }
});

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.listen(3000, () => console.log('Server started !'));

問題のあったコード

import axios from 'axios';

axios.defaults.headers.common['content-type'] = 'application/json';

const getPosts = await (data) => {
  try {
    const res = await axios.post(API_SIGNUP, data);
    return res;
  } catch (err) {
    throw err;
  }
};

API を叩くので常に content-type: application/json にしたかったので default.headers で設定したのですが email is required, password is required とPOSTしているはずのデータがバリエーションで弾かれるエラーになって返ってくるばかりでした。

POST したデータは Express では req.body に入ってくるので、これを console.log してみたところ空オブジェクト{} になっていました。(POST した筈のデータがが消えている… なーぜー?)

GET / POST メソッドの headers オプションで content-type を設定すると res.body でデータが取れ問題なく動作する

import axios from 'axios';

const getPosts = await (data) => {
  try {
    const res = await axios.post(API_SIGNUP, data, {
      headers: {
        content-type: 'application/json',
      }
    });
    return res;
  } catch (err) {
    throw err;
  }
};

Request Header に違いがあった

GET / POST の headers オプションで content-type を指定すれば問題なく動作していたことから、 API 側ではなくフロントの送り方に問題があるのではと推察しました。

送信されるリクエストを見比べていたところ Request Headersの content type に違いが出ていました。

axios.defaults.headers.common['content-type'] = 'application/json'; で送信したリクエス

[content-type]: application/json, application/json;charset=utf-8

axios.post(url, data, { headers: { 'content-type': 'application/json' } }) で送信したリクエスト 

[content-type]: application/json

defaults.headers オプションで送信した Request Header の Content-Type は axios が自動的に application/json;charset=utf-8 を付けているようで application/json が二重になっています。🤔 🤔 🤔

Defaults.headers で Content-Type を設定する時は Content-Type でなければならない

defaults.headers で設定していた Content-Type のキーが content-type と小文字になっていたことが問題でした。

🙅

axios.defaults.headers.common['content-type'] = 'application/json';
  • [Request Header]: application/json, application/json;charset=utf-8
  • req.body => {}

🙆

axios.defaults.headers.common['Content-Type'] = 'application/json';
  • [Request Header]: application/json
  • req.body => { foo: bar, … }

🙅

axios.defaults.headers.common['Content-type'] = 'application/json';
  • [Request Header]: application/json, application/json;charset=utf-8
  • req.body => {}

🙅

axios.defaults.headers.common['content-Type'] = 'application/json';
  • [Request Header]: application/json, application/json;charset=utf-8
  • req.body => {}

default.headers オプションで Content-Type を設定する時は C と T が大文字の Content-Type でなければ application/json;charset=utf-8 が追加で付けられてしまうようです。

GET / POST のオプションの方は大文字小文字の区別なく受け入れられる

🙆

axios.post(url, data, {headers: {'content-type': 'application/json'}});
  • [Request Header]: application/json;char
  • req.body => { foo: bar, … }

🙆

axios.post(url, data, {headers: {'Content-Type': 'application/json'}});
  • [Request Header]: application/json;char
  • req.body => { foo: bar, … }

🙆

axios.post(url, data, {headers: {'Content-type': 'application/json'}});
  • [Request Header]: application/json;char
  • req.body => { foo: bar, … }

🙆

axios.post(url, data, {headers: {'content-Type': 'application/json'}});
  • [Request Header]: application/json;char
  • req.body => { foo: bar, … }

恐らく toLowerCase() されているのだと思います。 headers: { 'cOntEnt-type': 'application/json' } みたいな設定にしても問題ありませんでした。

 
もしかしたら Express の express.json()type オプションを上手く変えれば問題なかったのかもしれませんが、axios の default.header オプションでの Content-Type の設定は "Content-Type" という固定の文字列でしか設定できない仕様になっているのが原因だったようです。

あーハマった!


基礎から学ぶ Vue.js

基礎から学ぶ Vue.js

  • 作者:mio
  • 発売日: 2018/05/29
  • メディア: 単行本(ソフトカバー)

Axios: Celebrating the Hymns of Greek Orthodox America

Axios: Celebrating the Hymns of Greek Orthodox America

  • 発売日: 2011/12/22
  • メディア: MP3 ダウンロード
AXIOS ってギリシャ語で正教会の用語でもあるんですね〜 (こうやってムダ知識ばかりつく)