json 使用JavaScript Fetch API将POST请求发送到本地主机上运行的FastAPI应用程序

jpfvwuh4  于 2023-06-25  发布在  Java
关注(0)|答案(1)|浏览(186)

我有一个FastAPI应用程序在我的本地机器上运行,其URL为:http://localhost:8000,使用以下Python代码:

from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
    "*"
    '''
    "http://localhost:8000/add_points/",
    "http://localhost:8000/check_points/",
    "http://localhost:8000/check_item_points/",
    "http://localhost:8000/redeem_points/"
    '''
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

users = {"matt": 0}
items = {"ticket": 7}

class User(BaseModel):
    name: str
    points: float
    item: str

class AddTransaction(BaseModel):
    name: str
    points: float

class UserPoints(BaseModel):  # anything that extnds this base model is a pyantic model
    name: str
    points: float

class Item(BaseModel):
    name: str
    points: float

# -----Post Requests-----
@app.post("/add_points/")
def add_points(add_transaction: AddTransaction):
    global users
    user_id = add_transaction.name
    points = add_transaction.points
    users[user_id] = users.get(user_id, 0) + points
    return users[user_id]

@app.post("/check_points/")
def check_points(user_points: UserPoints):
    global users
    user_id = user_points.name
    points = user_points.points
    return users[user_id], points

@app.post("/check_item_points/")
def check_item_points(item: Item):
    global items
    item_id = item.name
    points = item.points
    return item[item_id], points

@app.post("/redeem_points/")  # user spends points (they lose points) gain an item
def redeem_points(add_transaction: AddTransaction, user_points: UserPoints, item: Item, user: User):
    global users
    global items
    user_id = add_transaction.name
    user_points = user_points.points
    item_points = item.points
    item_pre = item.name
    item_post = user.item
    if user_points >= item_points:
        user_points == user_points - item_points
        item_post == item_pre
        return users[user_id], users[user_points], users[item_post]
    else:
        return "insufficient funds"

# -----Get Requests-----
@app.get("/")
def read_root():
    return {"Hello": "World"}

# -----Put Requests-----
""""
@app.put("/items/{item_id}")
def update_item(item_id: int, item:Item):
    return {"item_name": item.name, "item_id": item_id}
"""

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8000)

我还有一个HTML文件,里面有JS脚本,只需单击一个按钮就可以向http://localhost:8000/add_points/发送一个POST请求。代码如下:

<!DOCTYPE html>
<html>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<body>
<br><br><br><span style="text-align: center;"><button id="send">Send Request</button></span>
</body>

<script>
$("#send").on("click", evt => {
    $.post("http://localhost:8000/add_points/",
  {
    name: "string",
    points: 5.0
  },
  function(data, status){
    alert("Data: " + data + "\nStatus: " + status);
  });
});
</script>

</html>

然而,当我尝试发送POST请求时,我在PyCharm中得到以下错误:
INFO:127.0.0.1:49413-“OPTIONS /add_points/ HTTP/1.1”400错误请求
INFO:127.0.0.1:49413-“POST /add_points/ HTTP/1.1”422 Unprocessable Entity
我知道这些错误中至少有一个是由CORS政策限制引起的,然而,这个项目是针对移动的电话用户的,他们不应该安装任何浏览器扩展来覆盖政策。任何关于如何修复这些错误的建议将不胜感激!
编辑更新:

const url = new URL('localhost:8000/add_points/');
$("#send").on("click", evt => { fetch(url, 
      { 
        method: 'POST', 
       headers: {'Accept': 'application/json', 'Content-Type': 'application/json'}, 
          body: JSON.stringify({"name":"John", "points":50.0}) 
      }).then().catch((error) => { console.log(error.message); }); });

我仍然收到400 Bad Request错误。

ddrv8njm

ddrv8njm1#

如果您正在从Jinja2 templateHTMLResponse访问前端-这似乎是这样的,正如您的应用正在侦听的port以及您在CORSMiddleware中定义的origins所示-您不需要为您的应用启用CORS。如this answerthis answer中所述,只有当protocoldomainport全部匹配时,两个对象才具有相同的原点**。
请注意,localhost127.0.0.1被认为是不同的来源。因此,如果你从浏览器访问你的前端(通过在地址栏中键入URL)在http://127.0.0.1:8000/,那么你的异步JS请求(例如使用fetch)应该使用该域;例如:

fetch('http://127.0.0.1:8000/add',...

不是

fetch('http://localhost:8000/add',...

反之亦然。您可以始终使用相对路径来请求fetch(如下所示),因此,无论您使用哪个域名(localhost127.0.0.1)从浏览器访问前端,您的应用都将正常工作。

工作示例:

请参阅相关答案hereherehere

app.py

from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel

app = FastAPI()
templates = Jinja2Templates(directory="templates")

class Item(BaseModel):
    name: str
    points: float

@app.post("/add")
def add_points(item: Item):
    return item
    
@app.get("/")
def index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

模板/index.html

<!DOCTYPE html>
<html>
   <body>
      <input type="button" value="Submit" onclick="submit()">
      <div id="responseArea"></div>
      <script>
         function submit() {
            fetch('/add', {
                 method: 'POST',
                 headers: {
                    'Accept': 'application/json',
                    'Content-Type': 'application/json'
                 },
                 body: JSON.stringify({'name': 'John', 'points': 50.0})
              })
              .then(resp => resp.text()) // or, resp.json(), etc.
              .then(data => {
                 document.getElementById("responseArea").innerHTML = data;
              })
              .catch(error => {
                 console.error(error);
              });
         }
      </script>
   </body>
</html>

相关问题