Как вы, возможно, читали в моем блоге, часть I RESTful Rails, RESTful Rails — это способ отображения HTTP-глаголов ( GET , POST , PUT и DELETE ) в действия CRUD ( Create , Read , Update и Delete ) в ваших Rails Программы. Если это предложение было для вас полным бредом, найдите время, чтобы прочитать сообщение в блоге, и оно может иметь немного больше смысла.
Хорошо, я предполагаю, что у вас было достаточно времени, чтобы поглотить мой пост, и вы полны CRUD и REST. Теперь пришло время построить реальный пример.
REST Tumblelog
Для этого примера мы создадим (очень) базовое приложение типа tumblelog. Что такое Tumblelog? Ну, это как блог, но без рефератов. Tumblelog — это путаница частых, но коротких сообщений, которые обычно представляют собой ссылку, цитату, быстрый комментарий и так далее.
Для начала вам нужно создать новый проект rest_tumble с именем rest_tumble , с одной моделью и одним контроллером. Так что запустите свою любимую консоль и запустите:
rails rest_tumble cd rest_tumble script/generate model post script/generate controller posts
Откройтеdb/migrate/001_create_posts.rbи отредактируйте его, чтобы он выглядел так:
class CreatePosts def up create_table do |t| t.string :message, :null => false, :limit => 140 t.timestamps end end def self.down drop_table :posts end endclass CreatePosts def up create_table do |t| t.string :message, :null => false, :limit => 140 t.timestamps end end def self.down drop_table :posts end endЗатем запустите:
rake db:migrate
Наконец, добавьте некоторую проверку в модель post (app/models/post.rb):
class Post < ActiveRecord::Base validates_presence_of :message validates_length_of :message, :maximum => 140 end
Хорошо, теперь, когда мы создали приложение Rails, давайте настроим контроллер и маршруты. Если вы вернетесь к последней статье, вы помните, что есть четыре различных глагола RESTful. Давайте добавим эти четыре метода в наш контроллер (app/controllers/posts_controller.rb):
class PostsController < ApplicationController # GET - displays all posts def index end # GET - shows one post (based on the supplied id) def show end # POST - creates a new post def create end # PUT - updates an existing post def update end # DELETE - deletes a post def destroy end end
Из приведенных выше комментариев видно, как каждый глагол REST отображается на методы контроллера. Вы также заметите, что, хотя есть четыре глагола, нам нужно пять методов. У глаголаGETесть два соответствующих метода: один показывает все сообщения, а другой показывает определенный пост. На самом деле нам нужно еще два метода - новый и редактировать - для завершения контроллера.
Мы добавляем эти дополнительные методы по двум причинам. Прежде всего, Rails использует одну и ту же конечную точку (плюс, может быть, id) для всех глаголов, поэтому, если мы будем ссылаться на/posts(методGET), Rails вернет список записей. Добавляя эти методы, мы получаем способ отображения форм.
Давайте добавим дополнительные методы и немного кода:
class PostsController < ApplicationController # GET - displays all posts def index @posts = Post.find :all, :order => 'created_at ASC' end # GET - shows one post (based on the supplied id) def show @post = Post.find(params[:id]) end # GET - displays a form which can be used to create a post def new @post = Post.new end # POST - create a new post def create @post = Post.new(params[:post]) @post.save! redirect_to post_path(@post) rescue ActiveRecord::RecordInvalid render :action => 'new' end # GET - displays a form allowing us to edit an existing post def edit @post = Post.find(params[:id]) end # PUT - update an existing post def update @post = Post.find(params[:id]) @post.attributes = params[:post] @post.save! redirect_to post_path(@post) rescue ActiveRecord::RecordInvalid render :action => 'edit' end # DELETE - delete a post def destroy @post = Post.find(params[:id]) @post.destroy redirect_to posts_path end end
Если вы когда-либо делали какое-либо кодирование на Rails ранее, здесь не будет никаких сюрпризов, за исключением возможных методовposts_path,post_path,edit_post_pathиdelete_post_path. Эти специальные методы сгенерируют путь к ресурсу на основе записей вroutes.rbЕсли вы не помните синтаксис (и он может немного усложнить такие вещи, как вложенные маршруты), вы можете запустить очень полезную задачу по рейку маршрутов, например:
rake routes
Но чтобы эти специальные методы работали, нам нужно сообщить Rails, что у нас есть ресурс REST. Откройтеconfig/routes.rbи добавьте следующую строку (примерно в середине кода вы должны увидеть похожую примерную строку, которая была закомментирована; поместите эту строку под ней):
map.resources :posts
Все, что это делает, это сообщает Rails, что у нас естьposts_controllerкоторый хочет быть RESTful, поэтому он должен ответить соответствующим образом. Испытайте грабли на маршруте, чтобы увидеть результаты своей ручной работы.
Чтобы завершить сайт, нам нужно добавить представления - каждому действиюGETнеобходимо соответствующее представление:index.html.erb,show.html.erb,new.html.erbиedit.html.erb. Я также создал частичное_form.html.erb, потому что код формы для методов new и edit в основном одинаков.Вот
indexпредставление. В этом файле нет ничего особенного - опять же, мы используемpost_path, на этот раз в качестве параметра тегаlink_to:# app/views/posts/index.html.erb <%= link_to 'New message', new_post_path %> <hr /> <% if @posts.empty? -%> <p>There are no posts</p> <% else -%> <% @posts.each do |post| -%> <p> <%= link_to h(post.message), post_path(post) -%> </p> <% end -%> <% end -%>
Вот видshow. Для этой страницы мы показываем кнопку удаления и редактируем ссылку:
# show.html.erb <p><%= h(@post.message) -%></p> <% form_tag post_path(@post), :method => :delete do -%> <%= link_to 'Edit', edit_post_path(@post) -%> <button type="submit">Delete</button> <% end -%>
В приведенном выше коде вы заметите, что у нас есть вызовform_tagвключающийpost_pathи метод delete. Это указывает форме выполнить действиеDELETEпри нажатии кнопки. Однако при нажатии на ссылку редактирования мы попадаем в специальный вид редактирования.И
newиeditпредставления в основном одинаковы - настолько, что мы используем частичное для общего кода:
# app/views/posts/new.html.erb <%= error_messages_for('post') %> <% form_tag posts_path do -%> <%= render :partial => 'posts/form' %> <fieldset> <button type="submit">Save</button> <%= link_to 'Cancel', posts_path %> </fieldset> <% end -%> # app/views/posts/edit.html.erb <%= error_messages_for('post') %> <% form_tag post_path(@post), :method => :put do -%> <%= render :partial => 'posts/form' %> <fieldset> <button type="submit">Update</button> <%= link_to 'Cancel', posts_path %> </fieldset> <% end -%>
Вы заметите, что мы снова устанавливаем атрибут метода, на этот раз сPUT. Нам не нужно делать это дляnewпредставления, поскольку действие по умолчанию для формы,POST, подходит для нашегоnewдействия.Наконец, давайте посмотрим на частичную форму, которая очень проста для форм Rails:
# app/views/posts/_form.html.erb <fieldset> <legend>Enter your message</legend> <label for="post_message">Message</label> <%= text_area 'post', 'message' %> </fieldset>Давайте запустим его и посмотрим, что получится! Бегать:
ruby script/serverИ указав ваш браузер на
http://localhost:3000/posts. Если повезет, вы должны увидеть что-то похожее на это:
Поздравляем! Вы только что создали приложение RESTful Rails!
XML Goodness
Теперь я буду первым, кто признает, что эта демонстрация не очень впечатляет - мы могли бы сделать то же самое, используя обычные CRUD Rails. Но когда RESTful Rails становится классным, это когда вы можете публиковать свои собственные веб-сервисы бесплатно (ну, почти бесплатно - нам нужно добавить пару строк кода, но это все).
Для этого нам нужно сообщить Rails, что он должен по-разному реагировать на XML-запрос. К счастью, метод
respond_toделает всю тяжелую работу за нас. Итак, давайте изменим контроллер поста, чтобы он выглядел так:
class PostsController < ApplicationController # GET - displays all posts def index @posts = Post.find :all, :order => 'created_at ASC' respond_to do |format| format.xml { render :xml => @posts.to_xml } format.html { } end end # GET - shows one post (based on the supplied id) def show @post = Post.find(params[:id]) respond_to do |format| format.xml { render :xml => @post.to_xml } format.html { } end end # GET - displays a form which can be used to create a post def new @post = Post.new end # POST - create a new post def create @post = Post.new(params[:post]) @post.save! respond_to do |format| format.html { redirect_to post_path(@post) } format.xml { render :xml => @post.to_xml, :status => :created } end rescue ActiveRecord::RecordInvalid respond_to do |format| format.html { render :action => 'new' } format.xml { render :xml => @post.errors.to_xml, :status => 500 } end end # GET - displays a form allowing us to edit an existing post def edit @post = Post.find(params[:id]) end # PUT - update an existing post def update @post = Post.find(params[:id]) @post.attributes = params[:post] @post.save! respond_to do |format| format.html { redirect_to post_path(@post) } format.xml { render :xml => @post.to_xml, :status => :ok } end rescue ActiveRecord::RecordInvalid respond_to do |format| format.html { render :action => 'edit' } format.xml { render :xml => @post.errors.to_xml, :status => 500 } end end # DELETE - delete a post def destroy @post = Post.find(params[:id]) @post.destroy respond_to do |format| format.html { redirect_to posts_path } format.xml { head :ok } end end endДавайте посмотрим на то, что мы сделали здесь. Мы ввели новый блок
respond_to, который позволяет разделять запросыxmlиhtml. Мы также представили приложению различные типы рендеринга с использованием встроенного типа XML, а также с помощьюto_xmlдля преобразования объектов в сериализованный XML для вывода. Наконец, этот пример показывает нам, как выводить различные коды состояния HTTP.Методы
indexиshowочень просты: если запрос представляет собой XML, он сериализует объект (или объекты) и разбрасывает их по строке. Если вы установили curl , вы можете проверить это очень легко. Сначала добавьте пару сообщений с помощью веб-интерфейса, затем введите в терминал следующее:
curl -H 'Accept: application/xml' http://localhost:3000/posts.xmlЭта команда должна вернуть список сообщений, которые вы сделали.
Следующие функции - создание, обновление и удаление - являются наиболее интересными. Если вы сообщите Rails, что контент, который вы публикуете, представляет собой XML, он автоматически преобразует (или, по крайней мере, пытается преобразовать) данные в массив, из которого мы можем создать объект.
После успешного завершения публикации в Tumblelog мы отправляем HTTP-код состояния
201 Createdвместе с копией нового объекта сообщения. Отправка нового объекта в ответ означает, что потребитель веб-службы не должен делать еще один запрос, чтобы найти его, что дает им удобную контрольную точку.Команда
curlбудет выглядеть примерно так:
curl -H 'Accept: application/xml' -H 'Content-type: application/xml' http://localhost:3000/posts.xml -d '<post><message>A RESTful message</message></post>' -X POSTКогда мы успешно обновляем существующее сообщение, мы возвращаем обычный код состояния
200 OK, снова с копией объекта. Например, если бы у нас был объект сid4, мы могли бы обновить его следующим кодом:
curl -H 'Accept: application/xml' -H 'Content-type: application/xml' http://localhost:3000/posts/4.xml -d '<post><message>An updated RESTful message</message></post>' -X PUTВ обоих приведенных выше примерах, если представленные данные не полны, Rails вернет сообщения об ошибках в виде XML с кодом состояния
500 Internal Server Error.Последний пример - удаление, которое просто возвращает ответ
200 OK:curl -H 'Accept: application/xml' -H 'Content-type: application/xml' http://localhost:3000/posts/4.xml -X DELETEУпражнения для тебя дома
Возможно, вы заметили, что обработка ошибок для XML-версии сайта довольно проста. Если вы попытаетесь обновить несуществующее сообщение, вы получите ошибку HTML. Попробуйте выработать способ сделать это с XML - и сделайте то же самое с другими исключениями. Вот подсказка: посмотрите на
rescue_fromклассаrescue_from.Посмотрите, сможете ли вы разработать способ создания JSON-версии сайта, которая бы подходила для ответа на запросы AJAX. Как только вы начнете, вам будет легче, чем вы думаете!
Связывая некоторые свободные концы
- Эти инструкции выполняются на Rails 2.0.2 с использованием базы данных SQLite3 по умолчанию, поэтому нам не нужно настраивать MySQL.
- Я знаю, что Rails предлагает генератор ресурсов, который будет автоматически генерировать модель, контроллер и набор шаблонов для вашего приложения RESTful, но вы ничего не узнали бы, если бы я использовал это, не так ли?
- Как я упоминал в моем предыдущем посте, ни один браузер не может выполнять запросы HTTP
PUTилиDELETE. Rails эмулирует их, вводя секретное скрытое поле ввода с именем_method. Попробуйте и посмотрите источник на странице редактирования. Вы увидите, что метод формы все еще является верным старымPOST, и в нем есть дополнительное поле ввода.- Я ненавижу тот факт, что по умолчанию нет специального метода удаления, который позволял бы вам добавлять представление подтверждения удаления. Я обычно добавляю его с помощью атрибута
:memberв файле маршрутов (config/routes.rb), поэтому наше предыдущее добавление к этому файлу стало бы:map.resources :posts, :member => { :delete => :get }Это позволяет мне добавить представление
delete(delete.html.erb), а также заменить форму и кнопку удаления ссылкой вshowпредставления (show.html.erb).Вывод
Это завершает очень простой пример сайта RESTful Rails. Очевидно, есть некоторые другие вещи, которые вам нужно добавить для работающего веб-сайта (аутентификация для одного), но то, что мы достигли, все еще довольно внушительно. Как и было обещано, вы можете скачать полный исходный код для собственного использования .
