こんにちは!Yuyaです。
現在、Xのクローンアプリを開発しています。
DM機能の実装でレビューをいただき、RESTfulなURL設計についてご指摘をいただいたのでアウトプットしていきます。
レビュー前のURLについて
以下がレビュー前のコードです。
config/urls.py
from django.urls import include, path
urlpatterns = [
...
path('message/', include('apps.chats.urls')),
...
]
chats/urls.py
from django.urls import path
from .views import RoomUserListView, MessageView, CreateMessageView
app_name = 'chats'
urlpatterns = [
path('', RoomUserListView.as_view(), name="message_list"),
path('<int:user1_id>-<int:user2_id>/', MessageView.as_view(), name="message"),
path('<int:user1_id>-<int:user2_id>/create/', CreateMessageView.as_view(), name="message_create"),
]
なぜこのようなURL設計にしたのか
- XのDM機能が
/messages/<user1_id> - <user2_id>
となっていたから - RESTfulなURL設計を考えていなかったから
- URLで
送信者-受信者
とした方が直感的にわかりやすいと考えたから
レビュー内容
自分の
user_id
はsession
から撮れば良いので、相手のuser_id
のみパスパラメータで取得するのが良い。パスに関しては、users/:user_id/messages
のようにするのがよいですね。
めちゃくちゃ引っかかった部分がありました。
それはパスに関しての文章です。
私の考えとしてはレビューをいただいてからも以下の考えは変わりませんでした。
-
/message
:メッセージ一覧 -
/message/<user1_id> - <user2_id>
:任意のユーザーとのメッセージ一覧
message
から始まっているのに、なぜusers
が次に来るのかわからず
流石にこれはレビュワーの方がタイポ?考えが間違っているのではないかと思ったので、同じ課題を取り組んでいる方に相談をしました。
HC生と技術的な相談をして
結論、私のURL設計がRESTfulではないということが明らかになりました。
具体的には5つの問題が発生します。
曖昧性
- user1が自分でuser2が相手なのか不明
- URLだけ見て誰の視点かわからない
- 同じ会話に対して複数のURLが存在する可能性
権限チェックの複雑化
- user1, user2どちらがログインユーザーなのかチェック
- 相手ユーザーのIDを特定
非直感的なURL構造
/message/123-456/create/ # 何を作成するのか不明確
セキュリティ面での問題
# URLから他人の会話を推測可能
/message/100-101/ # ユーザー100と101の会話
/message/100-102/ # ユーザー100と102の会話
/message/101-102/ # ユーザー101と102の会話(推測可能)
動詞を使用している
# messageは動詞
path('message/', include('apps.chats.urls')),
# createは動詞
path('<int:user1_id>-<int:user2_id>/create/', CreateMessageView.as_view(), name="message_create"),
単数形vs複数形の非統一
- RESTfulでは複数形が標準
- 一覧取得なのか個別取得なのか区別できない
改善後
レビューワーの方からのご指摘を受けて、RESTfulなURLを意識したURLは以下のようになりました!
config/urls.py
from django.urls import include, path
urlpatterns = [
...
path('messages/', include('apps.chats.urls')), #messagesと複数形に変更
...
]
chats/urls.py
from django.urls import path
from .views import RoomUserListView, MessageView
app_name = 'chats'
urlpatterns = [
path('', RoomUserListView.as_view(), name="message_list"),
# # GET, POST
path('users/<int:receiver_id>/messages', MessageView.as_view(), name="message"),
]
RESTful URL設計時のチェックポイント
- 名詞を使用している(動詞を避けている)
- 複数形を使用している
- 階層構造でリソースの関係を表現している
- HTTPメソッドでアクションを表現している
- 一貫性のある命名規則を使用している
- URLから技術的詳細を隠している
- 略語を避けて明確な名前を使用している