webapp2にてhttpのheadメソッドに対応してみた。
2013年05月07日
最近、アクセスログを見ていると、HEADメソッドのリクエストがあることに気が付きました。ほぼロボット検索の様なのです。
どうやら、リンク先がの有無を調べる時などに利用されているようです。
実は、今回調べるまでHAEDメソッドの存在すら知らなかったのですが、GETメソッドと同じだがbodyを返さない仕様らしく、データの送受信量を減らすことが目的のようです。
しかも、HTTP/1.1では、GETとHEADは実装しなければいけない。という記載も見受けられます。
ちなみに、僕の管理サイトでは、googlebotからのHEADメソッドは、2013年5月2日から入ってくるようになっていました。
そんな感じで、googleさんからのリクエストにはちゃんと答えるべきだろう。と思って、実装方法を考えてみました。
以下は、Google App Engine for Pythonで標準となっているwebapp2での実装になります。
基本的には、GETと同じ内容で、生成されたresponseからbodyを削除するというのが基本的な考え方でコーディングしてみました。
結果、こんな感じのコードになりました。
class HomeHandler(webapp2.RequestHandler):
def get(self):
self.response.out.write(u'レスポンスコンテンツ')
def head(self):
self.get()
# bodyクリア前のレングスを取得
content_length = self.response.headers['Content-Length']
self.response.out.clear()
# bodyクリア前のレングスをセット
self.response.headers['Content-Length'] = content_length
# 動作確認用にログを出力
logging.info(self.response)
def get(self)は、通常通りコンテンツを作成します。
def head(self)を追加し、def get(self)を呼び出しています。
getで作成したresponseのheadersにセットされたContent-Lengthを保存してから、self.response.out.clear()でbodyをクリアしています。
その後保存したContent-Lengthをセットし直しています。
再度セットしなおさないと、bodyをクリアした際にContent-Lengthがクリアされてしまいました。
ちなみに、コードのテストはheadメソッドを生成するのが面倒だったので、def getにhead用のロジックを入れて確認しました。
本当は、UnitTestを書いてテストするべきなのでしょうが、ちょっとサボりました。。
localで確認する場合には、ターミナルでtelnetを使ってリクエストを出すと簡単かもしれません。その方法は、こちらのサイトを参考にさせて頂きました。
ロボット検索が意図したレスポンスになっているかは、良く分かりません。。。
----2013/5/10 追記-----
ここまで来たら、当然共通ロジックにした方がいいですよね。というか、するべきですよね。
webapp2は、get,post,headのそれぞれのメソッドを個別にコーディングしなければ行けないのですが、postも特別コーディング指定なければ、getと同じ動きをしてくれる方が便利ですよね。僕が今まで使っていたJavaのフレームワークとかも意識せずにその動きをしていたので、共通ロジックに入れてみました。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import webapp2
import logging
class ComRequestHandler(webapp2.RequestHandler):
def get(self, *args, **kwargs):
self.abort(405)
def post(self, *args, **kwargs):
self.get(*args, **kwargs)
def head(self, *args, **kwargs):
self.get(*args, **kwargs)
logging.info(self.response.headers)
content_length = self.response.headers['Content-Length']
self.response.out.clear()
self.response.headers['Content-Length'] = content_length
logging.info(self.response)
こんな感じになりました。loggingはデバッグ用です。getで405を返すようにしているのは、当クラス内でgetを定義した方が分かりやすいかと思ったのと、継承先でgetを実装していない場合に、405を返すべきかと思ったのでこんな感じになりました。
もっと、いい方法はあると思いますけど、これからもう少し勉強してみます。