fastapi U1S03、用浏览器登录和注册 - HTML发送post请求与跨域

这一篇会比较长,因为有一个大坑:跨域。
浏览器如果发送post请求,会有一个安全性的问题,这个时候必须有跨域。否则你发送post请求就会报错,什么307、422,各种各样的错。

我们最终要是实现的效果是:

image

不要看功能很简单,但是需要我们做的事情还蛮多的:

  • 在地址栏里输入登录页面的网址后可以进入登录页面,这需要我们的服务器对这个地址做识别,然后返回本地的html。
  • 输入账号密码,点击提交后浏览器可以发起登录的网络请求。
  • 服务器接受到登录的网络请求后,处理数据并返回成功或失败的结果

html文件

两个html文件的下载地址是:登录注册html的demo

请大家先把用到的html文件下载下来,和代码放在一起

image

文件夹里有两个html文件。因为html里面通过这个路径和名字识别文件,所以请按照相同的路径来放文件:

image

登录页面:

要获取登录页面,需要写一个get请求,返回这个html内容

@app.get("/login/",response_class=HTMLResponse,summary='获取登录的页面')
async def loginhtml():
	with open("./html/login.html","r",encoding="utf-8") as f:
		return f.read()

上面的代码写好后,运行代码,浏览器里输入地址应该就可以看到这个页面:

image

括弧:这个步骤要想跑通,需要

  1. 会写get请求,
  2. 知道怎么返回html文件给网页,
  3. 知道路径怎么用,
  4. 还需要文件放置正确,

缺一不可。这里遇到问题说明具体的流程不熟悉,可以加我微信帮你看chengguojiaoyu_su01

请务必在上面的页面出现之后再继续下面的步骤:

登录请求的接口

先看代码

浏览器里能出现登录页面之后,接下来就可以写登录请求的处理了。话不多说上代码:

#需要包含库
import json
from pydantic import BaseModel	#数据模型库

#创建一个用户的数据模型
class User(BaseModel):
	username: str
	password: str


#post请求的接口
@app.post("/user/login",summary="向服务器发起登录请求")
async def login(user:User):
	username = user.username
	password = user.password
	with open("user.json","r") as f:
		users = f.read()
	#一开始内容是空的,就会报错,所以判断一下
	if len(users)==0:
		userdic={}
	else:
		userdic = json.loads(users)
		
	#判断用户是否存在
	if username in userdic:
		if password == userdic[username]:
			return {"msg":"登录成功","success":True}
		else:
			return {"msg":"密码错","success":False}
	else:
		return {"msg":"用户名错","success":False}

注意这里的代码我只给出了post接口相关的部分,其他的可以参考最后面的完整代码

代码解析:

声明:

关于pydantic 和 json的使用,我们不多做解释。如果大家没有相关的知识储备,建议先去学一下面向对象和json解析,否则后面的内容很难理解。毕竟都学到服务器了,这俩不理解的话有点说不过去。

我这个笔记的目标是快速建立对fastapi服务器的整体性理解。之所以这么干主要是因为小孩子学Python学到这个阶段,会呢,都会一点,但是距离真正的应用差的还很远。这个时候的教学目标就不能是让他们能自己写出来,而是给他们建立一个体系化的理解,以后如果做这个东西,知道方向。很多东西必须要他们自己想要去做出来并且愿意投入精力去研究的时候,他们才能真正学会。

我们在这里用了最最简单的json来存放用户名和密码,这样做很low,也不安全,刚开始学嘛,不要搞那么花里胡哨,等你理解了整个体系,细节可以慢慢添加。

比如存储的部分不用json,改成数据库。再比如数据的传输增加一个加密。后面还可以增加登录后返回token,保持会话。

闲话就到这里,接下来解释几个重要的点

关于路径

image

关于json文件

image

success标志

image

跨域

上面的代码都写好之后,你依然会发现网页上的登录没用,会报网络错误。(就是这么蛋疼!)

