mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
16cb9ee5ef
* fix(app-router): return 405 for non-action mutations to static pages Static and SSG App Router page routes currently render HTML or RSC payloads for POST and PUT requests that are not server actions. That diverges from Next.js and makes unsupported mutation methods look successful. The App Router page render path only handled server actions and route handlers before falling through to page rendering. Add a small page-method policy helper and call it from the generated RSC entry before page rendering so static and SSG candidates return 405 with Allow: GET, HEAD while possible server action POSTs still pass through. Tests cover the helper policy and the live App Router fixture for static and ISR pages. * fix(app-router): preserve 405 allow header Set the App Router page 405 Allow header after merging middleware response headers so middleware cannot clobber the methods advertised by the generated Method Not Allowed response. Add a focused regression test that keeps middleware header propagation while asserting Allow remains GET, HEAD.
98 lines
2.9 KiB
TypeScript
98 lines
2.9 KiB
TypeScript
import { describe, expect, it } from "vite-plus/test";
|
|
import {
|
|
isStaticOrSsgAppPageCandidate,
|
|
resolveAppPageMethodResponse,
|
|
} from "../packages/vinext/src/server/app-page-method.js";
|
|
|
|
describe("app page method policy", () => {
|
|
it("returns 405 with Allow for non-action mutation requests to static candidates", async () => {
|
|
const response = resolveAppPageMethodResponse({
|
|
hasGenerateStaticParams: false,
|
|
isDynamicRoute: false,
|
|
request: new Request("https://example.com/about", { method: "POST" }),
|
|
revalidateSeconds: null,
|
|
});
|
|
|
|
if (!response) {
|
|
throw new Error("Expected a Method Not Allowed response");
|
|
}
|
|
expect(response.status).toBe(405);
|
|
expect(response.headers.get("allow")).toBe("GET, HEAD");
|
|
await expect(response.text()).resolves.toBe("Method Not Allowed");
|
|
});
|
|
|
|
it("preserves possible server action POSTs", () => {
|
|
const response = resolveAppPageMethodResponse({
|
|
hasGenerateStaticParams: false,
|
|
isDynamicRoute: false,
|
|
request: new Request("https://example.com/about", {
|
|
headers: { "next-action": "abc123" },
|
|
method: "POST",
|
|
}),
|
|
revalidateSeconds: null,
|
|
});
|
|
|
|
expect(response).toBeNull();
|
|
});
|
|
|
|
it("does not let middleware headers override the 405 Allow header", () => {
|
|
const middlewareHeaders = new Headers({
|
|
Allow: "POST",
|
|
"x-from-middleware": "1",
|
|
});
|
|
|
|
const response = resolveAppPageMethodResponse({
|
|
hasGenerateStaticParams: false,
|
|
isDynamicRoute: false,
|
|
middlewareHeaders,
|
|
request: new Request("https://example.com/about", { method: "PUT" }),
|
|
revalidateSeconds: null,
|
|
});
|
|
|
|
if (!response) {
|
|
throw new Error("Expected a Method Not Allowed response");
|
|
}
|
|
expect(response.headers.get("allow")).toBe("GET, HEAD");
|
|
expect(response.headers.get("x-from-middleware")).toBe("1");
|
|
});
|
|
|
|
it("treats ISR and generateStaticParams routes as SSG candidates", () => {
|
|
expect(
|
|
isStaticOrSsgAppPageCandidate({
|
|
hasGenerateStaticParams: false,
|
|
isDynamicRoute: false,
|
|
revalidateSeconds: 60,
|
|
}),
|
|
).toBe(true);
|
|
|
|
expect(
|
|
isStaticOrSsgAppPageCandidate({
|
|
hasGenerateStaticParams: true,
|
|
isDynamicRoute: true,
|
|
revalidateSeconds: null,
|
|
}),
|
|
).toBe(true);
|
|
});
|
|
|
|
it("does not guard force-dynamic or revalidate zero pages", () => {
|
|
expect(
|
|
resolveAppPageMethodResponse({
|
|
dynamicConfig: "force-dynamic",
|
|
hasGenerateStaticParams: false,
|
|
isDynamicRoute: false,
|
|
request: new Request("https://example.com/dynamic", { method: "PUT" }),
|
|
revalidateSeconds: null,
|
|
}),
|
|
).toBeNull();
|
|
|
|
expect(
|
|
resolveAppPageMethodResponse({
|
|
hasGenerateStaticParams: false,
|
|
isDynamicRoute: false,
|
|
request: new Request("https://example.com/no-store", { method: "PUT" }),
|
|
revalidateSeconds: 0,
|
|
}),
|
|
).toBeNull();
|
|
});
|
|
});
|