LoginSignup
3
4

More than 5 years have passed since last update.

React+Node.js+axios+jsdomでTDDしようとしたらCORS問題にぶつかった話

Last updated at Posted at 2017-11-22

Access-Control-Allow-Origin とかでエラーになるあれです。
React+Node.js+axios+jsdomでRequest投げた場合に発生しました。

ぱっと見の原因は、jsdomの作成時にURLが設定されていない場合、originに文字列で "null" が入ってしまい、jsdomのオリジンチェックで、文字列じゃない null(null !== "null") という感じで比較してエラー判定しているからだと思いました。

ここの関数で比較しています

そして、文字列の "null" が入るのはバグじゃないか?と調べてみたのですが、npmモジュールのwhatwg-urlのこの辺で、文字列nullが正しい的な記述がある。

古川さんのissueでも正しいように見えると答えているので、whatwgの仕様が気になったのですが、ちょうどNode.jsが実装したみたいな記事があったため試してみた。
(原典に当たれとか言われそうですが、今回やりたいのはCORS問題の解決なのでそこは捨てました。)

確認したいのは、originが存在しない場合に文字列nullが返ってくるのかどうか?です。ローカルのファイルアクセス用URLを使えばoriginが設定されていない状況になるので、それで試しました。

node -e 'const URL = require("url").URL; console.log(new URL("file://aaa.log"))'
URL {
  href: 'file://aaa.log/',
  origin: 'null',
  protocol: 'file:',
  username: '',
  password: '',
  host: 'aaa.log',
  hostname: 'aaa.log',
  port: '',
  pathname: '/',
  search: '',
  searchParams: URLSearchParams {},
  hash: '' }

結果、文字列"null"が帰ってくるという。(意外)

そもそもaxiosでエラーを投げるのは、CORS違反だからで、それ自体は正しい挙動だと思い直しました。

そして、originが設定されていないという状況がイレギュラーなんじゃないか?(Chromeの別タブ状態でXHRリクエスト投げた場合でも、originはgoogleのやつが入っていたりする。)

ということで、jsdomの生成時にURLを設定して試してみました。

// setup用のjs

require('babel-register')()

var JSDOM = require('jsdom').JSDOM
var dom = new JSDOM(`...`, {
  url: `localhost:3000` // ←ここ!!
})

global.window =  dom.window
global.document = dom.window.document

var exposedProperties = ['window', 'navigator', 'document']
Object.keys(document.defaultView).forEach((property) => {
  if (typeof global[property] === 'undefined') {
    exposedProperties.push(property)
    global[property] = document.defaultView[property]
  }
})

global.navigator = {
  userAgent: 'node.js'
}

結果おk。

これで localhost:8080で立ち上がっているWebサーバにリクエストを投げてもエラーは発生しなかったです。ちなみにlocalhost指定だと駄目で、localhost:3000とかポートまで指定するとうまくいくという謎現象もありました。。。

また、global.window.document.origin の値が使われていたので、そいつを上書きすれば解決か?とか思ったのですが、set出来ない書き方をしていたため、変更できずでした。(個人的に分かりづらいので、この書き方は嫌いです。)

// set出来なくする書き方。。
// react-tdd/node_modules/jsdom/lib/jsdom/living/generated/Document.js#L798

Object.defineProperty(Document.prototype, "origin", {
  get() {
    if (!this || !module.exports.is(this)) {
      throw new TypeError("Illegal invocation");
    }

    return this[impl]["origin"];
  },

  enumerable: true,
  configurable: true
});

npm iしたあとに作成されるgeneratedフォルダ配下なので、npm iしたあとの中身で見ないと見れないかもです(jsdom@11.4.0

githubのリンクを貼れないからそれもやだなぁとか、結構調べるのに骨が折れたので共有でした。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4