//tips
//smart contract
CORS (Cross-Origin Resource Sharing)とは、同一生成元ポリシー (Same-Origin Policy)によって設けられた制限を緩めるもの。
同一生成元ポリシーは、JavaScriptで自由にやりとりできるところは、その JavaScript をとってきたところと同一の場所だけに制限するというものでAPIを対する制限と言えそう。
Cookie 内のセッション情報を抜き取られて不正ログインを行われたり、本来はログインしたユーザーしか実行できない記事の投稿処理を勝手にされるなどの問題を防ぐことが目的。
同一生成元かどうか判断する時には、ホスト名、スキーム、ポート番号がチェックされ、現代の主要なブラウザは全て、同一生成元ポリシーに基づく制限を実装している。
一方で、企業がウェブサービスを一般公開し、誰でも自由にウェブサービスを使ってもらえるようにしても、同一生成元ポリシーがあるので、他のJavaScript からでも自由にそのウェブサービスを使えない。
その企業のウェブサービスにアクセスして情報を取得できるのは、基本的に企業のサーバーに配置されていてユーザーが取得したJavaScript内からとなってしまう。
この制限を緩めるのがCORS。同一生成元ではない場合をクロスオリジン (Cross-Origin) という。
オリジンの概念に似ているものにドメインがあるが、下記のように
ドメイン (domain): yahoo.co.jp
オリジン (origin): https://yahoo.co.jp:443
プロトコルとポート番号を含んでいるという点でドメインとは異なる。
JavaScriptではXMLHttpRquestやFetch API を使って、サーバーに非同期的な HTTP 要求を送信することができ、HTTP のメソッドとして GET、POSTを使いContent-Type などの基本的なHTTP ヘッダーがセットされただけのリクエストであれば、単純な HTTP リクエスト(simple Cross-Origin request) として、そこまで制限がかからない。
この単純な場合は、クロスオリジンの HTTP 要求であっても、直ちにサーバーに送信され、Origin HTTP ヘッダーには、要求の送信元の情報がセット、要求を受け取ったウェブサーバーは、その要求を許可するかどうか、要求にセットされた Origin ヘッダーなどから判断。サーバーが HTTP 要求を許可する場合、Access-Control-Allow-Origin HTTP ヘッダーをセットして応答を返す。
ブラウザ上のJavaScriptランタイムは、サーバーから適切な許可があることを確認してから、レスポンスデータをユーザーのコードに渡す。許可が確認できなければ、エラー。
この単純な場合以外では、ブラウザ上の JavaScript ランタイムは、サーバーへ事前に問い合わせ、 アクセスが許可されるかどうか確認。(プリフライト Preflight という)
プリフライトは、 HTTP の OPTIONS リクエストを使って行われ、 Origin ヘッダー、Access-Control-Request-Method ヘッダや Access-Control-Request-Headers ヘッダを使う。
サーバーはこれらをチェックし、許可する場合には、Access-Control-Allow-Originヘッダ、 Access-Control-Request-MethodヘッダやAccess-Control-Request-Headers ヘッダを返す。プリフライトの有効期間となる Access-Control-Max-Age も設定される。
こうして、サーバーからのアクセス許可が確認できてから、実際のHTTP 要求が送信されることになる。
クロスオリジンの要求を行うときには、ブラウザは自動的に CORS の手順に従い、実行してくれるのでコンソールのエラー内容を理解して対処できれば問題ない。
例えば、クロスドメインのリクエストを許可する場合は、Access-Control-Allow-Origin (ACAO) HTTP ヘッダーをセットして、応答しなければならないのでnode.jsなどの場合では
app.post('/test1', (req, res) => {
console.log('/test1');
console.log(req.body);
res.set('Access-Control-Allow-Origin', '*');
…
app.options('/test1', (req, res) => {
console.log('/test1 - OPTIONS');
res.set('Access-Control-Allow-Origin', ‘*’);//wildcardは使えない場合があるので注意
res.set('Access-Control-Allow-Headers', 'X-MY-ORIGINAL');
res.set('Access-Control-Max-Age', '600');
res.send();
});
のようにヘッダーの設定を行う必要がある。
また、fetch apiを利用する際には、
fetch('https://trusted-api.co.jp', {
mode: 'cors',
credentials: 'include'
});
にするそう。
実際は cors パッケージというミドルウェアが公開されており、module importを行うのが便利なよう。
const cors = require('cors');
参考:
https://javascript.keicode.com/newjs/what-is-cors.php
JSX は、ブラウザの JavaScript エンジンで直接はサポートされておらず、reactでJSXを使うには Babel を利用することになる。これはnext.jsの生成パッケージに組み込まれている。Babel は新しい JavaScript のシンタックスなどを、多くのブラウザがサポートできるバージョンに変換するために使われる JavaScript コンパイラ。
JSXでの記述の際に、import React from 'react'; が先頭にくるが、これが必要なのは、babelでの変換後に、returnはreturn React.createElement( として変換されるため。
JSX でクラス名を指定する場合は class 属性ではなく className属性を用いる必要がある。class は予約語となっているよう。
コンソールのwarning箇所をひたすら修正していく。
<table>と<tr>の間に<tbody>を書く必要があるようで、
{data.data.map((value, key)=> (
<table>
<tbody>
<tr key={key}>
<th>{value.name}</th>
</tr>
</tbody>
</table>
))}
と修正。
Failed to load resource: net::ERR_BLOCKED_BY_CLIENTは広告ブロック機能をchromeにつけていると表示されるよう。
また、コンポーネントの引数について少し調べた。
一番簡単な引数はpropsを用いるタイプでタグの属性を取得して表示する。
props =>
<div>
僕の名前は{props.name}です!
</div>
引数を分割代入を用いて取得すると、propsを書く必要がなくなり、
({ name }) =>
<div>
僕の名前は{name}です!
</div>
とできる。
なのでexport default function Layout({ children })と会ったらprops.childrenを分割代入で分解しているのだとわかる。
Propsのname属性部分が配列な場合は下記のようになる。
({ names }) =>
<div>
僕の名前は{names[0] + names[1]}です!
</div>
オブジェクトの場合は、
const names = {
lastName: 'かわむら',
firstName: 'たかし',
}
({ names }) =>
<div>
僕の名前は{names.lastName + names.firstName}です!
</div>
分割代入されたオブジェクトをさらに分割代入するには
({ names: {lastName, firstName} }) =>
<div>
僕の名前は{lastName + firstName}です!
</div>
とすれば良い。これでdivの中身が簡潔になる。この際に別名をつけることも可能。
({ names: {lastName: l, firstName: f} }) =>
<div>
僕の名前は{l + f}です!
</div>
引数が複数の場合もベースは変わらない。
({
name1: {lastName: l1, firstName: f1},
name2: {lastName: l2, firstName: f2},
}) =>
<div>
市長の名前は{l1 + f1}です!
知事の名前は{l2 + f2}です!
</div>
またjsxの該当箇所も
const names = {
lastName: 'かわむら',
firstName: 'たかし',
}
<C names={names}/>
ではなく、キーと値が同じ場合は
<C {... {names}}/>
のように簡略化できることを覚えておく。jsxに突如{... {names}のようなものが出てきたら混乱するが知っておけば楽。
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}
これも、下記だとわかればすんなり理解できる。
export default function App({ Component, pageProps }) {
return <Component pageProps={pageProps} />
}
徐々に外部データを扱うapiについての理解が深まってきた。実際に外部apiを組み込む準備をしていく。
https://nextjs.org/learn/basics/data-fetching/request-time
https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props
構造としては下記のようになる。
export async function getServerSideProps(context) {
return {
props: {
// props for your component
}
}
}
読んでいくとClient-side Renderingが使えるように思える。
This approach works well for user dashboard pages, for example. Because a dashboard is a private, user-specific page, SEO is not relevant, and the page doesn’t need to be pre-rendered. The data is frequently updated, which requires request-time data fetching.
こちらは要検討。その際にはSWRが役に立つとのこと。
https://swr.vercel.app/docs/data-fetching
GitHub APIを叩いてファイル名を取得してみる。
サーバーサイドでは”node-fetch”というパッケージのインストールが必要との話もあったがそうでもないよう。どっちなのか。
function Page({ data }) {
// Render data...
var data=data
return(
<p>{data}</p>
)
}
// This gets called on every request
export async function getServerSideProps() { // Fetch data from external API
const res = await fetch(`https://api.aoikujira.com/time/get.php`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page
ものすごい簡単なコンポーネントを作ってみたがindex.jsにimportに読み込ませても反応しない。謎。ルートが強調されるページを見かけたが、apiでのデータ取得とルートがどのように結びつくのかがよくわかっていない。
npx create-next-app nextjs-blog --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/data-fetching-starter"