目次
この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。
こんにちは。いくたです。
以前、ブログなどの更新情報をキャッチしてtwitterやslackに投稿するボットを作ったのですが、ひとつ問題が生じました。
それは空白文字を含むURLのデコード問題です。
エンコードとデコード、どっちがどっちだっけ?
よく、どっちがどっちか分からなくなるのでおさらいです。
こんにちは。いくたです。
↓ エンコード%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF%E3%80%82%E3%81%84%E3%81%8F%E3%81%9F%E3%81%A7%E3%81%99%E3%80%82
↓ デコードこんにちは。いくたです。
空白文字を含むURLをデコードすると…
日本語URLはそのページの内容が推察できるので好きなのですが、エンコードされていると長い暗号のような見た目になってしまいます。そのため日本語URLが含まれそうな場合には、デコードの処理を積極的にしています。
しかし、空白文字を含むURLをデコードすると少々問題があるようです。
例えば、Dorubyのこの記事のURL(https://doruby.jp/users/kurokawa/entries/番外編 「子供がスマホを持つことについて」)は全角スペース” ”を含むため、日本語のURLにデコードしたしたものをそのままslackに投稿すると…
このように、スペースのところでURLが途切れてしまっています。このままリンクをクリックすると存在しないページhttps://doruby.jp/users/kurokawa/entries/番外編
に飛んでしてしまいます。
自作のボットではRubyのURIモジュールからdecode_www_form_component
メソッドを使ってデコードしています。詳しいメソッドの使い方はリファレンスを参考にしてください。
2.4.1 :001 > require 'uri'
=> true
2.4.1 :002 > URI.decode_www_form_component("%E3%80%80")
=> " "
全角スペースだけをエンコードされた文字に変換
decode_www_form_component
メソッドを使ってデコードするときに全角スペースだけはデコード対象から除外するようにすれば、今回の問題は発生しません。しかし、除外するいい方法が見つからなかったので、デコードした後で全角スペース” ”だけをエンコードされた文字%E3%80%80
に置き換えました。
(だいぶ力技なのでもっとスマートな方法があれば教えていただきたいです…)
require 'uri'
url = "https://doruby.jp/users/kurokawa/entries/%E7%95%AA%E5%A4%96%E7%B7%A8%E3%80%80%E3%80%8C%E5%AD%90%E4%BE%9B%E3%81%8C%E3%82%B9%E3%83%9E%E3%83%9B%E3%82%92%E6%8C%81%E3%81%A4%E3%81%93%E3%81%A8%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%80%8D"
decode_url = URI.decode_www_form_component(url)
# => "https://doruby.jp/users/kurokawa/entries/番外編 「子供がスマホを持つことについて」"
safe_url = decode_url.gsub(/ /,"%E3%80%80")
# => "https://doruby.jp/users/kurokawa/entries/番外編%E3%80%80「子供がスマホを持つことについて」"
これで全角スペースのみエンコードされたURLを投稿することができ、正しいリンクで投稿することができました。
https://doruby.jp/users/kurokawa/entries/番外編%E3%80%80「子供がスマホを持つことについて」
半角スペースとタブ文字もエンコード文字に変換
デコードしたURLが途中で途切れてしまう現象は半角スペース” “やタブ文字”\t”でも現れます。それぞれエンコード文字に置き換えるメソッドを作りました。
require 'uri'
# 全角スペース/半角スペース/タブ文字をエンコード文字に置き換え
def gsub_space(url)
url.gsub(/ /,"%E3%80%80").gsub(/ /,"%20").gsub(/\t/,"%09")
end
url = "https://doruby.jp/users/yamana/entries/%09-Photoshop%E3%81%AE%E4%BE%BF%E5%88%A9%E6%A9%9F%E8%83%BD%E2%91%A1"
decode_url = URI.decode_www_form_component(url)
# => "https://doruby.jp/users/yamana/entries/\t-Photoshopの便利機能②"
safe_url = gsub_space(decode_url)
# => "https://doruby.jp/users/yamana/entries/%09-Photoshopの便利機能②"
一部分だけエンコードされたURLについて
ブラウザによっては一部分だけエンコードされたURLを受け付けないかもしれません。chromeとsafariでは受け付けてくれたので、最近のブラウザなら問題なく処理してくれそうではあります。
そもそも「一部分だけエンコードされたURL is ちょっと気持ち悪い」という意見もあるようです。うーん…確かにちょっと気になるかも…
時間のあるときにslackのIncoming Webhooksの仕様を読み込んでタイトルのテキストリンクとして投稿する方針に転換してもいいかもしれません。
まとめ
今回のような問題があるのでURLをエンコード/デコードをする際は空白文字に気を遣う必要がありそうです。
それでは、今日はこの辺で。
読んでいただき、ありがとうございました。