是否有一个Yesod替代Rails的`link_to foo,bar,方法:“删除”?

时间:2015-02-05 13:56:42

标签: haskell yesod

是否有一种简单的方法来生成使用与Yesod中的GET不同的方法的链接,例如在Ruby on Rails中,可以执行link_to foo, bar, method: "post",这将通过JavaScript提交链接?

更新: Ruby on Rails所做的是它生成一个data-method属性,然后由自己的JavaScript库处理。我不一定在寻找完全相同的解决方案,而是在Yesod中创建POST链接的现有机制。

2 个答案:

答案 0 :(得分:5)

  

是否有一种简单的方法可以生成使用与Yesod

中的GET不同的方法的链接

不是我意识到的。

  

在Yesod中创建POST链接的现有机制

要专门生成POST链接,您可以将表单样式呈现为链接。您可以将其包装在postLink :: Route -> Widget帮助器中。

答案 1 :(得分:4)

这是Rails正在做的大致实现。这涉及几个部分:生成链接,使用Javascript实现方法更改功能,以及后端的CSRF保护的额外代码。这是链接方面:

linkToPost :: Route App -> [(Text,Text)] -> Text -> Widget
linkToPost = linkToMethod "POST"

linkToMethod :: Text -> Route App -> [(Text,Text)] -> Text -> Widget
linkToMethod method url attributes content = 
    toWidget [hamlet|
    $newline never
    <a href="@{url}" rel="nofollow" data-method="#{method}" *{attributes}>#{content}</a>
    |]

您可以这样使用:

^{linkToPost CommentR [] "Create comment"}

此时,Javascript需要处理data-method属性。实际上你只需要包含rails.js,上面的代码就可以了。以我自己的痛苦和许多运行时错误为代价,我已经提取了相关的Javascript:

$(document).ready(function() {

  $(document).on("click", "a[data-method]", function() {
    var link = $(this);
    var method = link.data('method');

    handleMethod(link);
    return false;
  });
});

function handleMethod(link) {
  var href = link.attr('href'),
    method = link.data('method'),
    csrfToken = $('meta[name=csrf-token]').attr('content'),
    form = $('<form method="POST" action="' + href + '"></form>'),
    metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';

  if (csrfToken !== undefined) {
    metadataInput += '<input name="_token" value="' + csrfToken + '" type="hidden" />';
  }

  form.hide().append(metadataInput).appendTo('body');

  form.submit();
}

请注意,如果表单具有POST类型和_method参数的方法,则Yesod的默认middleware会将方法重写为_method的值,就像Rails一样。以上Javascript假设已启用此功能。

为了利用Rails.js正在使用的CSRF保护,我们需要将CSRF令牌嵌入到页面的<head>中。首先,我在defaultLayout函数中获取CSRF令牌:

mCsrfToken <- fmap reqToken getRequest

然后将其嵌入default-layout-wrapper.hamlet内的页眉:

$maybe token <- mCsrfToken
  <meta name="csrf-token" content=#{token}>

此时,可以应用用于表单的正常CSRF保护。这对我来说已经很晚了,但是我想出了一个简单的请求(比如DELETE / users / 1)来检查CSRF令牌的方法,可能有更好的方法:

checkCsrf :: Handler ()
checkCsrf = do
    ((res, _), _) <- runFormPost (renderDivs (mempty :: AForm Handler () ))
    case res of
        FormSuccess _ -> return ()
        _ -> permissionDenied "CSRF failure"

这是一个很长的答案+我累了,所以欢迎对上述代码进行审查。

更新

既然Yesod支持embedding CSRF tokens in cookies并在请求标头中验证它们,您可能希望使用它而不是将标记嵌入HTML并将请求作为表单。推理:

  • 查找Cookie中的值比HTML查询更简单,更受支持。
  • 添加请求标头与POSTing JSON /二进制数据等兼容。对我而言,这似乎不那么容易。
  • 所有这些都由Yesod
  • 测试明确支持