node.js で URLパースすると favicon.ico が返る

node.js で URLパースすると favicon.ico が返る

はてなのブログカードはたとえ自サイトであっても別ウィンドウ(タブ)で開いてしまいますので、それを同じウィンドウで開くようにしようと思っているのですが、DOM操作ではうまくいかず、じゃあ、サーバーを立ち上げて、呼ばれたら指定のサイトから OGPデータを取得してブログカード用にコーディングして返せばいいんじゃないかということで試してみようと思います。

01node.js でサーバーを立ち上げる

ググればいっぱいヒットします。簡単ですね。

const http = require('http');
const server = http.createServer();
const fs = require('fs');


server.on('request', function(req, res){
  
  fs.readFile('./index.html', 'utf-8', function(err, data){
    res.writeHead(200, {'Content-Type' : 'text/html'});
    res.write(data);
    res.end();
  });


});


server.listen(8080);
console.log('Server running at http://localhost:8080/');

エラー処理も一切なしのチェック用ですが、同じディレクトリに

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Sample HTML</title>
</head>
<body>
    <h1>Hello World!</h1>
    <!-- 将来の文字列置き換え用 -->
    <p>@@content@@</p>
</body>
</html>

として、index.html を置いておき、コンソールから

$ node server.js 
Server running at http://localhost:8080/

として、ブラウザで http://localhost:8080/ にアクセスすれば、Hello World! と表示されます。

02URL をパースする

で、やりたいことは、ブログカードにしたいサイトのアドレスをクエリパラメータとして送信し、そのアドレスから OGPデータを取得して、ブログカード用に整形して返すことですから、まずはクエリパラメータを取り出さなくてはいけません。

URLモジュールを使えばできるようで、あれこれやってみたのですがなかなかうまくいかず、どういうことだろうと次のコードでサーバーを立ち上げ、ブラウザから http://localhost:8080/?url=https://www.movieimpressions.com/entry/transit と打ち URLパースしてみました。

const http = require('http');
const server = http.createServer();
const urlParser = require('url');


server.on('request', function(req, res){
  console.log(urlParser.parse(req.url));
  res.end();
});


server.listen(8080);
console.log('Server running at http://localhost:8080/');

そうしましたら、何と、リクエストURLだけではなく、/favicon.ico が返ってくるではありませんか!?

Server running at http://localhost:8080/
Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: '?url=https://www.movieimpressions.com/entry/transit',
  query: 'url=https://www.movieimpressions.com/entry/transit',
  pathname: '/',
  path: '/?url=https://www.movieimpressions.com/entry/transit',
  href: '/?url=https://www.movieimpressions.com/entry/transit' }
Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: null,
  query: null,
  pathname: '/favicon.ico',
  path: '/favicon.ico',
href: '/favicon.ico' }

ただ、これは Chromeの場合で、FirefoxではリクエストURLだけで favicon.icoは返ってきません。

いろいろググってもなぜブラウザによって違うのかははっきりせず、対処法としては、

const http = require('http');
const server = http.createServer();
const urlParser = require('url');


server.on('request', function(req, res){
  
  if (req.url === '/favicon.ico') {
    console.log('favicon requested');
    res.writeHead(200, {'Content-Type': 'image/x-icon'} );
    res.end();
  }else{
    console.log(urlParser.parse(req.url));
    res.end();
  }
});


server.listen(8080);
console.log('Server running at http://localhost:8080/');

などと切り分ければOKということのようです。

そうしますとこんなんが返ります。

$ node server.js 
Server running at http://localhost:8080/
Url {
  protocol: null,
  slashes: null,
  auth: null,
  host: null,
  port: null,
  hostname: null,
  hash: null,
  search: '?url=https://www.movieimpressions.com/entry/transit',
  query: 'url=https://www.movieimpressions.com/entry/transit',
  pathname: '/',
  path: '/?url=https://www.movieimpressions.com/entry/transit',
  href: '/?url=https://www.movieimpressions.com/entry/transit' }
favicon requested

結構長い間悩んでいましたがこれで次へ進めそうです(笑)。