我有一个带有 Svelte 集成的 AstroJS SSR 应用程序。我正在 Svelte 中创建一个用户注册表单,并将其提交到 Astro API 端点来注册用户。由于表单已
POST
ed,我想在注册完成后重定向到另一个页面。然而,即使 Astro 说它做了重定向,我的应用程序仍停留在同一页面上。我需要帮助找出原因。
这是我的 Astro 页面,定义于
src/pages/signup.astro
:
---
import MainLayout from '@/layouts/Main.astro';
import SignupForm from "@/components/auth/SignupForm.svelte";
---
<MainLayout>
<SignupForm client:load />
</MainLayout>
这将创建表单,其定义在
src/components/SignupForm.svelte
:
<script>
import * as yup from "yup";
let email = null;
let password = null;
let errors = {};
function validate()
{
const schema = yup.object().shape({
email: yup.
string().
required("We need your email to sign you up").
email("Your email must be of the form <b>[email protected]</b>"),
password: yup.
string().
required("We need a password to sign you up").
min(8, "Your password must be at least 8 characters long")
});
try
{
errors = {};
schema.validateSync({ email, password }, { abortEarly: false });
return true;
}
catch (e)
{
e.inner.forEach(error => {
errors[error.path] = error.message;
});
return false;
}
}
async function onSubmit(e)
{
if (!validate())
{
return;
}
const form = new FormData(e.currentTarget);
const response = await fetch("/api/signup", {
method: "POST",
body: form,
});
const json = await response.json();
console.log(`in svelte form response: ${JSON.stringify(json)}`);
}
</script>
<div class="flex flex-col items-center m-4">
<form class="p-8 border border-gray-400 max-w-lg lg:w-1/3 rounded-lg bg-gray-200" novalidate on:submit|preventDefault={onSubmit}>
<div class="my-4">
<label for="email" class="mb-2 block text-sm font-bold">Email</label>
<input
type="email"
name="email"
placeholder="[email protected]"
class="w-full shadow appearance-none border rounded p-3"
bind:value={email}
>
{#if errors.email}
<p class="my-2 text-sm text-red-500">{@html errors.email}</p>
{/if}
</div>
<div class="my-4">
<label for="password" class="mb-2 block text-sm font-bold text-gray-700">Password</label>
<input
type="password"
name="password"
placeholder="shhh . . . don't tell anyone"
class="w-full shadow appearance-none border rounded p-3"
bind:value={password}
>
{#if errors.password}
<p class="my-2 text-sm text-red-500">{errors.password}</p>
{/if}
</div>
<div class="my-4">
<button class="mt-4 bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Signup</button>
</div>
</form>
</div>
提交此表单会进行客户端验证,如果顺利的话,
POST
会将表单发送至注册 API,网址为 src/pages/api/signup.js
:
import Auth from "@/services/auth";
export const POST = async({request, redirect}) => {
const form = await request.formData();
const email = form.get("email");
const password = form.get("password");
const response = Auth.signup(email, password);
if (response.error)
{
return new Response(
JSON.stringify(response),
{ status: response.error.code }
);
}
return redirect("/");
};
此代码是后端 API 代码,调用函数来注册用户并接收成功或错误响应。如果响应指示成功,则它会重定向到主页(理想情况下,这不应该重定向here,因为这只是一个 API,API 不知道有关重定向的任何信息;重定向应该由服务器发起,但这就是下次)。然而——这就是问题所在——页面实际上并没有重定向到主页。在控制台中,我看到 Astro 肯定receives
302
和 says 它重定向了,但浏览器实际上并没有转到主页。
18:33:48 [200] /signup 4ms
signing up new user with email [email protected] and password ftjjkyhgktjyt
21:24:08 [302] POST /api/signup 2ms
21:24:09 [200] / 3ms
这是我的
astro.config.mjs
文件,以防有人想看:
import { defineConfig } from 'astro/config';
import node from "@astrojs/node";
import svelte from "@astrojs/svelte";
// https://astro.build/config
export default defineConfig({
devToolbar: {
enabled: import.meta.env.DEV
},
output: "server",
adapter: node({
mode: "hybrid"
}),
integrations: [svelte()]
});
您需要决定选择您似乎混为一谈的两种方法之一(取决于您可能更喜欢其中一种的用例):
提交 HTML 表单(MPA 中的典型)
由于表单已发布,我想在注册完成后重定向到另一个页面。
这就是使用 HTML
<form method="POST"
在 MPA(多页面应用程序)中执行操作的方式,其中服务器响应的表单提交的响应由浏览器呈现为新页面。
参见 https://docs.astro.build/en/concepts/why-astro/#server-first 和 https://docs.astro.build/en/recipes/build-forms/
从 JavaScript 进行 HTTP 调用(SPA 的典型)
但是,
fetch("/api/signup", { method: "POST"
不会这样做。 fetch
是 JavaScript HTTP 调用,服务器通常会返回一些 JSON。这通常是您在 SPA(单页应用程序)中执行操作的方式。通常,在 JSON API 中返回重定向是没有意义的。在这种情况下,您想要做的是以 JSON 形式从服务器返回成功或错误消息,然后在您的 svelte 组件中向用户显示该消息,而无需浏览器加载新页面。