POST request on rails with hyperlinks

I need a bunch of links on the page, each of which does POST for another controller. But when I use regular links, I get an ActionController :: InvalidAuthenticityToken error. I understand that this is due to the lack of an authenticity_token value. But I do not want to use forms to do POST, because I want them to be links, not buttons. The fact is, I want full control over the style of links and buttons just not doing it for me. What is the standard way to do such things?

+6
post ruby-on-rails forms hyperlink
source share
5 answers

You have many options.

  • Disable AT check: protect_from_forgery :only => []
  • Assign an onclick handler to the link and submit the hidden form using javascript.
  • Take AT when creating the view and add it as a query parameter.

By the way, how exactly do you make "post" requests using only the "href" attribute? I thought form was necessary for this.

+2
source share

From a technical point of view, you should use buttons and forms for everything that is not GET ; hyperlinks intentionally do not allow methods other than GET (without hacks, such as the _method parameter). One very practical reason is that sometimes the web accelerator of the browser adds prefetch links to the page; if a GET link triggers an action with a mutation, the user or resource status may be erroneously changed.

However, you can style buttons to behave like links; For this, I use something like the following. It assumes the correct CSS reset with margins and indentation and all that distinguishes the desired material.

 input.restlink { border: 0; background: #fff; color: #236cb0; cursor: pointer; } input.restlink:hover { text-decoration: underline; } 

With this rule, you can use <%=button_to "Yay button", something_path, :method => :post %> , and it will look and behave like a link just fine.

+1
source share

If you use jQuery, you can also do something like this:

 <!-- in your view --> <%= form_tag( user_free_dates_path(:user_id => @user.id), :remote => true ) do |f| %> <%= hidden_field_tag :date, current_day.to_s(:short_db) %> <%= link_to "Allow", "#", :class => "submit"%> <% end %> // in application.js $("form a.submit").live("click", function(){ $(this).closest("form").submit(); return false; }); 

The idea is that you create a β€œreal” form in your view without a submit button. Then link_to with the class "submit" will cause the form to submit. The end result looks like a link, but actually it is a form. I am sure that a similar method is used with Prototype.

In my example, user_free_dates_path (: user_id => @ user.id) is just a path helper, and hidden_field_tag ​​is one of the parameters that I'm passing my POST request to.

+1
source share

If you don't mind using jQuery and some ajax'n, I have a blog post that covers this.

Here is some basic information if you want a high level overview.

Add this layout:

 <%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %> 

This code adds an authentication token to the response. That way, JS can pick it up and send it to the server.

Then we intercept any ajax call in application.js:

 function isPost(requestType) { return requestType.toLowerCase() == 'post'; } $(document).ajaxSend(function(event, xhr, settings) { if (isPost(settings.type)) { settings.data = (settings.data ? settings.data + "&" : "") + "authenticity_token=" + encodeURIComponent( AUTH_TOKEN ); } xhr.setRequestHeader("Accept", "text/javascript, application/javascript"); }); 

Add this to the application controller:

 before_filter :correct_safari_and_ie_accept_headers after_filter :set_xhr_flash protected def set_xhr_flash flash.discard if request.xhr? end def correct_safari_and_ie_accept_headers ajax_request_types = ['text/javascript', 'application/json', 'text/xml'] request.accepts.sort!{ |x, y| ajax_request_types.include?(y.to_s) ? 1 : -1 } if request.xhr? end 

And in your opinion:

 <%= link_to "Delete", delete_product_path(product), :class => 'delete' %> 

Back to application.js:

 $('a.delete').live('click', function(event) { if(event.button != 0) { return true; } $.post(link.attr('href').substring(0, link.attr('href').indexOf('/delete')), { _method: "delete" }); return false; }); 

This example performs the deletion, but it is actually the same process for processing messages or tags. The blog has a complete sample application that demonstrates this.

0
source share

Borrowing heavily from these answers / sources

to get a button that just looks like a link but functions like a button. In my application / helpers / application _helper.rb:

 def button_as_link(action, link_text, hiddens) capture do form_tag(action, :class => 'button_as_link') do hiddens.each { |k, v| concat hidden_field_tag(k.to_sym, v) } concat submit_tag(link_text) end end end 

CSS

 form.button_as_link { display: inline; } /* See /questions/35085/how-to-make-button-look-like-a-link/255814#255814 */ form.button_as_link input[type="submit"], form.button_as_link input[type="submit"]:focus, form.button_as_link input[type="submit"]:active { /* Remove all decorations to look like normal text */ background: none; border: none; display: inline; font: inherit; margin: 0; padding: 0; outline: none; outline-offset: 0; /* Additional styles to look like a link */ color: blue; cursor: pointer; text-decoration: underline; } /* Remove extra space inside buttons in Firefox */ form.button_as_link input[type="submit"]::-moz-focus-inner { border: none; padding: 0; } 

and finally, in my template, I call the button_as_link function:

 button_as_link('/some/path',{:key1=>:val1,:key2=>:val2}, 'text to display') 
0
source share

All Articles