10:Webリクエストとレスポンスの処理(10: Handling Web Requests and Responses)

Webアプリケーションは受信したリクエストを処理してレスポンスを返します。Pyramidはリクエストとレスポンスを便利で信頼できるものにします

目的(Objectives)

  • Pyramidnのリクエストリクエストとレスポンスの選択についての背景を学びます。
  • リクエストからデータを取得します。
  • レスポンスヘッダーの情報を変更します。

背景(Background)

Web開発とはWebのリクエストを処理することをです。これはWebアプリケーションの重要な部分であるため、Web開発者はWebリクエストとWebレスポンスを返すための堅牢で成熟したソフトウェアセットが必要です。

PyramidはPythonのWeb開発の世界(仮想環境、パッケージジング、Cookiecutter、Python3系を最初に採用するなど)に上手に適合しています。Pyramidはリクエストとレスポンスの処理のために評価がよいPythonライブラリの WebOb に目を向けました。前回の例の requesthello_world についてはPyramidの based on WebOb に基づきます。

手順(Steps)

  1. 最初に、view_classes での結果をコピーします:

    $ cd ..; cp -r view_classes request_response; cd request_response
    $ $VENV/bin/pip install -e .
    
  2. request_response/tutorial/__init__.py でルートを簡略化します:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    from pyramid.config import Configurator
    
    
    def main(global_config, **settings):
        config = Configurator(settings=settings)
        config.add_route('home', '/')
        config.add_route('plain', '/plain')
        config.scan('.views')
        return config.make_wsgi_app()
    
  3. request_response/tutorial/views.py のビューが必要です:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    from pyramid.httpexceptions import HTTPFound
    from pyramid.response import Response
    from pyramid.view import view_config
    
    
    class TutorialViews:
        def __init__(self, request):
            self.request = request
    
        @view_config(route_name='home')
        def home(self):
            return HTTPFound(location='/plain')
    
        @view_config(route_name='plain')
        def plain(self):
            name = self.request.params.get('name', 'No Name Provided')
    
            body = 'URL %s with name: %s' % (self.request.url, name)
            return Response(
                content_type='text/plain',
                body=body
            )
    
  4. request_response/tutorial/tests.py` のテストを更新します:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    import unittest
    
    from pyramid import testing
    
    
    class TutorialViewTests(unittest.TestCase):
        def setUp(self):
            self.config = testing.setUp()
    
        def tearDown(self):
            testing.tearDown()
    
        def test_home(self):
            from .views import TutorialViews
    
            request = testing.DummyRequest()
            inst = TutorialViews(request)
            response = inst.home()
            self.assertEqual(response.status, '302 Found')
    
        def test_plain_without_name(self):
            from .views import TutorialViews
    
            request = testing.DummyRequest()
            inst = TutorialViews(request)
            response = inst.plain()
            self.assertIn(b'No Name Provided', response.body)
    
        def test_plain_with_name(self):
            from .views import TutorialViews
    
            request = testing.DummyRequest()
            request.GET['name'] = 'Jane Doe'
            inst = TutorialViews(request)
            response = inst.plain()
            self.assertIn(b'Jane Doe', response.body)
    
    
    class TutorialFunctionalTests(unittest.TestCase):
        def setUp(self):
            from tutorial import main
    
            app = main({})
            from webtest import TestApp
    
            self.testapp = TestApp(app)
    
        def test_plain_without_name(self):
            res = self.testapp.get('/plain', status=200)
            self.assertIn(b'No Name Provided', res.body)
    
        def test_plain_with_name(self):
            res = self.testapp.get('/plain?name=Jane%20Doe', status=200)
            self.assertIn(b'Jane Doe', res.body)
    
  5. テストを実行します:

    $ $VENV/bin/py.test tutorial/tests.py -q
    .....
    5 passed in 0.30 seconds
    
  6. Pyramidアプリケーションを以下のように実行します:

    $ $VENV/bin/pserve development.ini --reload
    
  7. ブラウザで http://localhost:6543/ を開きます。 http://localhost:6543/plain にリダイレクトされます。

  8. ブラウザで http://localhost:6543/plain?name=alice を開きます。

分析(Analysis)

今回のビュークラスでは2つのルートと2つのビューがあり、最初の方はHTTPリダイレクトで2番目のビューにつながります。Pyramidはビューから特別なオブジェクトを返すか、特別な例外を発生させることによって、 generate redirects を生成できます。

今回のPyramidのビューは、request.url からアクセスしたURLを取得します。また、http://localhost:6543/plain?name=alice にアクセスした場合は、nameはレスポンスボディに含まれます:

URL http://localhost:6543/plain?name=alice with name: alice

最後にレスポンスのコンテンツタイプとボディを設定してレスポンスを返します。

ユニットと機能テストを更新して、コードがリダイレクトを行うことを証明しましたが、 /plain?name は送信した/されなかったのでしょうか?

エクストラクレジット(Extra credit)

  1. ルーティングを返すのではなく raise HTTPFound(location='/plain') でもできますか?もしそうなら、違いは何ですか?