跨域问题是浏览器调试一定会遇到的问题,但是我很疑惑的是基本上没有哪个fastapi的教程讲这一个问题。

或许是因为写服务器的人都认为跨域是一个不需要讲的问题?(知识诅咒啊!)

如果想要让浏览器通过post请求和服务器进行交互,我们需要在服务器上配置跨域。先看代码:

#导入的库增加
import fastapi_cdn_host
from starlette.middleware.cors import CORSMiddleware

#给app实例添加跨域
app = FastAPI()#创建一个服务器的实例(对象)
fastapi_cdn_host.patch_docs(app)

#具体的跨域配置
#配置跨域访问 添加 CORS 中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许的源
    allow_credentials=True,  # 允许带有凭证(如 Cookies)的请求
    allow_methods=["*"],  # 允许的请求方法(GET, POST, PUT, DELETE 等)
    allow_headers=["*"],  # 允许的请求头
)

这个跨域的配置说老实话具体我也不懂,不过这么设置之后浏览器和服务器确实可以跑通了。

注册部分

注册部分的原理和登录是一样样的,具体就不细说了,下面给出完整的代码做参考

from fastapi import FastAPI
from fastapi.responses import HTMLResponse
import json
from pydantic import BaseModel	#数据模型库
import fastapi_cdn_host
from starlette.middleware.cors import CORSMiddleware


app = FastAPI()#创建一个服务器的实例(对象)
fastapi_cdn_host.patch_docs(app)

#配置跨域访问 添加 CORS 中间件
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许的源
    allow_credentials=True,  # 允许带有凭证(如 Cookies)的请求
    allow_methods=["*"],  # 允许的请求方法(GET, POST, PUT, DELETE 等)
    allow_headers=["*"],  # 允许的请求头
)

class User(BaseModel):
	username: str
	password: str

@app.get("/")
async def index():
    return {"msg":"欢迎使用xxx的系统"}

@app.get("/login/",response_class=HTMLResponse,summary='获取登录的页面')
async def loginhtml():
	with open("./html/login.html","r",encoding="utf-8") as f:
		return f.read()

@app.post("/user/login",summary="向服务器发起登录请求")
async def login(user:User):
	username = user.username
	password = user.password
	with open("user.json","r") as f:
		users = f.read()
	#一开始内容是空的,就会报错,所以判断一下
	if len(users)==0:
		userdic={}
	else:
		userdic = json.loads(users)
		
	#判断用户是否存在
	if username in userdic:
		if password == userdic[username]:
			return {"msg":"登录成功","success":True}
		else:
			return {"msg":"密码错","success":False}
	else:
		return {"msg":"用户名错","success":False}

@app.get("/register/",response_class=HTMLResponse,summary='获取注册的页面')
async def registerhtml():
	with open("./html/register.html","r",encoding="utf-8") as f:
		return f.read()

@app.post("/user/register",summary='向服务器发起注册请求')
async def register(user:User):
	username = user.username
	password = user.password
	with open("user.json","r") as f:
		users = f.read()
	#一开始内容是空的,就会报错,所以判断一下
	if len(users)==0:
		userdic={}
	else:
		userdic = json.loads(users)
		
	#判断用户是否存在
	if username in userdic:
		return {"msg":"用户已存在","success":False}
	
	#如果没有这个用户,就添加数据
	userdic[username]=password
	
	#更新本地文件
	with open("user.json","w") as f:
		userjson = json.dumps(userdic,ensure_ascii=False)
		f.write(userjson)
		return {"msg":"注册成功","success":True}
	
if __name__ =='__main__':#表示直接运行当前的程序
	import uvicorn
	uvicorn.run('main:app', host='0.0.0.0', port=8100, reload=True)

posted @ 2025-01-08 11:38  一亩食堂  阅读(149)  评论(0)    收藏  举报