はじめに
Flaskを使っているときにGETやPOSTで想定通りのパラメータを取得することができず1時間くらい右往左往したため、忘記録として記述しておく。
問題が起きたプログラム
問題となったのは、パラメータlで受け取ったURLにリダイレクトするだけの以下のAPI。
なお粗末なプログラムですが、GitHubで公開しているので興味のある方はどうぞ。
import string
from flask import Flask, request
HTML = '''\
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>redirect</title>
<meta http-equiv="refresh" content="0; URL='${link}'" />
</head>
<body>
このページは自動的に転送されます。<br>
転送されない場合は<a href="${link}">こちら</a>をクリックしてください
</body>
</html>
'''
app = Flask(__name__)
@app.route('/')
def index():
link = request.args.get('l', '')
assert link, 'Cannot get l parameter querystring'
return string.Template(HTML).substitute({'link': link})
上記のWebアプリをhtml://hoge.com/r/
に配置して、パラメータlに適当なURLを与えてやる。たとえば、次のような感じで使うことを想定している。
http://hoge.com/r/?l=http:fuga.com/
問題と原因
パラメータlに色々なURLを放り込んでみるが、クエリを持つURLを与えると思った通りのリダイレクトをしてくれない。たとえば、以下のような感じ。
html://hoge.com/r/?l=http:fuga.com/?q=1&date=20190501
問題は明らかで、与えたl内に?や&が入っていること。解決するにはなんとかこれを消してやればよい。
replaceなどの力技も可能だが、極力スマートにやりたい。(あとAPI側をいじりたくない
解決策
リダイレクト先のURLを作ったあとにurllib.parse.quote
をかけてやることで解決する。
import urllib
target = 'http:fuga.com/?q=1&date=20190501'
return 'html://hoge.com/r/?l=' + urllib.parse.quote(target)
これでlで指定したURL(パラメータ付き)に正しくアクセスできる。
あとAPI側での修正は必要ない。よしなにデコードしてくれるようだ。
おまけ
実はこのurllib.parse.quote
は非常に便利で、たとえば、クロールしてきたWebページを保存するときに、そのURLをエンコードしてやることでファイル名として扱えたりできる。