如何正确地将FastAPI服务器与react集成?

问题描述 投票:0回答:1

我是 React 和 Fast Api 的新手。
我不太确定我做错了什么。 我有一个快速的 api 服务器,在发布请求时,用户令牌存储在 cookie 中。 我还有一个用 aiogram 编写的电报机器人。 我的想法是,在 aiogram 中,我向服务器发送请求,用户数据将通过解密的令牌写入数据库,并在反应中通过用户令牌获取此数据,以将某些值插入到模板中。为此,我在会话中使用 cookie 存储。 aiogramm 中的查询工作正常,所有内容都存储在数据库中,令牌被解密,但在我的 React 应用程序中,我收到 403 Forbidden 错误(

127.0.0.1:8000/generation/api/data/:1 Failed to load resource: the server responded with a status of 403 (Forbidden) RedSide1.tsx:46 Ошибка от сервера: Object config : {transitional: {…}, adapter: Array(3), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …} data : {detail: 'Токен не найден в куки.'} headers : AxiosHeaders {content-length: '54', content-type: 'application/json'} request : XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: true, upload: XMLHttpRequestUpload, …} status : 403 statusText : "Forbidden" [[Prototype]] : Object)`)

你能告诉我错误的原因是什么吗???

fast_api_server/src/some_feature/router.py


from auth.jwt_handler import sign_jwt, decode_jwt
from auth.auth_bearer import JWTBearer

router = APIRouter(
    prefix='/generation',
    tags = ['ImageGeneration'] 
)

####### POST REQUESTS #######

@router.post("/generate_token/")
async def generate_token(user: UserIDSchema, response: Response):
    
    token = await sign_jwt(user_id=user.user_id)
    # Установка токена в HTTP-only cookie
    response.set_cookie(
            key="access_token", 
            value=token['access_token'],
            httponly=True,  
            secure=False,   
            samesite="Lax"  
        )
    
    return {
        "status": "200",
        "data": token,
        "details": "Token is set in cookies"
    }

@router.post('/api/add_data', dependencies=[Depends(JWTBearer())])
async def create_user_details_for_second_template(
    new_template_info: Template2Param,
    request: Request,  # Получаем объект запроса для работы с куки
    session: AsyncSession = Depends(get_async_session)
):
    try:
        # Извлекаем токен из куки
        token = request.cookies.get("access_token")
        print(token)
        if not token:
            raise HTTPException(status_code=403, detail="Токен не найден в куки.")
        
        # Декодируем токен для получения user_id
        decoded_token = decode_jwt(token)
        if not decoded_token:
            raise HTTPException(status_code=403, detail="Невалидный или истекший токен.")

        user_id = decoded_token['user_id']

        # Вставляем данные с user_id
        stmt = insert(GenerationRequest).values(user_id=user_id, **new_template_info.model_dump())
        await session.execute(stmt)
        await session.commit()

        return {
            "status": "200",
            "data": new_template_info,
            "details": "All went correct and all data have been added"
        }
    except Exception as e:
        raise HTTPException(status_code=400, detail=f"Ошибка: {str(e)}")


# Получаем данные из бд для второго шаблона
@router.get('/api/data', dependencies=[Depends(JWTBearer())])
async def get_all_data_for_second_template(request: Request, session: AsyncSession = Depends(get_async_session)):
    try:
        # Извлекаем токен из куки
        token = request.cookies.get("access_token")
        if not token:
            raise HTTPException(status_code=403, detail="Токен не найден в куки.")
        
        # Декодируем токен для получения user_id
        decoded_token = decode_jwt(token)
        if not decoded_token:
            raise HTTPException(status_code=403, detail="Невалидный или истекший токен.")
        
        user_id = decoded_token['user_id']

        # Делаем запрос в базу данных с использованием user_id
        query = select(GenerationRequestAll).where(GenerationRequestAll.user_id == user_id).order_by(GenerationRequestAll.id.desc()).limit(1)
        result = await session.execute(query)

        last_record = result.scalar_one_or_none()

        if last_record:
            return {
                "status": "200",
                "data": last_record,
                "details": "Last record fetched successfully"
            }
        else:
            return {
                "status": "404",
                "data": None,
                "details": "No records found for the user"
            }
    except Exception as e:

        raise HTTPException(status_code=400, detail=f"Error: {str(e)}")
tg_bot/some/some_handlers.py


# other code #

@staticmethod
    async def generate_unic_token(tg_id: int, client: httpx.AsyncClient) -> None:
        url_token = "link to post"
        response = await client.post(url_token, json={"user_id": tg_id})

        # Проверка ответа
        if response.status_code == 200:
            token_data = response.json()
            print("Токен успешно получен:", token_data)
        else:
            print("Ошибка при получении токена:", response.status_code, response.text)

    @staticmethod
    async def post_request_to_api(data_dict, api_url, telegram_id, message: Message) -> None:
        # Общие данные для отправки
        data_for_server = {
            "some_data":"some_data"
        }

        # Добавляем специфическое поле для 'all' типа
        if 'all' in api_url:
            data_for_server['restroom'] = data_dict['санузлы']
        else:
            data_for_server["ceiling"] = int(data_dict['потолок'])

        async with httpx.AsyncClient() as client:
            # Получаем токен и сохраняем куки в сессии
            cookies = httpx.Cookies()  # Создаем объект для хранения куки
            client.cookies = cookies  # Привязываем куки к клиенту
            
            # Получаем токен и сохраняем его в куки
            await Status.generate_unic_token(tg_id=telegram_id, client=client)

            # Используем ту же сессию для отправки данных
            response = await client.post(api_url, json=data_for_server)

            # Проверка ответа
            if response.status_code == 200:
                print("Данные успешно добавлены:", response.json())
            else:
                print("Ошибка при добавлении данных:", response.status_code, response.text)

            # Теперь делаем GET запрос, используя ту же сессию и куки
            response = await client.get('link to get to check')

            # Проверка ответа
            if response.status_code == 200:
                print("Данные успешно получены:", response.json())
            else:
                print("Ошибка при получении данных:", response.status_code, response.text)

    # отправляет запрос на добавление на сервер
    @router.message(State_Status.create_template)
    async def handle_post_request(message: Message, state: FSMContext) -> None:
        data = await state.get_data()

        if data['choosing_template'] == 'Второй_шаблон':
            api_url = "link to post"
        else:
            api_url = "link to post"

        await Status.post_request_to_api(data_dict=data, api_url=api_url, telegram_id=message.from_user.id, message=message)

        await state.clear()
        
        
 frontend/src/pages/template.tsx


import React, { useEffect, useState } from "react";
import axios from "axios";
import { useLocation } from 'react-router-dom';
import FrameComponent1 from "../components/FrameComponent1";
import FrameComponent2 from "../components/FrameComponent2";
import Button1 from "../components/Button1";
import styles from "./RedSide1.module.css";


// Определяем интерфейс данных шаблона
interface TemplateData {
  user_id: number;
  above_building: string;
  name_building: string;
  below_building: string;
  area: number;        // Заменяем BigInteger на number
  bedroom: number;     // Заменяем BigInteger на number
  floor: number;       // Заменяем BigInteger на number
  restroom: string;    // Заменяем на string
  price: number;
  sales_price: number;
  path: string[];      // Заменяем List на string[]
}

const RedSide1: React.FC = () => {
  const [data, setData] = useState<TemplateData | null>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchData = async () => {
      try 
        const response = await axios.get('link to get', {
          withCredentials: true,
          headers: {
            'Content-Type': 'application/json'
        }  
        });
        setData(response.data);
        console.log(response.data)
      } catch (error: unknown) {
          // Приведение error к типу, который можно использовать
          if (axios.isAxiosError(error)) {
              // Если это ошибка axios
              if (error.response) {
                  console.log('Ошибка от сервера:', error.response);
                  setError(`Ошибка: ${error.response.status} - ${error.response.data}`);
              } else if (error.request) {
                  console.log('Проблема с запросом:', error.request);
                  setError('Ошибка: Сервер не ответил. Проверьте соединение.');
              } else {
                  console.log('Ошибка при настройке запроса:', error.message);
                  setError(`Ошибка: ${error.message}`);
              }
          } else {
              // Если ошибка не связана с axios
              console.log('Неизвестная ошибка:', error);
              setError('Произошла неизвестная ошибка.');
          }
      }
      setLoading(false);
    };
    fetchData();
  }, []);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!data) return <div>No data available</div>;

  /// other code///
fast_api_server/src/main.py



from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddlewarefrom some_features.router import my_router

app = FastAPI(
    title="Image Generation"
)

origins = [

    # here are localhost and //127.0.0.1:5173 and //127.0.0.1:3000
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["GET", "POST", "OPTIONS", "DELETE", "PATCH", "PUT"],
    allow_headers=["Content-Type", "Set-Cookie", "Access-Control-Allow-Headers", "Access-Control-Allow-Origin",
                "Access-Control-Allow-Credential", "Authorization", "AxiosHeaders"],
)

app.include_router(some_router)

我可以提供更多代码。另外,如果我在代码生成中还有其他错误,你能指出来吗?

我更改了 CORS 并配置了 cookie,尝试通过数据库中的 telegram_id 获取某个用户的 React 服务器,但在这种情况下,有必要以某种方式将 id 传递到那里。所以我停止了将令牌存储在会话 cookie 中的选项。但这会导致 403 错误。 如果我在电报机器人中与发布请求相同的会话中发送获取请求,则获取请求有效,但它在反应中不起作用。你能帮我一下吗?

python reactjs cors fastapi session-cookies
1个回答
0
投票

JWTBearer()
尝试在
Authorization
标头中找到您的令牌,而当您将令牌存储在cookie中并在函数中执行验证时,这种情况不会发生,因此会发生错误,因为没有
Authorization
标头并且没关系,因为您使用 cookie。 您可以删除
dependencies=[Depends(JWTBearer())]
,这样应该可以正常工作。

此外,您应该正确填充

origins
中的值(我可以看到代码当前已被注释掉)

© www.soinside.com 2019 - 2024. All rights reserved.