{"id":1543,"date":"2006-12-03T16:23:29","date_gmt":"2006-12-03T08:23:29","guid":{"rendered":"http:\/\/ihower.idv.tw\/blog\/archives\/1543"},"modified":"2015-09-05T10:49:28","modified_gmt":"2015-09-05T02:49:28","slug":"rails-restful-implement","status":"publish","type":"post","link":"https:\/\/ihower.tw\/blog\/1543-rails-restful-implement","title":{"rendered":"Rails RESTful \u5be6\u4f5c"},"content":{"rendered":"<p><strong>Update<\/strong>(2006\/12\/4): \u4e00\u4e9b\u5c0f\u4fee\u6539\u8ddf link_to \u7684 web accessibility \u88dc\u5145\u3002<\/p>\n<p><strong>Update<\/strong>(2006\/12\/11): \u8acb\u63a5\u8457\u770b\u7cfb\u5217\u6587\u7ae0\u4e0b\u4e00\u96c6:<a href=\"\/blog\/archives\/1541\"> Rails RESTful \u76f8\u95dc\u5de5\u5177<\/a>\u3002<\/p>\n<p>Rails RESTful <a href=\"\/blog\/archives\/1542\">\u7cfb<\/a> <a href=\"\/blog\/archives\/1545\">\u5217<\/a> \u5f48\u7b2c\u4e09\u7bc7\uff0c\u6211\u60f3 David Heinemeier Hansson \u7684\u9019\u7bc7\u6295\u5f71\u7247\u662f\u5f88\u597d\u7684\u958b\u5834\u767d:\u00a0\u00a0<a href=\"http:\/\/www.loudthinking.com\/lt-files\/worldofresources.pdf\">Discovering a world of Resources on Rails<\/a><\/p>\n<p>\u672c\u4f86\u9019\u7bc7\u60f3\u5beb\u7684\u4ed4\u7d30\u9ede\uff0c\u4e0d\u904e\u5f8c\u4f86\u6211\u767c\u73fe<a href=\"http:\/\/pragmaticprogrammer.com\/titles\/rails2\/index.html\">AWDwR\u7b2c\u4e8c\u7248<\/a>\u8b1b Resource-Based Routing \u7684\u9801\u6578\u9084\u4e0d\u5c11(\u5341\u5e7e\u9801\u5427\uff0c\u9084\u883b\u8a73\u7d30\u7684)\uff0c\u8981\u7528 Rails \u7684\u4eba\u61c9\u8a72\u90fd\u53bb\u8cb7\u4e00\u672c\uff0c\u6211\u60f3\u6211\u9019\u88e1\u5c31\u4e0d\u5de8\u7d30\u9761\u907a\u4e86&#8230; :p<\/p>\n<p>\u5728 Rails \u4e2d\u662f\u5982\u4f55\u5be6\u4f5c RESTful \u652f\u63f4\u7684? \u9996\u5148\u662f\u5728 Resource Routes \u4e0a\uff0c\u5728 routes.rb \u52a0\u5165<\/p>\n<blockquote><p>map.resources :users<\/p><\/blockquote>\n<p>\u9019\u6a23\u7684\u5ba3\u544a\u5c07\u5728\u81ea\u52d5\u5c0d\u61c9 URL\u8def\u5f91\u8ddf Controller \u7684 action \uff0c\u800c\u6709\u4ee5\u4e0b\u7684\u7d50\u679c :<\/p>\n<blockquote><p>GET: \/users\u00a0=&gt; [:action =&gt; &#8216;index&#8217;]<br \/>\nGET: \/users.xml =&gt; [:action =&gt; &#8216;index&#8217;, :format =&gt; &#8216;xml&#8217;]<br \/>\nGET: \/users\/1 =&gt; [:action =&gt; &#8216;show&#8217;, :id =&gt; 1]<br \/>\nGET: \/users\/1;edit =&gt; [:action =&gt; &#8216;edit&#8217;, :id =&gt; 1]<br \/>\nGET: \/users\/1.xml =&gt; [:action =&gt; &#8216;show&#8217;, :id =&gt; 1, :format =&gt; &#8216;xml&#8217;]<br \/>\nPOST: \/users =&gt; [:action =&gt; &#8216;create&#8217;]<br \/>\nPUT: \/users\/1 =&gt; [:action =&gt; &#8216;update&#8217;, :id =&gt; 1]<br \/>\nDELETE: \/users\/1 =&gt; [:action =&gt; &#8216;destroy&#8217;, :id =&gt; 1]<\/p><\/blockquote>\n<p>\u4e5f\u5c31\u662f\u4f7f\u7528\u00a0named routes \u4f86\u5be6\u4f5c\u51fa <strong>verb-oriented controllers<\/strong>\uff0c\u55ae\u4e00\u500b resource \u6839\u64da HTTP verb \u800c\u6709\u4e0d\u540c\u7684\u884c\u70ba\u3002<!--more--><\/p>\n<p>Resource method\u00a0\u4e5f\u63a5\u53d7\u4e00\u4e9b\u9032\u968e\u53c3\u6578\uff0c\u4f8b\u5982:<\/p>\n<blockquote><p>map.resources :articles,<br \/>\n\u00a0 :collection =&gt; {:sort =&gt; :put}, #\u5ba2\u88fd method\u00a0\u7d66\u7fa4\u96c6\uff0c\u4e26\u6307\u5b9a\u7528\u54ea\u7a2e HTTP verb ( :get\/:post\/:put\/:delete\u6216:any)<br \/>\n\u00a0 :member =&gt; {:deactivate =&gt; :delete},\u00a0# \u5ba2\u88fd method \u7d66\u67d0\u5143\u7d20<br \/>\n\u00a0 :new =&gt; {:preview =&gt; :post}, #\u5ba2\u88fd method \u7d66\u65b0\u5143\u7d20<br \/>\n\u00a0 :controller =&gt; &#8216;articles&#8217;, #\u6307\u5b9a Controller<br \/>\n\u00a0 :singular =&gt; &#8216;article&#8217;,\u00a0#\u6307\u5b9a\u7528\u55ae\u6578<br \/>\n\u00a0 :path_prefix =&gt; &#8216;\/teams\/:team_id&#8217;,\u00a0#\u5728routes\u524d\u52a0\u4e0aroute variables\uff0c\u8209\u4f8b\u5982\u4e0b\u3002<br \/>\n\u00a0 :name_prefix =&gt; &#8216;my_&#8217; #\u5728\u7522\u751f\u7684 helper \u524d\u52a0prefix\uff0c\u901a\u5e38\u914d\u5408 path_prefix \u6216 nested \u4f7f\u7528\u907f\u514d\u540d\u7a31\u885d\u7a81\u3002<\/p><\/blockquote>\n<p>\u4e0a\u8ff0\u7684 path_prefix \u53ef\u4ee5\u6539\u7528Nested \u7528\u6cd5\uff0c\u4f8b\u5982:<\/p>\n<blockquote><p>map.resources :teams do |teams|<br \/>\n\u00a0\u00a0\u00a0 teams.resources :players<br \/>\nend\u00a0<\/p><\/blockquote>\n<p>\u7522\u751f\u7684\u5c0d\u61c9\u6548\u679c\u662f<\/p>\n<blockquote><p>GET: \/teams\/13\/players\/1 =&gt; [:controller =&gt; &#8216;players&#8217;, :action =&gt; &#8216;show&#8217;, :team_id =&gt; 13, :id =&gt; 1]<\/p><\/blockquote>\n<p>\u9664\u4e86\u7522\u751f\u5c0d\u61c9 routes \u7684 action \u7d66 Controller\uff0c\u6700\u65b9\u4fbf\u7684\u662f\u81ea\u52d5\u7522\u751f\u4e86\u4e00\u4e9b helpers \u7d66 views\uff0c\u4f8b\u5982 (\u9019\u88e1\u7684team,player\u6839\u64da\u4f60\u7684resource name\u800c\u81ea\u884c\u66ff\u63db\uff0c\u6ce8\u610f\u55ae\u8907\u6578):<\/p>\n<table border=\"1\">\n<tr>\n<th>helpers<\/th>\n<th>HTTP Verb<\/th>\n<th>\u7522\u751f\u7684Path<\/th>\n<th>\u5c0d\u61c9\u7684Action<\/th>\n<\/tr>\n<tr>\n<td>teams_path<\/td>\n<td>GET<\/td>\n<td>\/teams<\/td>\n<td>index<\/td>\n<\/tr>\n<tr>\n<td>team_path(id)<\/td>\n<td>GET<\/td>\n<td>\/teams\/1<\/td>\n<td>show<\/td>\n<\/tr>\n<tr>\n<td>new_team_path<\/td>\n<td>GET<\/td>\n<td>\/teams\/new<\/td>\n<td>new<\/td>\n<\/tr>\n<tr>\n<td>teams_path<\/td>\n<td>POST<\/td>\n<td>\/teams<\/td>\n<td>create<\/td>\n<\/tr>\n<tr>\n<td>edit_team_path(id)<\/td>\n<td>GET<\/td>\n<td>\/teams\/1;edit<\/td>\n<td>edit<\/td>\n<\/tr>\n<tr>\n<td>team_path(id)<\/td>\n<td>PUT<\/td>\n<td>\/teams\/1<\/td>\n<td>update<\/td>\n<\/tr>\n<tr>\n<td>team_path(id)<\/td>\n<td>DELETE<\/td>\n<td>\/teams\/1<\/td>\n<td>destroy<\/td>\n<\/tr>\n<\/table>\n<p>\u800c Nested Resource \u7684\u7528\u6cd5\u8209\u4f8b:<\/p>\n<table border=\"1\">\n<tr>\n<th>helpers<\/th>\n<th>\u7522\u751f\u7684Path<\/th>\n<\/tr>\n<tr>\n<td>players_path(@team)<\/td>\n<td>\/teams\/1\/players<\/td>\n<\/tr>\n<tr>\n<td>player_path(@team, @player )<\/td>\n<td>\/teams\/1\/players\/5<\/td>\n<\/tr>\n<\/table>\n<p>\u6709\u4e86\u9019\u4e9b helper\uff0c\u6211\u5011\u5c31\u53ef\u4ee5\u9019\u6a23\u7522\u751f\u6309\u9215\u3001\u8d85\u9023\u7d50\u3001Ajax\u8d85\u9023\u7d50\u8ddf\u8868\u55ae\u00a0:<\/p>\n<blockquote><p>button_to &#8220;Destroy&#8221;, team_path(@team), :confirm =&gt; &#8220;Are you sure?&#8221;, :method =&gt; :delete<\/p>\n<p>link_to &#8220;Destroy&#8221;, team_path(@team), :confirm =&gt; &#8220;Are you sure?&#8221;, :method =&gt; :delete<\/p>\n<p>link_to_remote &#8220;Destroy&#8221;, :url =&gt; team_path(@team), :confirm =&gt; &#8220;Are you sure?&#8221;, :method =&gt; :delete<\/p>\n<p>form_for :team, @team, :url =&gt; team_path(@team), :html =&gt; { :method =&gt; :put } do |f| &#8230;<\/p><\/blockquote>\n<p>\u5176\u4e2d\u56e0\u70ba\u700f\u89bd\u5668\u4e0d\u652f\u63f4 PUT \u8ddf DELETE\uff0c\u6240\u4ee5\u662f link_to \u662f\u7528 Javascript + DOM \u5728\u6309\u4e0b\u52d5\u4f5c\u6642\u751f\u6210\u8868\u55ae form\u00a0\u8ddf _method \u53c3\u6578\u7528 HTTP POST \u4f86\u6a21\u64ec\uff0c\u800c form_for \u662f\u5728 HTML \u4e2d\u52a0\u4e0a\u96b1\u85cf\u7684 _method \u53c3\u6578\u3002\u82e5\u4f60\u95dc\u5fc3 web accessibility \u8b70\u984c\uff0c\u90a3\u9ebc link_to \u9664\u4e86HTTP GET\u4e4b\u5916\u90fd\u61c9\u6539\u7528 button_to\uff0c\u56e0\u70ba\u9019\u6a23\u5373\u4f7f Javascript \u95dc\u9589\u4e86\u9084\u53ef\u4ee5\u6b63\u78ba\u9001\u51fa(\u56e0\u70ba button_to \u7684\u00a0_method \u53c3\u6578\u662f hard-code \u5728HTML form\u88e1)\u3002anyway&#8230; \u9019\u4e9b\u90fd\u88ab helper \u96b1\u85cf\u8d77\u4f86\u4e86\uff0c\u7528\u8d77\u4f86\u4e0d\u7528\u64d4\u5fc3\uff0c\u8981\u6307\u5b9a HTTP verb \u6642\u53ea\u9700\u8a2d\u5b9a :method \u53c3\u6578\u5373\u53ef\u3002<\/p>\n<p>\u6574\u7406\u4e00\u4e0bResourse\u7684 named routes\uff0c\u53ef\u4ee5\u5206\u6210\u4e09\u7a2e\u60c5\u6cc1: collection \u7fa4\u96c6\u3001member \u67d0\u500b\u5143\u7d20\u3001new \u65b0\u5143\u7d20\uff0c\u6bcf\u7a2e\u53c8\u53ef\u4ee5\u642d\u914d\u4e0d\u540c HTTP verb\uff0c\u7136\u5f8c\u8207 Controller \u88e1\u7684\u00a0action \u914d\u5c0d\u3002\u4f8b\u5982\u9810\u8a2d\u00a0\/teams + GET \u5c0d\u61c9 action index\u3001\/teams +\u00a0POST \u5c0d\u61c9 action create\u3001\/teams\/1 \u9810\u8a2d\u5c0d\u61c9 action show\u3001 \/teams\/new \u9810\u8a2d\u5c0d\u61c9 action new\u7b49\u3002<\/p>\n<p>\u73fe\u5be6\u72c0\u6cc1\u4e2d\u9664\u4e86 GET\/POST\/PUT\/DELETE \u9019\u4e9b HTTP verb\u4e4b\u5916\uff0c\u5c0d\u8a72 resourse \u9084\u6703\u9700\u8981\u5176\u5b83\u5ba2\u88fd\u8655\u7406\uff0c\u56e0\u6b64\u9019\u4e09\u985e\u5206\u5225\u9084\u53ef\u4ee5\u52a0 method \u4e0a\u53bb\uff0c\u9996\u5148\u5728\u00a0routes.rb \u5c0d\u8a72 resource \u52a0\u8a2d\u5b9a: \u6307\u5b9a\u54ea\u7a2e\u60c5\u6cc1(:collection\/:member\/:new)\u3001\u642d\u914d\u7684 Controller action \u662f\u54ea\u500b\u3001\u7528\u4ec0\u9ebc HTTP verb\uff0c\u9019\u6a23\u5c31\u6703\u6709\u65b0\u7684 named routes \u51fa\u4f86\u4e86\uff0c\u8209\u4f8b\u5982\u4e0b:<\/p>\n<table border=\"1\">\n<tr>\n<th>\u5ba2\u88fd\u51fa helpers<\/th>\n<th>\u7522\u751f\u7684 Path<\/th>\n<th>\u5c0d\u61c9\u7684Action<\/th>\n<th>Map Options\u7684\u8a2d\u5b9a<\/th>\n<th>\u4f7f\u7528\u7684HTTP Verb<\/th>\n<\/tr>\n<tr>\n<td>sort_tags_path<\/td>\n<td>\/tags;sort<\/td>\n<td>sort<\/td>\n<td>:collection =&gt; { :sort =&gt; :put }<\/td>\n<td>PUT<\/td>\n<\/tr>\n<tr>\n<td>deactivate_tag_path(id)<\/td>\n<td>\/tag\/1;deactivate<\/td>\n<td>deactivate<\/td>\n<td>:member =&gt; { :deactivate =&gt; :delete }<\/td>\n<td>DELETE<\/td>\n<\/tr>\n<tr>\n<td>preview_new_tag_path<\/td>\n<td>\/tags\/new;preview<\/td>\n<td>preview<\/td>\n<td>:new =&gt; { :preview =&gt; :post }<\/td>\n<td>POST<\/td>\n<\/tr>\n<\/table>\n<p>\u4f60\u53ef\u80fd\u6703\u89ba\u5f97 new \u7684\u89d2\u8272\u6709\u9ede\u5947\u602a\uff0c\u60f3\u4e86\u4e00\u4e0b\u61c9\u8a72\u9019\u6a23\u770b: new\u662f\u91dd\u5c0d\u4e00\u500b(\u8cc7\u6599\u5eab\u4e2d)\u9084\u4e0d\u5b58\u5728\u4e14\u6c92\u6709id\u7684\u65b0\u5143\u7d20\uff0c\u800c member \u662f\u91dd\u5c0d\u5df2\u7d93\u5b58\u5728\u6709id\u7684\u3002<\/p>\n<p>RESTful \u4e09\u4f4d\u4e00\u9ad4\u7684\u4e00\u89d2\u662f format \u683c\u5f0f\uff0c\u6240\u4ee5\u4f60\u53ef\u4ee5\u7528 respond_to \u4f86\u8a2d\u5b9a\u4e0d\u540c\u683c\u5f0f\u8981\u600e\u9ebc\u8655\u7406:<\/p>\n<blockquote><p>respond_to { |wants| wants.all | .text | .html | .js | .ics | .xml | .rss | .atom | .yaml }<\/p><\/blockquote>\n<p>\u5b83\u6703\u6839\u64da client \u7684\u8acb\u6c42(HTTP header\u7684 Accept\u00a0\u6216 Content-Type \u6216 URL\u4e2d\u526f\u6a94\u540d\u7684\u4e0d\u540c)\u4f86\u6c7a\u5b9a\u56de\u61c9\u7684\u683c\u5f0f\uff0c\u4f8b\u5982:<\/p>\n<table border=\"1\">\n<tr>\n<th>helpers<\/th>\n<th>\u7522\u751f\u7684Path<\/th>\n<\/tr>\n<tr>\n<td>formatted_teams_path(:xml)<\/td>\n<td>\/teams.xml<\/td>\n<\/tr>\n<tr>\n<td>formatted_team_path(id,:rss)<\/td>\n<td>\/teams\/1.rss<\/td>\n<\/tr>\n<tr>\n<td>formatted_players_path(@team,:atom)<\/td>\n<td>\/teams\/1\/players.atom<\/td>\n<\/tr>\n<tr>\n<td>formatted_player_path(@team,@player, :js)<\/td>\n<td>\/teams\/1\/players\/5.js<\/td>\n<\/tr>\n<tr>\n<td>formatted_player_path(:team_id =&gt; 1, :id =&gt; 5, :format =&gt; :js )<\/td>\n<td>\/teams\/1\/players\/5.js<\/td>\n<\/tr>\n<\/table>\n<p>\u9019\u6a23\u770b\u4e0b\u4f86 Rails RESTful \u9664\u4e86\u6709\u5176<a href=\"\/blog\/archives\/1561\">\u91cd\u8981\u610f\u7fa9<\/a>\u4e4b\u5916\uff0c\u4e5f\u5e36\u4f86\u4e86\u975e\u5e38\u591a\u7684\u6163\u4f8b\u4f86\u7c21\u5316\u958b\u767c\u7a0b\u5e8f\u3002\u770b\u4f3c\u898f\u5247\u591a\uff0c\u5176\u5be6\u7d44\u5408\u5f48\u6027\u5927\uff0c\u597d\u8655\u5f88\u591a(Consistency\/Simplicity\/Discoverability)\u3002\u4e0d\u904e\u9b54\u8853\u8b8a\u591a\u4e86\uff0c\u5728\u4e00\u822c\u6c92\u6709 RESTful \u6982\u5ff5\u7684\u524d\u63d0\u4e0b\uff0c\u4e00\u958b\u59cb\u8981\u4e0a\u624b\u7684\u96e3\u5ea6\u4e5f\u63d0\u9ad8\u4e86(\u6703\u6709\u5f88\u591awhy\u9019\u6a23\u505a\u7684\u7591\u554f&#8230;:p)\u3002<\/p>\n<p>by the way&#8230; G\u5c0fR\u88dd\u7528\u529f \u4e5f\u5beb\u4e86\u4e00\u7bc7 <a href=\"http:\/\/g-little-r.blogspot.com\/2006\/12\/rails-restful.html\">Rails RESTful \u521d\u63a2<\/a> \u53ef\u4ee5\u53c3\u8003\u770b\u770b\u3002<\/p>\n<p>\u53c3\u8003\u8cc7\u6599:<\/p>\n<ul>\n<li><a href=\"http:\/\/www.ryandaigle.com\/articles\/2006\/08\/01\/whats-new-in-edge-rails-simply-restful-support-and-how-to-use-it\">What&#8217;s New in Edge Rails: Simply RESTful Support &#8211; And How to Use It<\/a><\/li>\n<li><a href=\"http:\/\/www.ryandaigle.com\/articles\/2006\/05\/30\/whats-new-in-edge-rails-restful-method-support-in-link-helpers\">What&#8217;s New in Edge Rails: RESTful Method Support in Link Helpers<\/a><\/li>\n<li><a href=\"http:\/\/caboo.se\/doc\/classes\/ActionController\/Resources.html\">ActionController::Resources Rdoc<\/a><\/li>\n<li><a href=\"http:\/\/peepcode.com\/articles\/2006\/10\/08\/restful-rails\">\u5f88\u597d\u7528\u7684 REST Cheat Sheet<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Update(2006\/12\/4): \u4e00\u4e9b\u5c0f\u4fee\u6539\u8ddf link_to \u7684 web accessibility \u88dc &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/ihower.tw\/blog\/1543-rails-restful-implement\" class=\"more-link\">\u95b1\u8b80\u5168\u6587<span class=\"screen-reader-text\">\u3008Rails RESTful \u5be6\u4f5c\u3009<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[5,53,28],"tags":[],"class_list":["post-1543","post","type-post","status-publish","format-standard","hentry","category-programming","category-rails","category-rest","entry"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p1q6tG-oT","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/ihower.tw\/blog\/wp-json\/wp\/v2\/posts\/1543","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ihower.tw\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ihower.tw\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ihower.tw\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/ihower.tw\/blog\/wp-json\/wp\/v2\/comments?post=1543"}],"version-history":[{"count":2,"href":"https:\/\/ihower.tw\/blog\/wp-json\/wp\/v2\/posts\/1543\/revisions"}],"predecessor-version":[{"id":8285,"href":"https:\/\/ihower.tw\/blog\/wp-json\/wp\/v2\/posts\/1543\/revisions\/8285"}],"wp:attachment":[{"href":"https:\/\/ihower.tw\/blog\/wp-json\/wp\/v2\/media?parent=1543"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ihower.tw\/blog\/wp-json\/wp\/v2\/categories?post=1543"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ihower.tw\/blog\/wp-json\/wp\/v2\/tags?post=1543"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}