是否有一种简单的方法来生成使用与Yesod中的GET
不同的方法的链接,例如在Ruby on Rails中,可以执行link_to foo, bar, method: "post"
,这将通过JavaScript提交链接?
更新: Ruby on Rails所做的是它生成一个data-method
属性,然后由自己的JavaScript库处理。我不一定在寻找完全相同的解决方案,而是在Yesod中创建POST
链接的现有机制。
答案 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并将请求作为表单。推理: