841-biborokuWebフロントエンドの備忘録

フィールドに設定したスラッグで記事を取得・表示・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"],
    }),
  }}
/>

参考サイト:https://www.devmaesters.com/blog/8


日付フォーマットはライブラリを使う

npm i date-fns

<time>のdatetime属性はdateTimeで指定する。

シェア