Rails通过javascript的fetch方法请求post或put等非get的api的时候问题


如果你用Fetch API发送非get请求给一个Rails的controller,你可能会bump into 那个 InvalidAuthenticityToken异常。
那是因为Rails需要一个特别的 CSRF token去验证这个request,你可以通过 X-CSRF-Token头传过去。
这里有个一个例子:

// Grab the CSRF token from the meta tag
const csrfToken = document.querySelector("[name='csrf-token']").content

fetch("/v1/articles", {
  method: "POST",
  headers: {
    "X-CSRF-Token": csrfToken, // 👈👈👈 Set the token
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ title: "awesome post" })
}).then(response => {
  if (!response.ok) { throw response; }
  return response.json()
}).then((data) => {
  console.log(data)
}).catch(error => {
  console.error("error", error)
})

在old-fashioned Rails apps, CSRF token是通过rails-ujs 来传输的 transparently, 所以你不需要做额外的工作。
无论如何,如果你正咋运行 Rails + React 或者 vanilla JavaScript 香草味道的JavaScript,也就是原味原生的javascript,你想在前端去fire这个原始的requests, 你将会需要上面的方式将代码 the code snippet  ,在html的markup里面去grab 抓去这个CSRF token,并把它放到headers里面传过去。

测试环境下, 页面不会生成这个csrf-token的meta tag,所以你可能就抓不到这个。
你可以将这个代码放到 utils.js里面

export function getCSRFToken() {
  const csrfToken = document.querySelector("[name='csrf-token']")

  if (csrfToken) {
    return csrfToken.content
  } else {
    return null
  }
}


在rails+stimulus.js里具体的用法就是:
#javascript/controllers/utils.js

export default function getCSRFToken() {
  const csrfToken = document.querySelector("[name='csrf-token']")

  if (csrfToken) {
    return csrfToken.content
  } else {
    return null
  }
}

不要忘记调用个getCSRFToken()  带上圆括号哦。
#./javascript/controllers/post_controller.js
import { Controller } from "@hotwired/stimulus"
import getCSRFToken from "controllers/utils"
//import getCSRFToken from "./utils"  # 这里不能用./utils,因为在生产环境下这种表达方式,会提示找不到utils文件。
...
...
fetch(this.turlValue, {
  method: "PUT",// non-GET
  headers: {"X-CSRF-Token": getCSRFToken() }
})
.then(response+> response.text())
...
...
阅读量: 749
发布于:
修改于: