2010-08-15 4 views
6

Necesito un montón de enlaces en una página cada uno de los cuales hace una POST a un controlador diferente. Pero cuando uso enlaces normales, obtengo un error ActionController :: InvalidAuthenticityToken. Entiendo que esto se debe al valor de authenticity_token faltante. Pero no quiero usar formularios para hacer el POST porque quiero que sean enlaces y no botones. El hecho es que quiero un control total sobre el estilo de los enlaces y botones, pero no lo hago por mí. ¿Cuál es la forma estándar de hacer tales cosas?haciendo la solicitud POST en rieles con hipervínculos

Respuesta

2

Tienes muchas opciones.

  1. Desactivar el momento del registro: protect_from_forgery :only => []
  2. Asignar onclick manejador al enlace y presentar formulario oculto con javascript.
  3. Capture AT mientras se genera la vista y agréguela como parámetro de solicitud.

Por cierto, ¿cómo se hacen las solicitudes 'post' usando solo el atributo 'href'? Pensé que la forma es imprescindible para eso.

+0

Carriles le permite especificar un '_method = (PUT | POST | DELETE) 'parámetro de consulta e interpretará la solicitud como si hubiera sido hecha con ese método. Es principalmente para apoyar 'PUT' /' DELETE' que no tienen soporte completo para el navegador. –

+0

Realmente no conozco las implicaciones de seguridad de deshabilitar la verificación AT. Sin embargo, podría intentar agregarlo como un parámetro de solicitud. Pero el POST se hace a un controlador diferente de lo que creó esta vista. ¿Debería eso importar? ¿Cuál es el alcance de este token? – akula1001

+0

@ manu1001 Por lo que tengo entendido, AT se crea para una sesión particular. Por lo tanto, debería funcionar. De todos modos, es fácil de probar: 'my_url? Authenticity_token = <% = form_authenticity_token%>' –

1

Técnicamente hablando, debe usar botones y formularios para cualquier cosa que no sea GET; los hipervínculos intencionalmente no permiten métodos distintos de GET (sin hacks como el parámetro _method). Una razón muy práctica es que, a veces, los complementos del navegador "web accelerator" captan previamente los enlaces en la página; si un enlace GET inicia una acción mutativa, el usuario o el estado del recurso pueden modificarse erróneamente.

Dicho esto, puede hacer que los botones se comporten como enlaces; Uso algo como lo siguiente para hacerlo bastante bien. Supone un restablecimiento CSS adecuado con márgenes y relleno y todas esas cosas buenas que se anulan.

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

Con una regla de esa manera, puede utilizar <%=button_to "Yay button", something_path, :method => :post %> y va a buscar y se comportan como un vínculo muy bien.

+0

Consideré esto, pero no funcionó porque aún hay ese molesto efecto push cuando se mueve hacia abajo en un botón; junto con una línea de puntos alrededor de él en algunos navegadores. – akula1001

+0

Con un restablecimiento CSS adecuado, ninguno de esos es el caso. Puede establecer 'outline: none' para los navegadores que muestran el rectángulo sucio. –

0

Si no se opone al uso de jQuery y algunos ajax'n, tengo un blog post que cubre esto.

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

Añadir ésta su diseño:

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

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

continuación interceptar cualquier llamada Ajax en los 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");  
}); 

Agregar 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' %> 

Volver 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 realiza una eliminación, pero en realidad es el mismo proceso para manejar las publicaciones o las publicaciones. La publicación del blog tiene una aplicación de muestra completa que demuestra esto.

1

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

<!-- 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; 
}); 

La idea es que se genera una forma "real" en su opinión, sin un botón de envío. Entonces, el link_to con la clase "submit" activará un envío de formulario. El resultado final parece un enlace, pero realmente es una forma. Estoy seguro de que hay una forma similar de usar Prototype.

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

0

préstamos en gran medida a partir de estas respuestas/fuentes

para obtener un botón que sólo se parece a un enlace, sino que funciona como un botón. En mi app/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 a la función button_as_link:

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