mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
fix(actions): support trailing slash (#12657)
* fix(actions): support trailing slash * refactoring
This commit is contained in:
parent
ccc5ad1676
commit
14dffcc3af
6 changed files with 67 additions and 3 deletions
5
.changeset/spicy-guests-protect.md
Normal file
5
.changeset/spicy-guests-protect.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Trailing slash support for actions
|
|
@ -1,6 +1,7 @@
|
||||||
import type fsMod from 'node:fs';
|
import type fsMod from 'node:fs';
|
||||||
import type { Plugin as VitePlugin } from 'vite';
|
import type { Plugin as VitePlugin } from 'vite';
|
||||||
import type { AstroSettings } from '../types/astro.js';
|
import type { AstroSettings } from '../types/astro.js';
|
||||||
|
import { shouldAppendForwardSlash } from '../core/build/util.js';
|
||||||
import {
|
import {
|
||||||
NOOP_ACTIONS,
|
NOOP_ACTIONS,
|
||||||
RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
|
RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
|
||||||
|
@ -84,6 +85,12 @@ export function vitePluginActions({
|
||||||
code += `\nexport * from 'astro/actions/runtime/virtual/server.js';`;
|
code += `\nexport * from 'astro/actions/runtime/virtual/server.js';`;
|
||||||
} else {
|
} else {
|
||||||
code += `\nexport * from 'astro/actions/runtime/virtual/client.js';`;
|
code += `\nexport * from 'astro/actions/runtime/virtual/client.js';`;
|
||||||
|
code = code.replace(
|
||||||
|
"'/** @TRAILING_SLASH@ **/'",
|
||||||
|
JSON.stringify(
|
||||||
|
shouldAppendForwardSlash(settings.config.trailingSlash, settings.config.build.format),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { ActionCalledFromServerError } from '../../../core/errors/errors-data.js';
|
import { ActionCalledFromServerError } from '../../../core/errors/errors-data.js';
|
||||||
import { AstroError } from '../../../core/errors/errors.js';
|
import { AstroError } from '../../../core/errors/errors.js';
|
||||||
|
import type { Pipeline } from '../../../core/base-pipeline.js';
|
||||||
|
import { apiContextRoutesSymbol } from '../../../core/render-context.js';
|
||||||
|
import { shouldAppendForwardSlash } from '../../../core/build/util.js';
|
||||||
|
import { removeTrailingForwardSlash } from '../../../core/path.js';
|
||||||
import type { APIContext } from '../../../types/public/index.js';
|
import type { APIContext } from '../../../types/public/index.js';
|
||||||
import { ACTION_RPC_ROUTE_PATTERN } from '../../consts.js';
|
import { ACTION_RPC_ROUTE_PATTERN } from '../../consts.js';
|
||||||
import {
|
import {
|
||||||
|
@ -279,7 +283,15 @@ export function getActionContext(context: APIContext): ActionMiddlewareContext {
|
||||||
calledFrom: callerInfo.from,
|
calledFrom: callerInfo.from,
|
||||||
name: callerInfo.name,
|
name: callerInfo.name,
|
||||||
handler: async () => {
|
handler: async () => {
|
||||||
const baseAction = await getAction(callerInfo.name);
|
const pipeline: Pipeline = Reflect.get(context, apiContextRoutesSymbol);
|
||||||
|
const callerInfoName = shouldAppendForwardSlash(
|
||||||
|
pipeline.manifest.trailingSlash,
|
||||||
|
pipeline.manifest.buildFormat,
|
||||||
|
)
|
||||||
|
? removeTrailingForwardSlash(callerInfo.name)
|
||||||
|
: callerInfo.name;
|
||||||
|
|
||||||
|
const baseAction = await getAction(callerInfoName);
|
||||||
let input;
|
let input;
|
||||||
try {
|
try {
|
||||||
input = await parseRequestBody(context.request);
|
input = await parseRequestBody(context.request);
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { parse as devalueParse, stringify as devalueStringify } from 'devalue';
|
||||||
import type { z } from 'zod';
|
import type { z } from 'zod';
|
||||||
import { REDIRECT_STATUS_CODES } from '../../../core/constants.js';
|
import { REDIRECT_STATUS_CODES } from '../../../core/constants.js';
|
||||||
import { ActionsReturnedInvalidDataError } from '../../../core/errors/errors-data.js';
|
import { ActionsReturnedInvalidDataError } from '../../../core/errors/errors-data.js';
|
||||||
|
import { appendForwardSlash as _appendForwardSlash } from '../../../core/path.js';
|
||||||
import { AstroError } from '../../../core/errors/errors.js';
|
import { AstroError } from '../../../core/errors/errors.js';
|
||||||
import { ACTION_QUERY_PARAMS as _ACTION_QUERY_PARAMS } from '../../consts.js';
|
import { ACTION_QUERY_PARAMS as _ACTION_QUERY_PARAMS } from '../../consts.js';
|
||||||
import type {
|
import type {
|
||||||
|
@ -13,6 +14,8 @@ import type {
|
||||||
export type ActionAPIContext = _ActionAPIContext;
|
export type ActionAPIContext = _ActionAPIContext;
|
||||||
export const ACTION_QUERY_PARAMS = _ACTION_QUERY_PARAMS;
|
export const ACTION_QUERY_PARAMS = _ACTION_QUERY_PARAMS;
|
||||||
|
|
||||||
|
export const appendForwardSlash = _appendForwardSlash;
|
||||||
|
|
||||||
export const ACTION_ERROR_CODES = [
|
export const ACTION_ERROR_CODES = [
|
||||||
'BAD_REQUEST',
|
'BAD_REQUEST',
|
||||||
'UNAUTHORIZED',
|
'UNAUTHORIZED',
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import { ActionError, deserializeActionResult, getActionQueryString } from 'astro:actions';
|
import {
|
||||||
|
ActionError,
|
||||||
|
deserializeActionResult,
|
||||||
|
getActionQueryString,
|
||||||
|
appendForwardSlash,
|
||||||
|
} from 'astro:actions';
|
||||||
|
|
||||||
const ENCODED_DOT = '%2E';
|
const ENCODED_DOT = '%2E';
|
||||||
|
|
||||||
|
@ -83,7 +88,15 @@ async function handleAction(param, path, context) {
|
||||||
headers.set('Content-Length', '0');
|
headers.set('Content-Length', '0');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const rawResult = await fetch(`${import.meta.env.BASE_URL.replace(/\/$/, '')}/_actions/${path}`, {
|
|
||||||
|
const shouldAppendTrailingSlash = '/** @TRAILING_SLASH@ **/';
|
||||||
|
let actionPath = import.meta.env.BASE_URL.replace(/\/$/, '') + '/_actions/' + path;
|
||||||
|
|
||||||
|
if (shouldAppendTrailingSlash) {
|
||||||
|
actionPath = appendForwardSlash(actionPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawResult = await fetch(actionPath, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body,
|
body,
|
||||||
headers,
|
headers,
|
||||||
|
|
|
@ -564,6 +564,30 @@ it('Base path should be used', async () => {
|
||||||
await devServer.stop();
|
await devServer.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should support trailing slash', async () => {
|
||||||
|
const fixture = await loadFixture({
|
||||||
|
root: './fixtures/actions/',
|
||||||
|
adapter: testAdapter(),
|
||||||
|
trailingSlash: "always"
|
||||||
|
});
|
||||||
|
const devServer = await fixture.startDevServer();
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('channel', 'bholmesdev');
|
||||||
|
formData.append('comment', 'Hello, World!');
|
||||||
|
const res = await fixture.fetch('/_actions/comment/', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(res.ok, true);
|
||||||
|
assert.equal(res.headers.get('Content-Type'), 'application/json+devalue');
|
||||||
|
|
||||||
|
const data = devalue.parse(await res.text());
|
||||||
|
assert.equal(data.channel, 'bholmesdev');
|
||||||
|
assert.equal(data.comment, 'Hello, World!');
|
||||||
|
await devServer.stop();
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Follow an expected redirect response.
|
* Follow an expected redirect response.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue