Niedzielski has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/395039 )
Change subject: Fix: show original server errors
......................................................................
Fix: show original server errors
When the server encounters an error, show the original error encountered
on the server instead of allowing a client side render which may cause a
different error.
Change-Id: I3603e26ffdbb8f0313c71c8c598055639b085a54
---
M src/client/index.tsx
A src/common/models/ssr-data.ts
M src/common/router/router.ts
M src/server/components/html-page.tsx
M src/server/index.tsx
M test/server/components/html-page.test.ts
6 files changed, 37 insertions(+), 4 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/marvin refs/changes/39/395039/1
diff --git a/src/client/index.tsx b/src/client/index.tsx
index 0386958..3ded82a 100644
--- a/src/client/index.tsx
+++ b/src/client/index.tsx
@@ -3,6 +3,7 @@
import "wikimedia-ui-base/wikimedia-ui-base.css";
import "./index.css";
import { RouteResponse, newRouter } from "../common/router/router";
+import { SSRData } from "../common/models/ssr-data";
import { WithContext } from "../common/components/with-context";
import { routes } from "../common/router/routes";
@@ -13,6 +14,7 @@
require("preact/debug");
}
+const ssrData: SSRData = (window as any).__SSR_DATA__;
const history = newHistory();
const router = newRouter(routes);
const pageRoot = (_ => {
@@ -43,4 +45,6 @@
// Replace the server rendered root, which does not include CSS, with a styled
// page that manages navigation with History. This enables the single page app
// experience.
-route(location.pathname);
+if (!ssrData.forceSSR) {
+ route(location.pathname);
+}
diff --git a/src/common/models/ssr-data.ts b/src/common/models/ssr-data.ts
new file mode 100644
index 0000000..a881d5b
--- /dev/null
+++ b/src/common/models/ssr-data.ts
@@ -0,0 +1,9 @@
+/**
+ * Server-side rendered data. This data is serialized by the server and
+ * deserialized by the client through the `window.__SSR_DATA__` global which is
+ * set prior to any client script execution.
+ */
+export interface SSRData {
+ /** Forbid client-side rendering of subsequently requested pages. */
+ forceSSR: boolean;
+}
diff --git a/src/common/router/router.ts b/src/common/router/router.ts
index c4b8f45..f35deff 100644
--- a/src/common/router/router.ts
+++ b/src/common/router/router.ts
@@ -6,7 +6,6 @@
Route
} from "../../common/router/route";
import HttpResponse from "../http/http-response";
-
import notFoundPage from "../pages/not-found";
import errorPage from "../pages/error";
import { RedirectError } from "../http/fetch";
diff --git a/src/server/components/html-page.tsx
b/src/server/components/html-page.tsx
index 61603ff..12fcdb6 100644
--- a/src/server/components/html-page.tsx
+++ b/src/server/components/html-page.tsx
@@ -2,6 +2,7 @@
import { PRODUCTION } from "../../common/assets/config";
import { asset } from "../../common/assets/manifest";
import { ChildrenProps } from "../../common/components/preact-utils";
+import { SSRData } from "../../common/models/ssr-data";
declare function __non_webpack_require__(name: string): any; //
eslint-disable-line camelcase
export interface Props extends ChildrenProps {
@@ -10,6 +11,7 @@
// Chunk to preload on the HTML. May not be needed if the chunks are already
// included, like the error pages
chunkName?: string;
+ ssrData: SSRData;
}
// The production asset manifest from the public build products or
@@ -23,6 +25,7 @@
export default function HTMLPage({
title = "",
chunkName,
+ ssrData,
children
}: Props): JSX.Element {
// Asset order matters.
@@ -36,6 +39,10 @@
const style = asset("index", "css", manifest);
const favicon = asset("favicon/wikipedia", "ico", manifest);
+ // Serialize the SSR data safely. See:
+ //
https://github.com/reactjs/redux/blob/cda8699/docs/recipes/ServerRendering.md#inject-initial-component-html-and-state
+ const ssrDataString = JSON.stringify(ssrData).replace(/</g, "\\u003c");
+
return (
<html lang="en">
<head>
@@ -46,6 +53,13 @@
{/* Preload the stylesheet before the scripts */}
<link rel="preload" href={style} as="style" />
<link rel="stylesheet" href={style} />
+ {/* Set the global server-side rendered data before scripts execute.
*/}
+ <script
+ type="text/javascript"
+ dangerouslySetInnerHTML={{
+ __html: `window.__SSR_DATA__ = ${ssrDataString}`
+ }}
+ />
{assets.map(path => <link rel="preload" href={path} as="script" />)}
<link rel="shortcut icon" href={favicon} />
</head>
diff --git a/src/server/index.tsx b/src/server/index.tsx
index fb30274..a4b2c0f 100644
--- a/src/server/index.tsx
+++ b/src/server/index.tsx
@@ -6,6 +6,7 @@
import { RedirectError } from "../common/http/fetch";
import { routes } from "../common/router/routes";
import { SERVER_PORT, SERVER_URL } from "../common/assets/config";
+import ErrorPage from "../common/pages/error";
import HTMLPage from "./components/html-page";
const server = express();
@@ -15,10 +16,11 @@
server.use("/public", express.static("dist/public"));
function render({ chunkName, Component, props }: RouteResponse<any>) {
+ const ssrData = { forceSSR: Component === ErrorPage.Component };
return (
"<!doctype html>" + // eslint-disable-line prefer-template
renderToString(
- <HTMLPage title="" chunkName={chunkName}>
+ <HTMLPage title="" chunkName={chunkName} ssrData={ssrData}>
<Component {...props} />
</HTMLPage>
)
diff --git a/test/server/components/html-page.test.ts
b/test/server/components/html-page.test.ts
index a9b1243..ef48709 100644
--- a/test/server/components/html-page.test.ts
+++ b/test/server/components/html-page.test.ts
@@ -7,6 +7,7 @@
const vNode = HTMLPage({
title: "Test",
chunkName: "",
+ ssrData: { forceSSR: false },
children: ["body"]
});
const html = render(vNode);
@@ -18,7 +19,11 @@
});
it("contains a <title/> when rendered", () => {
- const vNode = HTMLPage({ title: "Test", chunkName: "" });
+ const vNode = HTMLPage({
+ title: "Test",
+ chunkName: "",
+ ssrData: { forceSSR: false }
+ });
const html = render(vNode);
const expected = "<title>Test - Marvin</title>";
assert.ok(
--
To view, visit https://gerrit.wikimedia.org/r/395039
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I3603e26ffdbb8f0313c71c8c598055639b085a54
Gerrit-PatchSet: 1
Gerrit-Project: marvin
Gerrit-Branch: master
Gerrit-Owner: Niedzielski <[email protected]>
Gerrit-Reviewer: Sniedzielski <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits