フィールドに設定したスラッグで記事を取得・表示・XSS対策
記事用のテンプレを用意する
フィールド[slug]をURLに使うので、ファイル名は[slug].tsx
URL=重複NGなのでフィールドの詳細設定で「重複を許可しない」を有効にする。
※1つのAPIに対して5つのフィールドまで設定可能。
「aspidaを使ってmicroCMSの記事を取得してタイトルをリスト表示する」で作ったaspida/fetchを使う。
If a page has Dynamic Routes and uses getStaticProps, it needs to define a list of paths to be statically generated.
getStaticPaths|Next.js 日本語翻訳プロジェクト
動的ルーティングを使うときは、getStaticPathsとgetStaticPropsを両方使う。
export const getStaticPaths: GetStaticPaths = async () => {
const data = await client.blog.$get();
const paths = data.contents.map((content) => `/${content.slug}`);
return {
paths,
fallback: false,
};
}
export const getStaticProps: GetStaticProps = async (context) => {
const slug = context.params?.slug;
const data = await client.blog.$get({
query: {
filters: `slug[equals]${slug}`,
},
});
return {
props: { data },
};
};
export const getStaticPaths: GetStaticPaths = async () => {
const data = await client.blog.$get();
const paths = data.contents.map((content) => `/${content.slug}`);
return {
paths,
fallback: false,
};
}
export const getStaticProps: GetStaticProps = async (context) => {
const slug = context.params?.slug;
const data = await client.blog.$get({
query: {
filters: `slug[equals]${slug}`,
},
});
return {
props: { data },
};
};
import { format } from "date-fns";
//略
type PostPageProps = InferGetStaticPropsType<typeof getStaticProps>;
const Post: NextPage<PostPageProps> = (data: PostPageProps) => {
const post = data.data.contents[0];
const publishedAt = format(new Date(post.updatedAt), "yyyy-MM-dd");
return (
<article>
<h1 className="text-xl font-bold pb-4">{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.body }} /> //TODO:dompurifyに変更する
<time dateTime={publishedAt}>
{publishedAt}
</time>
</article>
)
}
export default Post;
リッチエディタの本文を表示する
dangerouslySetInnerHTMLをそのまま使うのは、安全性に欠けるとのことなのでXXS対策としてDOMPurifyを入れる。
npm i dompurify
dompurify__WEBPACK_IMPORTED_MODULE_3___defaul
のエラーがでた →
Note that I had to call DOMPurify.sanitize() server-side, as it assumes we’re in a Node.js environment, so I put it into getStaticProps():
How to parse Markdown in Next.js
getStaticPropsの場合、Node環境で実行していることを想定しないといけないよう。
→エラー回避をどうするかトライ中
クライアントとサーバーどちらでも使えるパッケージがあったので、「isomorphic-dompurify」を入れる。
npm i isomorphic-dompurify
import dompurify from "isomorphic-dompurify";
const alert = "hoge<script>alert();</script>hoge";
//何も表示されずscriptが実行されアラート表示
<div dangerouslySetInnerHTML={{ __html: sanitizer(alert) }}></div>
//script部分が除去されhogehogeだけ出力される
const sanitizer = dompurify.sanitize;
<div dangerouslySetInnerHTML={{ __html: sanitizer(alert) }}></div>
2023.10.13追記
microCMSのリッチエディタでリンクと別タブで開くを設定すると、target="_blank"
が入るようになりましたが、サニタライズで除去されてしまったのでtarget属性を許可する設定を追加しました。
<div
className={styles.body}
key={`${sec.fieldId}${index}`}
dangerouslySetInnerHTML={{
__html: sanitizer(sec.body, {
ADD_ATTR: ["target"],
}),
}}
/>
npm i date-fns
<time>のdatetime属性はdateTime
で指定する。