ブログ  前の記事  次の記事  2007-10-22 

たけまる / Catalyst::Controller::Atompub - URI の命名規則


2007-10-22

_ Catalyst::Controller::Atompub - URI の命名規則 [perl][atompub]

間があいてしまいましたが,Atompub モジュールの使い方シリーズです.

Catalyst::Controller::Atompub は Catalyst の拡張モジュールで,
AtomPub (Atom Publishing Protocol) サーバを簡単に実装することがで
きます.

# クライアントは Atompub モジュール [2007-08-12-1] にあります.

さて,AtomPub サーバの基本的な機能である "リソースの CRUD (作成,
取得,更新,削除)" については [2007-09-13-1][2007-09-27-1]
説明しました.今回は,リソース URI の変更方法について説明します.

"REST" というアーキテクチャスタイルを知っている人は多いかと思いま
す.REST は URI でリソースを表し,GET, POST などの HTTP メソッドで
リソースを操作します.AtomPub は REST を実装したプロトコルのひとつ
です.REST では URI が重要な役割を果たします.

# REST の詳しい解説は yohei-y:weblog: REST 入門 にあります.

■ Default URIs

デフォルトでは,C::C::Atompub::Collection モジュールが自動的にリソー
ス URI を決定します.命名規則は次のようになっています.
- URI の前半部分は Collection URI と同じ
- HTTP Request に Slug ヘッダがあれば,それを基にリソース名を作成
   - 空白とドット (.) はアンダースコア (_) に変更
   - 大文字は小文字に変換
   - URI に使えない文字は Percent Encode
   - Slug がなければ,日時から自動生成
- HTTP Request の Content-Type を参考に,一般的な拡張子を付与

たとえば,Slug が "Entry 1" であるような Atom Entry が,
http://example.com/collection という Collection に POST されたとし
ます.
POST /collection HTTP/1.1
Host: example.com
...
Content-Type: application/atom+xml;type=entry
Slug: Entry%201

<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom">
 ...
</entry>

この場合のリソース URI は http://example.com/colleciton/entry_1.atom
となります.AtomPub 仕様に従い Slug の値が Percent Encode されてい
ますが,"Entry 1" に戻してから処理を行います.

また,Slug のない GIF 画像が POST されたときの URI は,
http://example.com/colleciton/20071022-123456-123456.gif のように
なります.リソース名の部分は,"年月日 - 時分秒 - マイクロ秒" です.

この命名規則は AtomPub としては標準的だと思いますが,重複チェック
していないなどの問題点もあります.

■ Changing URIs of Entry Resoures

命名規則を変更するには,C::C::Atompub::Collection のサブクラスで
make_edit_uri メソッドを override します.

Entry リソースを扱う Collection で,URI の重複チェックを行うように
変更してみます.URI は "entries" テーブルの uri カラムに格納されて
いるとします.
package MyAtom::Controller::MyCollection;
use POSIX qw( strftime );
use Time::HiRes qw( gettimeofday );
use base qw( Catalyst::Controller::Atompub::Collection );

sub make_edit_uri {
    my ( $self, $c, @args ) = @_;

    # 親クラスの make_edit_uri を呼び出す
    my $uri = $self->SUPER::make_edit_uri( $c, @args );

    # URI が存在しなければ,そのまま返す
    return $uri unless $c->model('Entries')->find( { uri => $uri } );

    # URI が重複するときは,日時を拡張子の前に挿入する
    my ( $sec, $usec ) = gettimeofday;
    my $dt = strftime '%Y%m%d-%H%M%S', localtime( $sec );
    $usec  = sprintf '%06d', $usec;
    $uri =~ s{(\.[^./?]+)$}{-$dt-$usec$1};

    return $uri;
}

親クラスが決定した URI を検索し,存在するときには日時を追加して重
複を回避します.たとえば,http://example.com/colleciton/entry_1.atom
という URI がすでに存在しているといれば,http://example.com/colleciton/entry_1-20071022-123456-123456.atom
のように日時を追加します.

■ Changing URIs of Media Resources and Media Link Entries

Media リソースを扱う Collection では,Media リソースだけでなく
Media Link Entry の URI も決定しなければなりません.この場合,
make_edit_uri はふたつの URI を返します.ひとつめが Media Link
Entry, ふたつめが Media リソースです.

先ほどと同様に,URI の重複を回避するように修正してみます.Media
Link Entry と Media リソースの URI は,それぞれ "entry_uri",
"media_uri" というカラムに格納されているとします.
package MyAtom::Controller::MyCollection;
use POSIX qw( strftime );
use Time::HiRes qw( gettimeofday );
use base qw( Catalyst::Controller::Atompub::Collection );

sub make_edit_uri {
    my ( $self, $c, @args ) = @_;

    # 親クラスの make_edit_uri を呼び出す
    my ( $entry_uri, $media_uri ) = $self->SUPER::make_edit_uri( $c, @args );

    # URI が存在しなければ,そのまま返す
    return ( $entry_uri, $media_uri )
        unless $c->model('Entries')->find( {
            '-or' => [ { entry_uri => $entry_uri }, { media_uri => $media_uri } ] 
        } );

    # URI が重複するときは,日時を拡張子の前に挿入する
    my ( $sec, $usec ) = gettimeofday;
    my $dt = strftime '%Y%m%d-%H%M%S', localtime( $sec );
    $usec  = sprintf '%06d', $usec;
    $_ =~ s{(\.[^./?]+)$}{-$dt-$usec$1} for ( $entry_uri, $media_uri );

    return ( $entry_uri, $media_uri );
}

■ Changing URIs of //content/@src

AtomPub では,Media リソースを表す URI がふたつあります.ひとつは,
link 要素で,もうひとつは content 要素です.
<entry>
  <link rel="edit-media" href="http://example.com/collection/foo.gif"/>
  <content src="http://example.com/collection/foo.gif"/>
  ...
</entry>

C::C::Atompub::Collection は,デフォルトでは,両者に同じ URI を割
り当てます.ここまではこのふたつを区別せずに説明しました.

しかし,CRUD 操作に対してはリソース本体を返し,通常のアクセスに対
してはキャッシュを返したいときがあります.このような理由で,別々の URI を用意したいこと
があります.

そのときには,POST あるいは PUT されたリソースを格納する前に,
//content/@src をキャッシュ用 URI に変更してください.

一言メッセージをこっそり送信できます (非公開)
 今年の西暦→