Description
Accessing and manipulating Zope’s HTTP request and response objects programmatically.
他のいくつかの Web フレームワークとは異なり、Plone では HTTP レスポンスのオブジェクトを明示的に作成したり応答することができません。 HTTP リクエストのオブジェクトは常に HTTP レスポンスのオブジェクトと関連付けられます。また、レスポンスのオブジェクトは、リクエストを Web サーバーが受け取ったときに生成されます。
レスポンスはリクエストが処理されている間中、常に利用可能です。 これは、処理コードのどの段階でもレスポンスのヘッダーを設定したり修正ができるということです。
通常 Plone は Zope の ZServer (Medusa として知られています) で動作します。 他には WSGI に対応した Repoze のような Web サーバーがあります。
Web サーバーは HTTP オブジェクトを構築する方法に影響を与える可能性があります。
全ての外から入ってくる HTTP リクエストは (Zope の) ZPublisher の HTTPRequest オブジェクトで覆われます。
通常は構成パラメーターとして探索されたコンテクストとともに、view 機能またはインスタンスは HTTP リクエストオブジェクトを受け取ります。
view では HTTP リクエストに以下のようにアクセスできます。:
from Products.Five.browser import BrowserView
class SampleView(BrowserView):
def __init__(context, request):
# Each view instance receives context and request as construction parameters
self.context = context
self.request = request
def __call__(self):
# Entry point of request processing
# Dump out incoming request variables
print self.request.items()
問い合わせられた URL を取得します。:
>>> request["ACTUAL_URL"]
'http://localhost:8080/site'
応答したオブジェクトの URL を取得できます。 デフォルトページとデフォルトビューの処理により、問い合わせ URL とは異なる場合があります。
>>> request["URL"]
'http://m.localhost:8080/site/matkailijallefolder/@@frontpage'
ノート
URLs, as accessed above, do not contain query string.
Unparsed query string can be accessed.
E.g. if you go to http://localhost:8080/site?something=foobar
>>> self.request["QUERY_STRING"]
'something=foobar'
Query string is an empty string if it’s not present in HTTP request.
ノート
You can use request.form dictionary to access parsed query string content.
リクエスト URI のパスは request.path から取得できます。 request.path はパスの構成要素のリストです。 request.path はバーチャルパスであり、サイト id は含まれていません。
例:
reconstructed_path = "/".join(request.path) # will be
他に以下のヘッダーから取得が可能です。:
('PATH_INFO', '/plonecommunity/Members')
('PATH_TRANSLATED', '/plonecommunity/Members')
PHP の REQUEST_URI に対応すると値は以下のようにすると取得できます。:
# Concatenate the user visible URL and query parameters
full_url = request.ACTUAL_URL + "?" + request.QUERY_STRING
parsed = urlparse.urlsplit(full_url)
# Extract path part and add the query if it existed
uri = parsed[2]
if parsed[3]:
uri += "?" + parsed[3]
詳細な情報は以下を参照して下さい。
例:
def get_ip(request):
""" Extract the client IP address from the HTTP request in proxy compatible way.
@return: IP address as a string or None if not available
"""
if "HTTP_X_FORWARDED_FOR" in request.environ:
# Virtual host
ip = request.environ["HTTP_X_FORWARDED_FOR"]
elif "HTTP_HOST" in request.environ:
# Non-virtualhost
ip = request.environ["REMOTE_ADDR"]
else:
# Unit test code?
ip = None
return ip
以下を参照してください。
REQUEST_METHOD が GET のときは HTTP GET 変数は request.form で取得できます。
例:
# http://yoursite.com/@@testview/?my_param_id=something
print self.request.form["my_param_id"]
HTTP POST 変数は request.form に辞書型で保存されます。:
print request.form.items() # Everything POST brought to us
GET と POST の変数へは同じようにアクセスできます。
HTTP ヘッダーは request.get_header() と request.environ 辞書で取得できます。
例:
referer = self.request.get_header("referer") # Page referer (the page from user came from)
if referer == None: # referer will be none if it was missing
pass
全てのヘッダーを出力します。:
for name, value in request.environ.items():
print "%s: %s" % (name, value)
Web サーバーの環境変数は request.other (ZServer) または request.environ (Repoze と他の WSGI ベースの Web サーバー) で参照できます。:
print request.other.items()
user_agent = request.other["HTTP_USER_AGENT"]
user_agent = request.environ["HTTP_USER_AGENT"] # WSGI or Repoze server
以下はバーチャルホストに対しても安全な方法で HTTP サーバーの名前をを取得する例です。
def get_host_name(request):
""" Extract host name in virtual host safe manner
@param request: HTTPRequest object, assumed contains environ dictionary
@return: Host DNS name, as requested by client. Lowercased, no port part.
Return None if host name is not present in HTTP request headers
(e.g. unit testing).
"""
if "HTTP_X_FORWARDED_HOST" in request.environ:
# Virtual host
host = request.environ["HTTP_X_FORWARDED_HOST"]
elif "HTTP_HOST" in request.environ:
# Direct client request
host = request.environ["HTTP_HOST"]
else:
return None
# separate to domain name and port sections
host=host.split(":")[0].lower()
return host
It is possible to extract Zope instance port from the request. This is useful e.g. for debugging purposes if you have multiple ZEO front ends running and you want to identify them easily.
port = request.get(“SERVER_PORT”, None)
ノート
SERVER_PORT variable returns port as a string, not integer.
ノート
This port number is not the one visible to the external traffic (port 80, HTTP)
GET、POST と Web の環境変数はリクエストオブジェクトにフラットに保存されており辞書形式で参照できます。:
# Comes from POST
request["input_username"] == request.form["input_username"]
# Comes from environ
request.get('HTTP_USER_AGENT') == request.environ["HTTP_USER_AGENT"]
HTTP リクエストオブジェクトを書き換えたり変数を追加しても、影響を与えることができません。 キャッシュ変数を作成する必要がある場合は、 annotations をリクエストのライフサイクルに使用します。 「TODO: annotaition の例を書いたら内部リンクを追加します。」
HTTP リクエストオブジェクトをつかみたいと思う場合がしばしばありますが、フレームワークの下では取得できません。 そのような場合にリクエストオブジェクトにアクセスする2種類の方法があります。
獲得を使用した HTTP リクエストを取得する例です。:
# context is any traversed Plone content item
request = getattr(context, "REQUEST", None)
if request is not None:
# Do the normal flow
pass
else:
# This code path was not initiated by an incoming web server request
# Handle cases like
# - command line scripts
# - add-on installer
# - code called during Zope start up
# -etc.
pass
通常 view から HTTP レスポンスを直接返すことはありません。 その代わりに既存の(リクエストに付属した)HTTP レスポンスオブジェクトを変更して、 HTTPレスポンスのペイロードを返します。
応答するペイロードオブジェクトは以下が使用できます。
リクエストを知っていれば HTTP レスポンスにアクセスできます。:
from Products.Five.browser import BrowserView
class SampleView(BrowserView):
def __init__(context, request):
# Each view instance receives context and request as construction parameters
self.context = context
self.request = request
def __call__(self):
response = self.request.response
return "<html><body>Hello world!</body></html>"
HTTPResponse の setHeader() メソッドを使用してヘッダーを設定します。:
# Response dynamically generated image
self.request.response.setHeader("Content-type", "image/jpeg")
return image_data
Content-Disposion ヘッダーはダウンロードのファイル名をセットするときに使用します。 Flash ダウンロードが有効かどうか確認するときに Flash 10 にも使用されます。
例としてダウンロード時にダウンロード対象となるファイル名を強制する方法を載せます。:
response = self.request.response
response.setHeader("Content-type", "text/x-vCard; charset=utf-8")
response.setHeader("Content-Transfer-Encoding", "8bit")
cd = 'attachment; filename=%s.vcf' % (context.id)
response.setHeader('Content-Disposition', cd)
その他の情報は以下を参照してください。
HTTPResponse.setStatus(self, status, reason=None, lock=None) を使用して HTTP の応答ステータス(404 Not Found、500 Internal error等)を設定します。
lock=True を指定した場合は HTTPResponse をさらに修正することはできずエラーを返さずに失敗して終了します。
Plone はすべてを探索して実行するように、ミドルウェアの概念がありません。 ミドルウェアのような振る舞いを 探索の前 のフックで真似ることができます。 探索の前のフックは探索グラフ中のどんな永続的なオブジェクトにもインストールすることが可能です。 フックは永続的であるので、データベースの変更と特別な GenericSetup の Python コードを使用してインストールしなければなりません。
警告
探索の前のフックは新しい HTTP レスポンスを作成できないか、代わりの HTTP レスポンスを返すことができません。 HTTP のリダイレクトのような、例外的な HTTP レスポンスの変更のみに対応しています。 レスポンス全体を書き換えたい場合は出力後のフックを使用する必要があります。
より詳細な情報は以下を参照してください。
例
出力後のフックは以下の時に実行されます。
これはキャッシュを行うために役立ちます。 レスポンスにキャッシュ用のヘッダーの内容を決定して挿入する理想的な場所です。
詳細は plone.postpublicationhook パッケージのページ を読んでください。
例:
from zope.component import adapter, getUtility, getMultiAdapter
from plone.postpublicationhook.interfaces import IAfterPublicationEvent
from Products.CMFCore.interfaces import IContentish
def get_contentish(object):
"""
Traverse acquisition upwards until we get contentish object used for the HTTP response.
"""
contentish = object
while not IContentish.providedBy(contentish):
if hasattr(contentish, "aq_parent"):
contentish = contentish.aq_parent
else:
break
return contentish
# This must be refered in configure.zcml
@adapter(Interface, IAfterPublicationEvent)
def language_fixer(object, event):
""" Redirect mobile users to mobile site using gomobile.mobile.interfaces.IRedirector.
Note: Plone does not provide a good hook doing this before traversing, so we must
do it in post-publication. This adds extra latency, but is doable.
"""
request = event.request
response = request.response
# object can be a page template, view, whichever happens to be at the very end of traversed acquisition chain
context = get_contentish(object)