问题描述
我最近刚刚开始使用module.exports = {
pathPrefix: '/stfuandclick',// ...
}
和"deploy": "gatsby build --prefix-paths && gh-pages -d public"
,即使用户刷新页面,我也试图保持存储的会话。 (我希望可以做到)。
这个想法是,用户可以登录,并以经过身份验证的方式重定向到首页。 但是,当我在浏览器中单击“刷新”时,会话为空,用户必须再次进行登录过程。
有什么想法吗?
svelte
sapper
server.js
import sirv from "sirv";
import polka from "polka";
import compression from "compression";
import * as sapper from "@sapper/server";
import bodyParser from "body-parser";
import session from "express-session";
import sessionFileStore from "session-file-store";
const { PORT,NODE_ENV } = process.env;
const dev = NODE_ENV === "development";
const FileStore = sessionFileStore(session);
polka()
.use(
bodyParser.json(),session({
secret: "secret",resave: false,saveUninitialized: true,cookie: {
maxAge: 31536000,},store: new FileStore({
path: process.env.Now ? `/tmp/sessions` : `.sessions`,}),})
)
.use(
compression({ threshold: 0 }),sirv("static",{ dev }),sapper.middleware({
session: (req) => ({
user: req.session && req.session.user,})
)
.listen(PORT,(err) => {
if (err) console.log("error",err);
});
login.svelte
<script context="module">
export async function preload({ params },{ user }) {
if (user) {
this.redirect(302,`/`);
}
}
</script>
<script>
import { goto,stores } from "@sapper/app";
import api from "../api.js";
import Button from "../components/Button.svelte";
import Input from "../components/Input.svelte";
import InputPassword from "../components/InputPassword.svelte";
let errors;
let email;
let password;
let disabled;
const { session } = stores();
const handleSubmit = async () => {
try {
errors = null;
disabled = true;
await api.get("/csrf-cookie");
const authToken = await api.post("/login",{ email,password });
api.defaults.headers.common["Authorization"] = `Bearer ${authToken.data}`;
const user = await api.get("/me");
session.set({ user: user.data });
disabled = false;
goto("/");
} catch (e) {
errors = e;
disabled = false;
}
};
</script>
<style>
.login-form {
max-width: 35em;
margin: 5% auto;
padding: 2em;
background: rgba(233,233,0.5);
border: 1px solid rgba(151,151,0.5);
border-radius: 5px;
}
.form-title {
margin-top: 0;
font-size: 1.2em;
}
.error-block {
color: red;
}
</style>
<svelte:head>
<title>Login</title>
</svelte:head>
<div class="login-form">
<h3 class="form-title">Login</h3>
<form on:submit|preventDefault={handleSubmit}>
<Input placeholder="Username" id="email" bind:value={email} />
<InputPassword placeholder="Password" id="password" bind:value={password} />
<Button {disabled} type="submit">Login</Button>
{#if errors}<span class="error-block">{errors}</span>{/if}
</form>
</div>
我使用index.svelte
进行身份验证。
不知道我还需要提供什么来解决这个问题。
解决方法
似乎您从sapper realworld项目中提取了大部分代码(如果我错了,请纠正我),但是您忘记了实现服务器端的“ api路由”来添加新登录的代码用户参加会话。
在真实世界的项目中,当用户登录时,将向服务器端/auth/login
路由发出POST请求,该路由由以下功能提供服务:
import * as api from 'api.js';
export function post(req,res) {
const user = req.body;
api.post('users/login',{ user }).then(response => {
if (response.user) req.session.user = response.user;
res.setHeader('Content-Type','application/json');
res.end(JSON.stringify(response));
});
}
此功能的作用是:
- 它将请求转发到realworld project API的
/users/login
端点 - 如果对该请求的响应包含一个
user
对象,则会将该对象存储到服务器端会话中 - 它将原始API响应作为JSON返回给应用程序,用于在其中填充会话存储(如果原始响应包含用户对象)
考虑到您显然不使用真实世界的项目API进行身份验证,但是您自己的身份验证过程中,您需要添加的是与上述类似的服务器端路由,但是将会:
- 将登录请求中继到您自己的进程中,
- 填写响应并使用该响应设置会话用户,
- 最后将响应传递给客户端(或者,将存储的用户对象传递给会话,否则传递错误消息)。
考虑到您用于在代码的 client 端设置用户的API调用,该函数看起来像这样(例如,将该文件另存为/routes/auth/login.js
) :
import * as api from 'api.js';
export async function post(req,res) {
const { email,password } = req.body;
const authToken = await api.post("/login",{ email,password });
api.defaults.headers.common["Authorization"] = `Bearer ${authToken.data}`;
const user = await api.get("/me");
if (user) req.session.user = user.data;
res.setHeader('Content-Type','application/json');
res.end(JSON.stringify(user));
}
,并且handleSubmit
文件中的login.svelte
方法变为:
const handleSubmit = async () => {
try {
errors = null;
disabled = true;
// substitute your auth API request chain with a proxy request to
// the server-side API where you will set the server-side session user
const user = await fetch('/auth/login',{
method: 'POST',credentials: 'include',body: JSON.stringify({ email,password }),headers: { 'Content-Type': 'application/json' },})
session.set({ user: user.data });
disabled = false;
goto("/");
} catch (e) {
errors = e;
disabled = false;
}
};
请注意,在您的特定情况下,您可能还希望将auth令牌存储在会话中,以避免每次想对数据API进行身份验证的请求时都必须请求新的令牌。
,使用精简的localStorage:
创建商店,例如myStore.js
import { writable } from 'svelte/store';
export let mystore = writable({
session: ""
});
export function setSession(session) {
mystore.set({
session: session
});
session = session; // refresh UI
}
在routes / _layout.svelte中订阅
<script>
import {mystore,setSession} from './myStore.js'
let session = setSession("A_SESSION"); // here comes the session
const unsubscribeMyStore = mystore.subscribe(value => {
session = session;
});
</script>
<A_COMPONENT bind:session={mystore}/> // if the component exports session
在A_COMPONENT中使用:
<script>
export let session;
</script>
<div>
{session.session}
</div>