大数据

nextjs 解析api数据

方式1:

"use client";

export default function Home() {
  async function getApiData() {
    const response = await fetch("http://127.0.0.1:8000/");
    const data = await response.json();
    console.log(data);
  }

  async function handleClick() {
    await getApiData();
  }


  return (
    <div>
      <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
        <button onClick={handleClick}>
          Lookup Data
        </button>
      </main>
    </div>

方式2:

"use client";

import {useState} from "react";

export default function Home() {
  const [datsStr, setDatsStr] = useState("");
  async function getApiData() {
    const response = await fetch("http://127.0.0.1:8000/");
    const data = await response.json();
    setDatsStr(JSON.stringify(data));
  }

  async function handleClick() {
    await getApiData();
  }


  return (
    <div>
      <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
        <button onClick={handleClick}>
          Lookup Data
        </button>
        <div>
          {datsStr}
        </div>
      </main>
    </div>

方式3:

npm install swr
import useSWR from "swr";

const fetcher = (...args) => fetch(...args).then(res => res.json());

xport default function Home() {
  const {data, error, isLoading} = useSWR("http://127.0.0.1:8000/", fetcher);
  if (isLoading) {
    return (
        <div>
          loading...
        </div>
    )
  }
  if (error) {
    return (
        <div>
          failed to load
        </div>
    )
  }

  return (
    <div>
      <main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
        {JSON.stringify(data)}
      </main>
    </div>

提交nextjs提交表单数据

formData格式

"use client";

const LOGIN_URL = "http://localhost:8000/auth/token";

export default function Page() {
    async function handleSubmit(event) {
        // 禁止页面地址跳转 会拼接form表单中的key和value到提交地址中/login?username=xxx&password=xxx
        event.preventDefault();
        // event.target <form>标签里的html内容
        console.log(event, event.target);

        // 构建表单数据
        const formData = new FormData(event.target);
        // 构建请求信息:请求方式、请求头、请求体
        const requestOptions = {
            method: "POST",
            body: formData,
        }
        const response = await fetch(LOGIN_URL, requestOptions);
        if (response.ok) {
            const rData = await response.json();
            console.log(rData);
        }
    }
    return (
        <div className="h-[95vh]">
            <div className="max-w-md mx-auto py-5">
                <h1>Login Here</h1>
                <form onSubmit={handleSubmit}>
                    <input type="text" required name="username" placeholder="Your Username" />
                    <input type="password" required name="password" placeholder="Your Password" />
                    <button type="submit">Login</button>
                </form>
            </div>
        </div>
    )
}

formdata转json格式

"use client";

const LOGIN_URL = "http://localhost:8000/auth/token";

export default function Page() {
    async function handleSubmit(event) {
        // 禁止页面地址跳转 会拼接form表单中的key和value到提交地址中/login?username=xxx&password=xxx
        event.preventDefault();
        // event.target <form>标签里的html内容
        console.log(event, event.target);

        // 构建表单数据
        const formData = new FormData(event.target);
        // 从表单中获取数据对象
        const objectFromForm = Object.fromEntries(formData);
        // 将对象转换为json格式 {"username":"xxx","password":"xxx"}
        const jsonData = JSON.stringify(objectFromForm);
        // console.log(jsonData);
        // 构建请求信息:请求方式、请求头、请求体
        const requestOptions = {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: jsonData,
        }
        const response = await fetch(LOGIN_URL, requestOptions);
        if (response.ok) {
            const rData = await response.json();
            console.log(rData);
        }
    }
    return (
        <div className="h-[95vh]">
            <div className="max-w-md mx-auto py-5">
                <h1>Login Here</h1>
                <form onSubmit={handleSubmit}>
                    <input type="text" required name="username" placeholder="Your Username" />
                    <input type="password" required name="password" placeholder="Your Password" />
                    <button type="submit">Login</button>
                </form>
            </div>
        </div>
    )
}

cookie设置

/app/login/page.jsx

"use client";

// const LOGIN_URL = "http://localhost:8000/auth/token_v2";
// 访问next js的地址/api/login 使用route.jsx可以定义不同method的请求方式来处理不同的请求 同时也可以使用USE SERVER
const LOGIN_URL = "/api/login";

export default function Page() {
    async function handleSubmit(event) {
        // 整体思路:
        //     1、获取表单数据 转换成json格式 将数据发送给 nextjs /api/login
        //     2、/api/login 获取数据将数据发送给fastapi后端获取到access_token 并将 token设置到session中

        // 禁止页面地址跳转 会拼接form表单中的key和value到提交地址中/login?username=xxx&password=xxx
        event.preventDefault();
        // event.target <form>标签里的html内容
        console.log(event, event.target);

        // 构建表单数据
        const formData = new FormData(event.target);
        // 从表单中获取数据对象
        const objectFromForm = Object.fromEntries(formData);
        // 将对象转换为json格式 {"username":"xxx","password":"xxx"}
        const jsonData = JSON.stringify(objectFromForm);
        // console.log(jsonData);
        // 构建请求信息:请求方式、请求头、请求体
        const requestOptions = {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: jsonData,
        }
        // 从next js 定义的 /api/login 地址请求数据
        const response = await fetch(LOGIN_URL, requestOptions);
        const data = await response.json();
        console.log(data);
        if (response.ok) {
            console.log("login success");
        }
    }
    return (
        <div className="h-[95vh]">
            <div className="max-w-md mx-auto py-5">
                <h1>Login Here</h1>
                <form onSubmit={handleSubmit}>
                    <input type="text" required name="username" placeholder="Your Username" />
                    <input type="password" required name="password" placeholder="Your Password" />
                    <button type="submit">Login</button>
                </form>
            </div>
        </div>
    )
}

整体思路:

1、获取表单数据 转换成json格式 将数据发送给 nextjs /api/login

2、/api/login 获取数据将数据发送给fastapi后端获取到access_token 并将 token设置到session中

/app/api/login/route.jsx

"use server";

import {cookies} from "next/headers";
import {NextResponse} from "next/server";

const FASTAPI_LOGIN_URL = "http://localhost:8000/auth/token_v2";

export async function POST(request) {
    // 获取cookie next js 13 之后需要先await
    const cookieStore = await cookies();
    const myAuthToken = cookieStore.get("auth-token");
    console.log(myAuthToken);

    // 获取/login发来的请求数据
    const requestData = await request.json();
    // 会在终端打印,不在浏览器客户端打印
    // console.log(requestData);
    // 转换json格式
    const jsonData = JSON.stringify(requestData);
    // console.log(jsonData);
    // 构建请求信息:请求方式、请求头、请求体
    const requestOptions = {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: jsonData,
    }
    // 向fastapi请求数据
    const response = await fetch(FASTAPI_LOGIN_URL, requestOptions);
    const responseData = await response.json();
    // console.log(responseData);
    if (response.ok) {
        console.log("login success");
        const authToken = responseData.access_token;
        cookieStore.set({
            name: "auth-token",
            value: authToken,
            httpOnly: true,
            sameSite: "strict",
            secure: process.env.NODE_ENV !== "development",
            maxAge: 3600
        });
    }

    return NextResponse.json({"hello": "world", "cookie": myAuthToken}, {status: 200});
}

封装auth方法

/app/lib/auth.jsx

import {cookies} from "next/headers";


const TOKEN_AGE = 3600;
const TOKEN_NAME = "auth-token";
const TOKEN_REFRESH_TOKEN = "auth-refresh-token";

// 获取 CookieStore(单例模式)
async function getCookieStore() {
    return await cookies();
}

export async function getToken() {
    // api request
    const cookieStore = await getCookieStore();
    const myAuthToken = cookieStore.get(TOKEN_NAME);
    return myAuthToken?.value;
}

export async function getRefreshToken() {
    // api request
    const cookieStore = await getCookieStore();
    const myAuthToken = cookieStore.get(TOKEN_REFRESH_TOKEN);
    return myAuthToken?.value;
}

export async function setToken(authToken) {
    // login
    const cookieStore = await getCookieStore();
    cookieStore.set({
        name: TOKEN_NAME,
        value: authToken,
        httpOnly: true,
        sameSite: "strict",
        secure: process.env.NODE_ENV !== "development",
        maxAge: TOKEN_AGE
    });
}

export async function setRefreshToken(authRefreshToken) {
    // login
    const cookieStore = await getCookieStore();
    cookieStore.set({
        name: TOKEN_REFRESH_TOKEN,
        value: authRefreshToken,
        httpOnly: true,
        sameSite: "strict",
        secure: process.env.NODE_ENV !== "development",
        maxAge: TOKEN_AGE
    });
}

export async function deleteToken() {
    // logout
    const cookieStore = await getCookieStore();
    cookieStore.delete(TOKEN_REFRESH_TOKEN);
    return cookieStore.delete(TOKEN_NAME);
}

修改/app/api/login/route.jsx

"use server";

import {NextResponse} from "next/server";
import {getRefreshToken, getToken, setRefreshToken, setToken} from "@/app/lib/auth";

const FASTAPI_LOGIN_URL = "http://localhost:8000/auth/token_v2";

export async function POST(request) {
    const myAuthToken = await getToken();
    const myRefreshAuthToken = await getRefreshToken();
    console.log(myAuthToken, myRefreshAuthToken);

    // 获取/login发来的请求数据
    const requestData = await request.json();
    // 会在终端打印,不在浏览器客户端打印
    // console.log(requestData);
    // 转换json格式
    const jsonData = JSON.stringify(requestData);
    // console.log(jsonData);
    // 构建请求信息:请求方式、请求头、请求体
    const requestOptions = {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: jsonData,
    }
    // 向fastapi请求数据
    const response = await fetch(FASTAPI_LOGIN_URL, requestOptions);
    const responseData = await response.json();
    // console.log(responseData);
    if (response.ok) {
        console.log("login success");
        const {access_token, refresh_token} = responseData;
        await setToken(access_token);
        await setRefreshToken(refresh_token);
    }

    return NextResponse.json({"hello": "world", "cookie": myAuthToken}, {status: 200});
}

logout功能实现

/app/api/logout/route.jsx

import {deleteToken} from "@/app/lib/auth";
import {NextResponse} from "next/server";

export async function POST(request) {
    await deleteToken();
    return NextResponse.json({}, {status: 200});
}

/app/logout/page.jsx

"use client";

import {useRouter} from "next/navigation";

const LOGOUT_URL = "/api/logout";

export default function Page() {
    const router = useRouter();
    async function handleClick(event) {
        event.preventDefault();

        // 构建请求信息:请求方式、请求头、请求体
        const requestOptions = {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
            },
            body: "",
        }
        // 从next js 定义的 /api/login 地址请求数据
        const response = await fetch(LOGOUT_URL, requestOptions);
        if (response.ok) {
            console.log("logout success");
            router.replace("/login");
        }
    }
    return (
        <div className="h-[95vh]">
            <div className="max-w-md mx-auto py-5">
                <h1>Are you sure you want to logout?</h1>
                <button className="bg-red-500 text-white hover:bg-red-300 px-3 py-2" onClick={handleClick}>
                    Yes, logout
                </button>
            </div>
        </div>
    )
}