Play Framework中JavaScript变量转Scala及注入问题咨询
嘿,我来帮你拆解这个问题~首先得搞清楚核心矛盾:你用的@CSRF(routes.Some.thing(something))是服务器端模板渲染代码(比如Play Framework这类框架的语法),它在页面加载前就被服务器编译好了,而你的something是客户端JavaScript变量,服务器根本看不到它,自然会报错“something不被识别”。
能不能转换/注入JS变量到CSRF路由里?
直接在模板代码里引用JS变量是行不通的,但有几种变通方式实现类似的“动态注入”:
预生成令牌映射(适合
something值有限的场景):如果something的可能值是可控且数量不多的,你可以在服务器端模板里提前生成每个值对应的CSRF令牌和路由,把它们存在一个JS对象里,比如:// 服务器渲染时生成这个对象 const csrfRouteMap = { "user123": "@CSRF(routes.Some.thing("user123"))", "post456": "@CSRF(routes.Some.thing("post456"))" // 其他可能的something值 }; // 客户端JS里直接取用 const currentSomething = "user123"; // 你的动态变量 const csrfToken = csrfRouteMap[currentSomething];但如果
something是任意动态值(比如用户输入、随机生成的ID),这种方式就不现实了。实时请求获取对应令牌(适合动态/任意值场景):让后端提供一个接口,接收
something作为参数,返回该参数对应的CSRF令牌和完整路由URL。比如客户端JS可以这样做:async function getCsrfForSomething(something) { const response = await fetch(`/api/get-csrf?something=${encodeURIComponent(something)}`); const data = await response.json(); return { token: data.csrfToken, url: data.routeUrl }; } // 调用示例 const mySomething = "dynamicValue"; const { token, url } = await getCsrfForSomething(mySomething); // 然后用token和url发起REST请求后端需要在这个接口里,根据传入的
something生成对应的CSRF令牌和路由,再返回给前端。
能不能在JS中实时生效?
当然可以!上面说的实时请求获取对应令牌的方案就是实时生效的——你可以在JS拿到something的动态值后,立刻请求后端获取对应的CSRF信息,然后马上发起REST调用,完全不需要预先生成所有可能值的数组。
替代方案
如果不想搞这么复杂,还有两个更简单的思路:
使用全局CSRF令牌:如果你的后端支持全局CSRF令牌(而不是每个路由单独生成),可以在页面渲染时把全局令牌注入到JS里:
const globalCsrfToken = '@GLOBAL_CSRF_TOKEN'; // 后端提供的全局令牌变量然后手动构造请求URL:
/api/some/thing/${encodeURIComponent(something)},请求时把全局令牌放到请求头(比如X-CSRF-Token)里即可。不过要先确认后端是否接受这种全局令牌的验证方式。改用查询参数传递
something:把路由参数改成查询参数,这样模板里可以生成基础路由,JS再动态拼接参数:// 服务器端生成基础路由和对应令牌 const baseRoute = '@routes.Some.thing()'; const baseCsrfToken = '@CSRF(routes.Some.thing())'; // 客户端拼接参数 const fullUrl = `${baseRoute}?something=${encodeURIComponent(something)}`; // 用baseCsrfToken发起请求即可这种方式需要后端路由配置支持查询参数的形式,并且CSRF令牌对应该基础路由有效。
内容的提问来源于stack exchange,提问作者Marcus Persson

