Realización de solicitud POST en carriles con hipervínculos.

Necesito un montón de enlaces en una página, cada uno de los cuales hace un POST a un controlador diferente. Pero cuando uso enlaces normales, recibo un error ActionController :: InvalidAuthenticityToken. Entiendo que esto se debe a que falta el valor de autenticidad en token. Pero no quiero usar formularios para hacer el POST porque quiero que sean enlaces y no botones. El hecho es que quiero un control completo sobre el estilo de los enlaces y los botones, pero no lo haga por mí. ¿Cuál es la forma estándar de hacer tales cosas?

Tienes muchas opciones.

  1. Desactivar la comprobación AT: protect_from_forgery :only => []
  2. Asigne el controlador onclick al enlace y envíe el formulario oculto con javascript.
  3. Agarre AT mientras genera la vista y agréguelo como parámetro de solicitud.

Por cierto, ¿cómo hace exactamente las solicitudes de ‘publicación’ utilizando solo el atributo ‘href’? Pensé que la forma es una necesidad para eso.

Técnicamente hablando, deberías usar botones y formularios para cualquier cosa que no sea un GET ; los hipervínculos no permiten intencionalmente otros métodos que no sean GET (sin hacks como el parámetro _method ). Una razón muy práctica es que, a veces, los complementos del navegador “acelerador web” realizan una búsqueda previa de enlaces en la página; Si un enlace GET inicia una acción mutativa, el estado del usuario o del recurso puede modificarse erróneamente.

Dicho esto, puedes personalizar los botones para que se comporten como enlaces; Uso algo como lo siguiente para hacerlo bastante bien. Asume un restablecimiento de CSS adecuado con márgenes y relleno y todas esas cosas buenas que se están anulando.

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

Con una regla como esa, puedes usar <%=button_to "Yay button", something_path, :method => :post %> y se verá y se comportará como un enlace muy bien.

Si está utilizando jQuery, también podría hacer algo como esto:

  <%= 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; }); 

La idea es que cree un formulario “real” en su vista, sin un botón de envío. Luego, el link_to con la clase “submit” activará un envío de formulario. El resultado final parece un enlace pero en realidad es un formulario. Estoy seguro de que hay una forma similar de usar Prototype.

En mi ejemplo, user_free_dates_path (: user_id => @ user.id) es solo un ayudante de ruta, y hidden_field_tag es uno de los parámetros que estoy pasando en mi solicitud POST.

Si no te opones al uso de jQuery y algunos ajax’n, tengo una publicación en el blog que cubre esto.

Aquí hay información básica si desea una descripción general de alto nivel.

Añade este a tu diseño:

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

Este código agrega el token de autenticación a la respuesta. De esta manera, el JS puede recogerlo y enviarlo al servidor.

A continuación interceptamos cualquier llamada ajax en 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"); }); 

Agregue esto a su controlador de aplicación:

 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 

Y en su opinión:

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

De vuelta a 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; }); 

Este ejemplo hace una eliminación, pero en realidad es el mismo proceso para manejar publicaciones o entradas. La publicación del blog tiene una aplicación de muestra completa que demuestra esto.

Préstamos fuertemente de estas respuestas / fonts

para obtener un botón que solo se ve como un enlace pero funciona como un botón. En mi aplicación / 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 https://stackoverflow.com/a/12642009/326979 */ 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; } 

y finalmente en mi plantilla, llamo la función button_as_link:

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