2010-08-15 50 views
6

Tôi cần một loạt các liên kết trên một trang mà mỗi liên kết tạo một POST tới một bộ điều khiển khác. Nhưng khi tôi sử dụng các liên kết bình thường, tôi nhận được một lỗi ActionController :: InvalidAuthenticityToken. Tôi hiểu điều này là do giá trị authenticity_token bị thiếu. Nhưng tôi không muốn sử dụng các biểu mẫu để thực hiện POST vì tôi muốn chúng là các liên kết chứ không phải các nút. Thực tế là, tôi muốn hoàn toàn kiểm soát phong cách của các liên kết và các nút chỉ không làm điều đó cho tôi. Cách tiêu chuẩn để làm những việc như vậy là gì?yêu cầu POST trong đường ray với siêu liên kết

Trả lời

2

Bạn có nhiều tùy chọn.

  1. Disable AT kiểm tra: protect_from_forgery :only => []
  2. Gán onclick handler cho sự liên kết và nộp biểu mẫu ẩn với javascript.
  3. Lấy AT trong khi tạo chế độ xem và thêm nó làm thông số yêu cầu.

BTW, bạn yêu cầu 'đăng' chỉ sử dụng thuộc tính 'href' chính xác như thế nào? Tôi nghĩ rằng hình thức là phải cho điều đó.

+0

Rails phép bạn chỉ định một '_method = (PUT | POST | DELETE) 'tham số truy vấn và nó sẽ giải thích yêu cầu như thể nó được thực hiện với phương thức đó. Đó là chủ yếu để hỗ trợ 'PUT' /' DELETE' mà không có hỗ trợ trình duyệt đầy đủ. –

+0

Tôi không thực sự biết các tác động bảo mật của việc vô hiệu hóa kiểm tra AT. Tôi có thể thử thêm nó như là một tham số yêu cầu mặc dù. Nhưng POST được thực hiện cho một bộ điều khiển khác so với những gì tạo ra khung nhìn này. Điều đó có quan trọng không? Phạm vi của mã thông báo này là gì? – akula1001

+0

@ manu1001 Theo như tôi hiểu, AT được tạo cho phiên cụ thể. Vì vậy, nó sẽ làm việc. Dù sao, thật dễ dàng để thử: 'my_url? Authenticity_token = <% = form_authenticity_token%>' –

1

Về mặt kỹ thuật, bạn nên sử dụng các nút và biểu mẫu cho bất kỳ thứ gì không phải là GET; siêu liên kết cố tình không cho phép các phương thức khác ngoài GET (không có các thông số như thông số _method). Một lý do rất thực tế là đôi khi, "trình tăng tốc web" trình duyệt thêm các liên kết tìm nạp trước trong trang; nếu liên kết GET khởi động một hành động đột biến, trạng thái người dùng hoặc tài nguyên có thể bị sửa đổi sai.

Điều đó nói rằng, bạn có thể tạo các nút kiểu để hoạt động như liên kết; Tôi sử dụng một cái gì đó như sau để làm điều đó khá độc đáo. Nó giả định một thiết lập lại CSS đúng với lề và đệm và tất cả những thứ tốt được làm đầy.

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

Với quy tắc như vậy, bạn có thể sử dụng <%=button_to "Yay button", something_path, :method => :post %> và nó sẽ trông và hoạt động giống như liên kết tốt.

+0

Tôi đã cân nhắc điều này nhưng nó không hiệu quả vì vẫn có hiệu ứng push gây phiền nhiễu khi bạn di chuột xuống trên một nút; cùng với một đường chấm xung quanh nó trên một số trình duyệt. – akula1001

+0

Với cài đặt lại CSS thích hợp, không phải trường hợp nào là đúng. Bạn có thể đặt 'outline: none' cho các trình duyệt hiển thị hình chữ nhật bẩn. –

0

Nếu bạn không phản đối việc sử dụng jQuery và một số ajax'n, tôi có một số blog post bao gồm điều này.

Dưới đây là một số thông tin cơ bản nếu bạn muốn có tổng quan cấp cao.

Thêm cái này bố trí của bạn:

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

Mã này cho biết thêm token auth để phản ứng. Bằng cách này, JS có thể nhặt nó lên và gửi nó đến máy chủ.

Tiếp theo chúng ta đánh chặn bất kỳ cuộc gọi ajax trong 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");  
}); 

Thêm này để điều khiển ứng dụng của bạ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 

Và theo quan điểm của bạn:

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

Trở lại trên tới 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; 
}); 

Ví dụ này thực hiện xóa nhưng thực sự là quá trình tương tự để xử lý bài đăng hoặc đặt. Bài đăng blog có ứng dụng mẫu đầy đủ thể hiện điều này.

1

Nếu bạn đang sử dụng jQuery, bạn cũng có thể làm điều gì đó như thế này:

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

Ý tưởng là bạn xây dựng một hình thức "thật" theo quan điểm của bạn, mà không có một nút gửi. Sau đó, liên kết_to với lớp "gửi" sẽ kích hoạt gửi biểu mẫu. Kết quả cuối cùng trông giống như một liên kết nhưng thực sự nó là một hình thức. Tôi chắc chắn có một cách tương tự bằng cách sử dụng Prototype.

Trong ví dụ của tôi, user_free_dates_path (: user_id => @ user.id) chỉ là một con đường helper, và hidden_field_tag ​​ là một trong những thông số mà tôi đang đi qua trong yêu cầu POST của tôi.

0

Vay nặng nề từ những câu trả lời/nguồn

để có được một nút mà chỉ trông giống như một liên kết nhưng các chức năng như một nút. Trong ứng dụng của tôi/người giúp đỡ/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; 
} 

và cuối cùng trong mẫu của tôi, tôi gọi hàm button_as_link:

button_as_link('/some/path',{:key1=>:val1,:key2=>:val2}, 'text to display') 
Các vấn đề liên quan