-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Description
Describe the problem
I would like to use hooks to handle things like ensuring session validity. This way, I can be sure that security settings are applied sitewide, and not have to remember to apply them in every single form action handler.
But hooks don't play nicely with redirects when it's a form action handler.
Let's say you have this handler:
// hooks.server.ts
export const handle = async () => {
if (!isLoggedIn()) {
throw redirect(303, '/login');
}
};and these actions:
// +page.server.ts
export const actions = {
alwaysRedirects: async () => {
throw redirect(303, '/');
},
sometimesRedirects: async () => ({data: 'foo'}),
};and a .svelte file that has <Form use:enhance=...> and calls applyAction(result) if result.type === 'redirect'
It plays out as follows:
- If your form action is
/?alwaysRedirects, you always get redirected ✅ - If your form action is
/sometimesRedirects, and you are logged in, you get{data: 'foo'}✅ - If your form action is
/sometimesRedirects, and you are logged out, then:- If javascript is disabled, you get redirected as you would expect ✅
- if javascript is enabled,
applyAction(result)doesn't work becauseresultis{"type":"error","error":Error('SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON')'}❌
I believe this is because throw redirect() is handled differently when you're in a form action handler vs when you're in a generic endpoint. So, when you throw a redirect from inside the handle() hook, SvelteKit doesn't "know" that it's a form action, and so doesn't return the redirect information in a format that the client javascript understands. Instead, the client tries to parse the / route (which has been returned as HTML) as JSON, and fails.
Describe the proposed solution
I would like to see:
- Documentation telling me how I can achieve this using existing functionality (I have looked...)
Failing that, I would like to see:
- If you throw a redirect inside the
handle()hook, and you happen to be handling a form action at the time, the response is returned to the client in a manner that lets them redirect usingapplyAction(), as if the redirect were thrown inside the action handler itself.- This could potentially be done using my header inspection and JSON object returning workaround, discussed below. If this is a "good first issue", I'd be happy to take this on with a small pointer in the right direction.
I guess you could also have a separate hook for form actions, inside which throw redirect(...) would work as expected.
Alternatives considered
My current workaround is to inspect the request headers and, if an enhanced form is detected, return a Svelte redirect object instead of an actual redirect.
// inside handle() in hooks.server.ts
if (!isLoggedIn()) {
const javascriptFormSubmission = event.request.headers.get('x-sveltekit-action') === 'true';
if (javascriptFormSubmission) {
return new Response(
JSON.stringify({
type: 'redirect',
status: 303,
location: '/login',
} satisfies ActionResult)
);
} else {
throw redirect(303, `/login`);
}
}Importance
nice to have
Additional Information
This is currently a nice-to-have, given the workaround is pretty simple, but I thought I'd write it up here in case other people encounter the same issue.