PerlでAtomPubサーバを作ろう!

第3回 AtomPubをより効果的に ─ 認証・キャッシュなど

この記事を読むのに必要な時間:およそ 6 分

フィードのページング

前回までに実装したAtomPubサーバでは,コレクションのメンバを列挙するとき(Atomフィードを返すとき)に,列挙するメンバ数を制限していませんでした。コレクションに非常に多くのメンバが含まれていると,サーバにはとても大きな負荷がかかります。

ここでは,第1回で作成したエントリコレクションを修正し,フィードを複数のページに分割します。ページは"?page="というクエリ変数で指定します。

フィードページング(lib/MyBlog/Controller/EntryCollection.pm)

# ページあたりのエントリ数
my $ENTRIES_PER_PAGE = 10;
sub get_feed :Atompub(list) {
    my($self, $c) = @_;

    # フィード(XML::Atom::Feed)のひな型を取得する
    my $feed = $self->collection_resource->body;

    # コレクションURI
    my $uri = $self->collection_resource->uri;

    # リクエストされたページ(デフォルトは1ページ目)
    my $page = $c->req->param('page') || 1;

    # 検索時の開始レコード(offset)と取得数(rows)を指定する
    my $attr = {
	offset   => ($page-1) * $ENTRIES_PER_PAGE,
	rows     => $ENTRIES_PER_PAGE,
	order_by => 'edited DESC',
    };

    # entriesテーブルからエントリを取得する
    my $rs = $c->model('DBIC::Entries')->search({}, $attr);

    # フィードのひな形にエントリを追加する
    while (my $entry_resource = $rs->next) {
	my $entry = XML::Atom::Entry->new(\$entry_resource->xml);
	$feed->add_entry($entry);
    }

    # 最初のページへのリンクを追加する。
    $feed->first_link($uri);

    # 前後のページへのリンクを追加する
    $feed->previous_link("$uri?page=".($page-1)) if $page > 1;
    $feed->next_link("$uri?page=".($page+1)) if $rs->count >= $ENTRIES_PER_PAGE;

    return $self;
}

まず,ページ数を表すクエリ変数(page)を取得します。指定されなければ1ページとします。次に,開始レコード(offset)と取得数(rows)を指定して,データベースを検索します。最後に,最初のページ(first_link)と前後のページ(previous_link,next_link)へのリンクを追加します。

サービス文書のカスタマイズ

第2回では,サービス文書のコントローラをそのまま使いました。ここでは,サービス文書をカスタマイズする方法を説明します。

サービス文書に登場するコレクションの順序を変更するだけであれば,Catalyst設定ファイル(myblog.yaml)で変更できます。たとえば,エントリコレクションを先にする場合は次のように設定します。

サービス文書のカスタマイズ(myblog.yaml)

Controller::Service:
    workspace:
      - title: My Blog
        collection:
          - Controller::EntryCollection
          - Controller::MediaCollection

重要なコレクションを先に書いた方がよい,というプラクティスがあったような気がします(ソースを思い出せないので記憶違いかもしれませんが,重要な順にして問題はありません)⁠

さらにカスタマイズするときは,modify_service メソッドをオーバライドしてください。

サービス文書のカスタマイズ(lib/Catalyst/Controller/Service.pm)

sub modify_service {
    my($self, $c, $service) = @_;

    # サービス文書$service(XML::Atom::Service)を修正する...

    return $service;
}

エラー処理

ここまで,説明を簡単にするためにエラー処理を省略してきました。エラーが発生したときには,errorメソッドを呼んでreturnしてください。errorメソッドの引数は,Catalystオブジェクト($c)⁠HTTPステータスコード,エラーメッセージです。

たとえば,リクエストされたエントリが見つからないときは,次のようにして404エラーを返します。

エントリが見つからないときのエラー(lib/MyBlog/Controller/EntryCollection.pm)

sub get_entry :Atompub(read) {

    # 省略...

    # entriesテーブルからエントリを検索する
    my $rs = $c->model('DBIC::Entries')->find({ uri => $uri })
        || return $self->error($c, 404, 'Entry does not exist');

    # 省略...
}

すると,AtomPubサーバは,404エラーコードを返します。レスポンスボディは,エラーメッセージを含むエントリ文書になります。

HTTP/1.1 404 Not Found
Content-Type: application/atom+xml;type=entry

<?xml version="1.0" encoding="UTF・"?>?
<entry xmlns="http://www.w3.org/2005/Atom">
  <updated>2007-01-01T00:00:00Z</updated>
  <link rel="related"
        href="http://localhost:3000/mycollection/entry_1.atom"/>
  <title>404 Entry does not exist</title>
  <content type="xhtml">
    <div xmlns="http://www.w3.org/1999/xhtml">
      404 Entry does not exist
    </div>
  </content>
</entry>

著者プロフィール

井上武(いのうえたける)

NTTに入社後,未来ねっと研究所でマルチキャストやモバイルIPなどのネットワーク技術の研究開発に取り組んでいたが,最近はWebアーキテクチャに関する仕事をしている。

URLhttp://teahut.sakura.ne.jp/