2022-10-14

flask

python

Posted by

applemango

最終更新: 2023-03-05

残念ながら今回はロジックメインなので凝ったcssは書かないですが

待ちに待ったfrontendです

現在のファイル構成はこのようになっているはずです

main

page

.git

node_modules

pages

api

_app.tsx

index.tsx

public

styles

.eslintrc.json

next.config.js

next-env.d.ts

package.json

README.md

tsconfig.json

yarn.lock

api

venv

app.py

pagesのfileがそのままurlのpathになります

index.tsxはindex.htmlと同じように

127.0.0.1:3000/でアクセスできます(今はまだ実行してないから入れませんが

ただhtmlと違うのは.htmlなどが要らないのです。

例えば test.tsx なら /test で入れます

[id].tsxなどはflaskのrouteで言う<id>と同じような機能になります

info

https://127.0.0.1:3000/* と同じ

今回で言えば

https://127.0.0.1:3000/10 などの任意のurlでアクセス出来ます

逆にid.tsxは

https://127.0.0.1:3000/id からしかアクセス出来ません

さて、前回言ったアプリに必要な物は以下の物でしたよね

1. https://example.com/<id>(<id>は例えば"g48Wa"などが入る) 等のurlにアクセスすると設定したwebsiteに飛ばさ れること

2. httos://example.com にアクセスすると新しくurlを作れる事

とりあえずindex.tsxは既に有るので2はこれを改造したら行けそうです

1は新しくファイルを作りましょう

フォルダ名は[id].tsxにしましょう

参考までに、下現在のフォルダ

main

page

.git

node_modules

pages

api

_app.tsx

[id].tsx

index.tsx

public

styles

.eslintrc.json

next.config.js

next-env.d.ts

package.json

README.md

tsconfig.json

yarn.lock

api

venv

app.py

[id].tsxの中身を書く前にaxiosを入れておきましょう

axiosはPromiseベースのHTTPクライアントです

簡単に言うとapiなどを呼び出すのに使うライブラリです

今回は先ほど作ったbackendのapiを呼び出すのに使います

axiosを入れるのは簡単でコマンドを実行するだけです

新しくcmd(command prompt)かてterminal等を開きfrontendのフォルダーに移動します

A:\abc>cd A:\abc\osaka\main\page
apple@apple ~ % cd /abc/osaka/main/page

次は

A:\abc\osaka\main\page>npm install axios

added 9 packages, and audited 246 packages in 2s

79 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

A:\abc\osaka\main\page>
apple@apple page % npm install axios

added 9 packages, and audited 246 packages in 2s

79 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

apple@apple page %

と入力すればインストールが終わりました

せっかくcmdを開いたのですからfrontendを実行しましょう

A:\abc\osaka\main\page>npm run dev

added 9 packages, and audited 246 packages in 2s

79 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

D:\docs\trash\my-app>npm run dev

> [email protected] dev
> next dev

ready - started server on 0.0.0.0:3000, url: http://localhost:3000
event - compiled client and server successfully in 1979 ms (154 modules)
apple@apple page % npm run dev

added 9 packages, and audited 246 packages in 2s

79 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

apple@apple page % npm run dev

> [email protected] dev
> next dev

ready - started server on 0.0.0.0:3000, url: http://localhost:3000
event - compiled client and server successfully in 1979 ms (154 modules)

info

Ctrl + C で出れます

macbookの場合は

command + . で出れます

これで127.0.0.1:3000にアクセスすると表示されるはずです

さぁ[id].tsxを書きましょう

書きましょうとは言ったもののやはり書く前にある程度どのようなものを書くのかを考えましょう

1. backendからidを元にurlを取得する

2. もしurlがあるならそのurlにリダイレクトする

3. ないなら404を表示する

簡単そうですね

とりあえずリダイレクトのテストと404の表示だけしてしまいましょう

import { GetServerSideProps } from 'next'
import DefaultErrorPage from 'next/error'
import React from 'react'
import axios from 'axios'

export const getServerSideProps: GetServerSideProps = async (context: any) => {
    return {
        redirect: {
            permanent: false
            ,destination: "https://exmaple.com"
        }
    }
}

export default function none() {
    return (<DefaultErrorPage statusCode={404} />)
}

これで https://127.0.0.1:3000/[id](例 127.0.0.1:3000/test) にアクセスするとexample.comにリダイレクトされるはずです

試しにreturnを変更したら

import { GetServerSideProps } from 'next'
import DefaultErrorPage from 'next/error'
import React from 'react'
import axios from 'axios'

export const getServerSideProps: GetServerSideProps = async (context: any) => {
    return {
        props:{}
    }
}

export default function none() {
    return (<DefaultErrorPage statusCode={404} />)
}

404になるはずです

今はリダイレクトに戻しておきましょう

簡単な解説をしておくと

import { GetServerSideProps } from 'next'
import DefaultErrorPage from 'next/error'
import React from 'react'
import axios from 'axios'
export const getServerSideProps: GetServerSideProps = async (context: any) => {
    return {
        redirect: {
            permanent: false
            ,destination: "https://exmaple.com"
        }
    }
}
export default function none() {
    return (<DefaultErrorPage statusCode={404} />)
}

code 1の1行目はリダイレクトなどをするために必要な関数を定義するのに有ったら便利な物です

2行目は404を返すのに使います普通に書いても良いですが今回はサクッと作りたいのでdefaultのを使っています

3行目は今は使いませんがそのうちbackendからurlを持ってくるのに使います

code 2はリダイレクトするのに使いますasyncなのはその後非同期処理を同期処理にしたいからです

又引数のcontextは/[id]のidを取得するのに使います、今回は使いませんがその他にも/?id=10などのurl queryも取得できます

code 3 code 2 でリダイレクトされなかったときは404が返されます

さぁ次はbackendからurlを取得しましょう

その前にurlからidを/[id].tsxなどのクエリーは以下のような感じで取得できます

context.query.id

いよいよbackendから取得しましょう

今回は簡単なgetで事足ります

const res = await axios.get(`http://127.0.0.1:5000/get/${context.query.id}`)

axios.get(url) でデータを取得できます

送られてきたデータをresに代入しています

awaitはasyncの関数内のみで使用でき非同期処理を同期処理に出来ます

なぜ同期処理にするかと言うと

同期処理をしないとpromisが返されるからです

promisは送信した物であって送信して帰ってきた物では無いのです

今回は送られてきた物が必要なので同期処理をしています

promisが送られてきて困ったらとりあえずasyncからawaitで解決できます

backendが送るのは以下のjsonですが送られてきたデータの中のdataにurlが入っていますその為取り出すには以下のようにする必要が有ります

{"url":"https://example.com"}

どうやってurlを取り出すべきか

と、なります

後はurlがundefinedでは無い時だけリダイレクトするだけです

ここまでで以下のようなコードがかけます

import { GetServerSideProps } from 'next'
import DefaultErrorPage from 'next/error'
import React from 'react'
import axios from 'axios'

export const getServerSideProps: GetServerSideProps = async (context: any) => {
    const res = await axios.get(`http://127.0.0.1:5000/get/${context.query.id}`)
    if ( !res.data.url ) {
        return {
            props: {}
        }
    }
    return {
        redirect: {
            permanent: false
            ,destination: res.data.url
        }
    }
}

export default function none() {
    return (<DefaultErrorPage statusCode={404} />)
}

試しに 127.0.0.1:3000/test にアクセスしてみましょう

アクセスできました

後は新しくurlを作る機能を作ればfrontendは終わりですね

index.tsxを改造して作ろうと思いましたが

面倒なので一度index.tsxを一度すべて消してしまいましょう

解説の本筋とはあまり関係が無いのでサクッとformを作ってしまいました

後は関数、postを書くだけです

info

これからも関数はconstで定義します

functionと大差無いですがconstの方が管理やバグが少なく抑えられると言われているのでconstを使用しています

一番の違いはfunctionは最初に定義しても最後に定義しても何処からでも呼び出せるのに対しconstは定義をした後からしか呼び出せませんvarとletのような物です

warning

errorが出ますがnetwork errorなら想定内です

import type { NextPage } from "next"
import { useState } from "react"
import axios from "axios"

const Index: NextPage = () => {
    const [result, setResult] = useState("")
    const [url, setUrl] = useState("")
    const post = async () => {}
    return <div>
        <input value={url} onChange={ (e) => setUrl(e.target.value)} type="text" />
        <p>{result}</p>
        <button onClick={post}>create</button>
    </div>
}
export default Index

一応簡単な解説をしておきます

import type { NextPage } from "next"
import { useState } from "react"
import axios from "axios"
const Index: NextPage = () => {
    const [result, setResult] = useState("") //後で送られてきた短縮urlを入れる箱
    const [url, setUrl] = useState("") //入力したurlを入れる箱
    const post = async () => {} //仮定義
    // 最低限必要なelement達
    return <div>
        <input value={url} onChange={ (e) => setUrl(e.target.value)} type="text" />
        <p>{result}</p>
        <button onClick={post}>create</button>
    </div>
}
export default Index

code 1で読み込んで

code 2でformに必要な最低限を定義して

code 3でexportする

解説?と呼べるのか不思議なものではあったがとりあえずこれで良いだろう

後は先ほどと同じようaxiosを使ってにpostしよう!(さっきはget

const post = async () => {
    const res = await axios.post("http://127.0.0.1:5000/create", {
        body: JSON.stringify({"url":url})
    })
    setResult(res.data.url)
}

さっきと違い axios.getからaxios.postにかわっている

それはさっきは一方的に貰うだけだったが今回はurlを与える必要があるからだ

urlは第二の引数でbodyの中に入れて送っている

JSON.stringifyはオブジェクトなどをjsonに変換して返す

bodyはjsonでなければならないのだ

つまりやっていることは127.0.0.1:5000/create にjsonを送って送られてきたjsonをresultとして表示させているだけなのだ

{"url":url}

ここまでで以下のようなコードになっているだろう(index.tsx

import type { NextPage } from "next"
import { useState } from "react"
import axios from "axios"

const Index: NextPage = () => {
    const [result, setResult] = useState("")
    const [url, setUrl] = useState("")
    const post = async () => {
        const res = await axios.post("http://127.0.0.1:5000/create", {
            body: JSON.stringify({"url":url})
        })
        setResult(res.data.url)
    }
    return <div>
        <input value={url} onChange={(e) => setUrl(e.target.value)} type="text" />
        <p>{result}</p>
        <button onClick={post}>create</button>
    </div>
}
export default Index

warning

errorが出ますがnetwork errorなら想定内です

ほよょ?

エラーが出ましたね

AxiosError: Network Errorと出ています

ネットワークに接続しているか確認してしまいそうなerrorですがlocalhostなので関係ありません

コードを確認してもfrontend,backendともに特に問題ありません

何故でしょうか

次回、Errorの原因はCORS

エラーの原因は何なのでしょうか?乞うご期待

このドキュメントどう?

emoji
emoji
emoji
emoji