В RESTful дизайне, как лучше всего поддерживать различные типы GET? - PullRequest
4 голосов
/ 01 февраля 2009

В текущем проекте мне нужно поддержать поиск пользователя по учетным данным, а также по адресу электронной почты. Я знаю, что в дизайне RESTful вы используете GET для поиска ресурсов. В Рельсах ...

GET /users    # => UsersController.index -- find all the users

GET /users/1  # => UsersController.show -- find a particular user

Но мне также нужно что-то сродни ...

GET /users?username=joe&password=mysterio

GET /users?email=foo@bar.com

Обычно добавляются дополнительные маршруты и действия, кроме index и show?

Или более распространено использовать условную логику в действии show, чтобы посмотреть на параметры и определить, находим ли мы то или иное?

Существует аналогичная проблема с запросами PUT. В одном случае мне нужно установить для пользователя значение «активный» (user.active = true), а в другом случае мне просто нужно выполнить обычную операцию редактирования на основе форм.

Спасибо, ребята. В конце концов я собираюсь выяснить этот материал REST.

Ответы [ 5 ]

3 голосов
/ 09 февраля 2009

Я новичок в SO, поэтому я не могу комментировать, но отмеченный зеленым ответ не является RESTful.

В мире RESTful ваш контроллер захватывает все параметры и передает их на уровень модели для обработки. Как правило, вы не должны создавать другое действие.

Вместо этого вы должны сделать что-то вроде этого:

def show
  @user = User.find_by_login_or_email(params[:user])
  ... #rest of your action
end

Ваша модель может иметь такой метод:

class User
  self.find_by_login_or_email(params)
    return find_by_login(params[:login]) unless params[:login].blank?
    return find_by_email(params[:email]) unless params[:email].blank?
    nil #both were blank
  end
end

Ваш взгляд может выглядеть следующим образом:

<%= f.text_field :user, :email %>

или

<%= f.text_field :user, :login %>

Примечание: непроверенный код, поэтому может быть с ошибками ... но общая мысль обычно заключается не в том, чтобы создавать новые действия для каждого разового правила. Вместо этого посмотрите, можете ли вы использовать логику в моделях. Если ваши контроллеры начинают выполнять слишком много нестандартных действий, то, возможно, пришло время пересмотреть моделирование вашего домена, и, возможно, это реорганизовать действия для некоторых новых моделей.

PS: вы никогда не должны передавать пароли через GET, как это

2 голосов
/ 05 февраля 2009

Что касается запроса "ByEmail", рассматривали ли вы вопрос о создании нового ресурса электронной почты.

GET /email/foo_at_bar_dot_com

Ответ может содержать ссылку на соответствующего пользователя.

Я вижу так много людей, которые пытаются применить принципы дизайна RESTful к своей структуре URL, а затем отображают эти URL в код процедурного обработчика. например GET = Показать или GET = Index или ShowByEmail. Делая это, вы действительно притворяетесь, что делаете RESTful-дизайн, а затем пытаетесь создать соответствие между ресурсно-ориентированным пространством URL и процедурно-ориентированной реализацией. Это действительно трудно сделать, и процедурный характер продолжает просачиваться в URL.

Ресурсно-ориентированное проектирование часто требует совершенно другого подхода к проблемам, к которым мы привыкли, и, к сожалению, многие из существующих сред продолжают втягивать нас обратно в модель RPC.

2 голосов
/ 01 февраля 2009

Я не знаю, насколько это условно, но я бы так и сделал. я добавит еще одно действие, если оно конкретно связано с этим ресурс. В вашем примере show является находкой по идентификатору пользователя, поэтому имеет смысл другое действие на UsersController. Вы можете превратить его в предложение, которое делает смысл, "дай мне пользователя с этим адресом электронной почты"

Для другого, GET /users?username=joe&password=mysterio, я бы сделал это как другой ресурс. Я предполагаю, что вы думаете, что действие войдет в систему пользователь, если пароль был правильным. Глагол GET не имеет смысла в этом контекст.

Вы, вероятно, хотите ресурс 'сеанса' (кстати, именно так работает restful_auth). Поэтому вы можете сказать «создайте мне сеанс для этого пользователя» или что-то вроде POST /sessions, где тело сообщения - это имя пользователя и пароль для пользователя. Это также имеет хороший побочный эффект - не сохранять пароль в истории. или позволить кому-то перехватить его на HTTP-прокси.

Итак, код вашего контроллера будет выглядеть примерно так:

class UsersController < ActionController::Base

    def show
      @user = User.find_by_id(params[:id])
      # etc ...
    end

    def show_by_email
      @user = User.find_by_email(params[:email)
    end
end

class SessionsController < ActionController::Base
  def create
     # ... validate user credentials, set a cookie or somehow track that the 
     # user is logged in to be able to authenticate in other controllers
  end
end

Вы бы настроили свои маршруты так:

map.connect "/users/byemail", :controller => "users", :action => "show_by_email", :conditions => { :method => :get }
map.resources :users
map.resources :sessions

Это даст вам URL, такие как /users/byemail?email=foo@example.com. Есть проблемы с кодировкой электронной почты непосредственно в пути URL, rails видит «.com» в конце и по умолчанию переводит это в формат:. Там, вероятно, Обходной путь, но это то, над чем я работал.

Также, как говорит Клэтус, есть способы, чтобы ваш маршрут соответствовал на основе формата частей URL, например, всех цифр или буквенно-цифровых символов, но я не знаю, как сделать так, чтобы это работало с точками в URL.

2 голосов
/ 01 февраля 2009

Первое, что вы можете сделать, это сделать ваши GET как можно умнее. В вашем примере это может быть обработано программно. Аргумент может быть обработан следующим образом:

  • Это число? Это идентификатор пользователя;
  • Есть ли в нем @? Это электронное письмо;
  • В противном случае? Это имя пользователя.

Но я предполагаю, что вы не просто говорите об этом примере и хотите, чтобы что-то обрабатывалось в общем случае, а не только в этом конкретном случае.

Есть два основных способа решения этой проблемы:

  1. Добавить дополнительную информацию о пути, например /users/email/me@here.com, / users / name / cletus; или
  2. Будьте более конкретны в своем URL "верхнего уровня", например /user-by-email/me@here.com, /user-by-name/cletus.

Я бы справился с этим программно, если бы вы могли.

1 голос
/ 01 февраля 2009

Возможно, вы сможете настроить разные маршруты для разных задач. Поэтому в этом случае у вас может быть один маршрут к методу в UserControll, предназначенный для получения пользователя по электронной почте, а другой - для получения информации по учетным данным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...