[Node.js] 新旧APIでのURLパースの違い
[Node.js] 新旧APIでのURLパースの違い:
千 Advent Calendar 2018の19日目の記事です。
Node.jsでurlのパースをするには2通りの方法があると知り、それぞれ何が違うのか調べてみた内容を書きたいと思います。
Electronアプリでaws-sdkをプロキシに対応させるために、以下のようなことをしていました。
そこでurlのパースを行なったときに直面したお話です。
プロキシ対応の内容はこちらとだいたい同じです。
→ Using the AWS SDK for JavaScript from Behind a Proxy
URLのパースのために、
を使っていた所、これはLegacy APIなので↓が良いのでは?とご指摘を受けました。
やってることは同じパースなら書き換えても変わらないだろうと思ったのですが、やってみると何やら挙動が変わりました。
まず、
次に、
大体は同じですが、Legacy APIの方では
どちらも
それぞれでポート番号を変更してみます。
ですが、
まずは
次に
結構違いがあります。
Symbolについてはこちらの記事を読みました。→ ECMAScript6にシンボルができた理由
上の記事で
https-proxy-agentというモジュールを使っていたのですが、このモジュールでは引数で渡したurlオブジェクトに対して
https://github.com/TooTallNate/node-https-proxy-agent/blob/11bc3478fdff1eec108cde4607bf10298be65ca3/index.js#L35
Node.jsのドキュメントを読んで、urlモジュールにはLegacy API と Node.js 8.0で正式採用されたWHATWG API の2つのAPIがあるとわかりました。
新規のアプリケーションはWHATWG APIの使用を推奨していました。
初のQiita投稿でしどろもどろな感じですが、おかしなところは随時修正していきます…(;´Д`)
千 Advent Calendar 2018の19日目の記事です。
Node.jsでurlのパースをするには2通りの方法があると知り、それぞれ何が違うのか調べてみた内容を書きたいと思います。
背景
Electronアプリでaws-sdkをプロキシに対応させるために、以下のようなことをしていました。そこでurlのパースを行なったときに直面したお話です。
const AWS = require('aws-sdk') const url = require('url') const HttpsProxyAgent = require('https-proxy-agent') const proxyUrl = url.parse('http://proxyurl:8080') proxyUrl.rejectUnauthorized = false // 証明書によるエラーを無視 AWS.config.update({ httpOptions: { agent: HttpsProxyAgent(proxyUrl) } })
→ Using the AWS SDK for JavaScript from Behind a Proxy
url.parse()
と new URL()
URLのパースのために、url.parse('http://proxyurl:8080')
new URL('http://proxyurl:8080')
2つのパースはどう違うのか
まず、url.parse()
の結果はこうなります。> url.parse('http://proxyurl:8080') Url { protocol: 'http:', slashes: true, auth: null, host: 'proxyurl:8080', port: '8080', hostname: 'proxyurl', hash: null, search: null, query: null, pathname: '/', path: '/', href: 'http://proxyurl:8080/' }
new URL()
はこうです。> new URL('http://proxyurl:8080') URL { href: 'http://proxyurl:8080/', origin: 'http://proxyurl:8080', protocol: 'http:', username: '', password: '', host: 'proxyurl:8080', hostname: 'proxyurl', port: '8080', pathname: '/', search: '', searchParams: URLSearchParams {}, hash: '' }
auth
だったのが、WHATWG APIではusername
とpassword
に別れたりと若干違います。どちらも
hoge
という変数に代入するとhoge.protocol
で'http:'
の結果が得られます。
プロパティの変更
それぞれでポート番号を変更してみます。> hoge = url.parse('http://proxyurl:8080') > hoge.port = '9090' > hoge Url { protocol: 'http:', slashes: true, auth: null, host: 'proxyurl:8080', port: '9090', hostname: 'proxyurl', hash: null, search: null, query: null, pathname: '/', path: '/', href: 'http://proxyurl:8080/' }
url.parse()
は変更すると、portが指定通り変わりました。new URL()
の方は、変更すると他のプロパティに表示されるポート番号も合わせて変わりました。> hoge = new URL('http://proxyurl:8080') > hoge.port = '9090' > hoge URL { href: 'http://proxyurl:9090/', origin: 'http://proxyurl:9090', protocol: 'http:', username: '', password: '', host: 'proxyurl:9090', hostname: 'proxyurl', port: '9090', pathname: '/', search: '', searchParams: URLSearchParams {}, hash: '' }
プロパティの削除
url.parse()
の場合> hoge = url.parse('http://proxyurl:8080') > delete hoge.port // true > hoge // portは削除されている Url { protocol: 'http:', slashes: true, auth: null, host: 'proxyurl:8080', hostname: 'proxyurl', hash: null, search: null, query: null, pathname: '/', path: '/', href: 'http://proxyurl:8080/' }
new URL()
の場合> hoge = new URL('http://proxyurl:8080') > delete hoge.port // true > hoge // portは残ったまま URL { href: 'http://proxyurl:8080/', origin: 'http://proxyurl:8080', protocol: 'http:', username: '', password: '', host: 'proxyurl:8080', hostname: 'proxyurl', port: '8080', pathname: '/', search: '', searchParams: URLSearchParams {}, hash: '' }
delete hoge.xxx
の結果はいずれもtrueが返ります。ですが、
new URL()
の方は実際には削除されません。
Object.assign()
の結果
まずはurl.parse()
> huga = Object.assign({}, url.parse('http://proxyurl:8080')) { protocol: 'http:', slashes: true, auth: null, host: 'proxyurl:8080', port: '8080', hostname: 'proxyurl', hash: null, search: null, query: null, pathname: '/', path: '/', href: 'http://proxyurl:8080/' }
new URL()
> huga = Object.assign({}, new URL('http://proxyurl:8080')) { [Symbol(context)]: URLContext { flags: 400, scheme: 'http:', username: '', password: '', host: 'proxyurl', port: 8080, path: [ '' ], query: null, fragment: null }, [Symbol(query)]: URLSearchParams {} }
Symbol()
ってなに…?と思い調べて、Symbol型というものの存在を知りました。Symbolについてはこちらの記事を読みました。→ ECMAScript6にシンボルができた理由
上の記事で
シンボルをキーとしてオブジェクトに保存された値は、一般的な for ... in ループで列挙されない。作成したシンボル(つまり上記のfoo)をスコープの外に出るなどして忘れてしまったら、既存の方法で値を参照することは二度と不可能になる。と書かれている通り、キーとなるシンボル
Symbol(context)
を参照できないと、このオブジェクトの値を参照できなくなってしまうようです。
なぜ挙動が変わったのか
https-proxy-agentというモジュールを使っていたのですが、このモジュールでは引数で渡したurlオブジェクトに対してObject.assign()
していました。https://github.com/TooTallNate/node-https-proxy-agent/blob/11bc3478fdff1eec108cde4607bf10298be65ca3/index.js#L35
url.parse()
であれば問題は無いのですが、new URL()
だとObject.assign()
後に値を参照できなくなり、得られる結果が変わってしまったのが原因でした。
Legacy API と WHATWG API
Node.jsのドキュメントを読んで、urlモジュールにはLegacy API と Node.js 8.0で正式採用されたWHATWG API の2つのAPIがあるとわかりました。url.parse()
はLegacy API、new URL()
はWHATWG APIの書き方だそうです。The url module provides two APIs for working with URLs: a legacy API that is Node.js specific, and a newer API that implements the same WHATWG URL Standard used by web browsers.Legacy APIの方は、廃止されてはいませんが既存アプリケーションの下位互換性のために残っているようです。
新規のアプリケーションはWHATWG APIの使用を推奨していました。
While the Legacy API has not been deprecated, it is maintained solely for backwards compatibility with existing applications. New application code should use the WHATWG API.なので今回は、https-proxy-agentとの互換性のために
url.parse()
を使うことにしました。
コメント
コメントを投稿