(02)fastapi学习——请求表单文件、依赖项、安全校验学习

前言

一、请求表单

1.1 登录案例

1.2 登录案例GET与POST的请求方式 

二、请求文件

三、依赖项

3.1 请求体更新数据

3.2 什么是依赖注入? 

FastAPI 兼容性 依赖注入系统如此简洁的特性,让 FastAPI 可以与下列系统兼容:

3.3 子依赖

3.4 路径操作装饰器依赖项

3.5 全局依赖项 

四、安全校验

4.1 安全校验基础知识

 4.2 使用密码和 Bearer 的简单 OAuth2

4.3 展示登录案例 

参考文献

前言

本博文将继续学习请求表单、请求文件,依赖项、安全校验等相关知识的学习,并结合已有的资料进行学习记录。具体如下所示:

一、请求表单

参考:FastAPI 学习之路(十六)Form表单_fastapi formdata-CSDN博客

1.1 登录案例

通过账户密码的方式实现账号登录操作。代码分为前端和后端、前端通过html+css+js实现,后端通过fastapi实现。

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login Form</title>
</head>
<body>
    <h2>Login Form</h2>
    <form id="loginForm">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" required>
        <br><br>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password" required>
        <br><br>
        <button type="submit">Login</button>
    </form>
    <p id="response"></p>
    <script>
        // 获取表单和响应显示区域
        const loginForm = document.getElementById('loginForm');
        const responseParagraph = document.getElementById('response');
        // 监听表单的提交事件
        loginForm.addEventListener('submit', async function (event) {
            event.preventDefault(); // 阻止表单提交的默认行为
            // 获取表单数据
            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;
            // 构建要发送的数据对象
            const loginData = {
                username: username,
                password: password
            };
            try {
                // 发送POST请求到FastAPI后端
                const response = await fetch('http://127.0.0.1:8080/userlogin/requestlogindemo2', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify(loginData)
                });
                // 处理响应
                const result = await response.json();
                if (response.status === 200) {
                    // 如果登录成功,显示成功消息
                    responseParagraph.innerText = 'Login Successful: ' + result.msg;
                } else {
                    // 如果登录失败,显示错误消息
                    responseParagraph.innerText = 'Login Failed: ' + result.detail;
                }
            } catch (error) {
                // 捕获和处理任何网络错误
                responseParagraph.innerText = 'Error: ' + error.message;
            }
        });
    </script>
</body>
</html>

fastapi后端代码:

from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
router = APIRouter()
class UserLogin(BaseModel):
    username: str
    password: str

@router.get("/userlogin/hello")
async def userlogin_hello():
    print("这是一个userlogin_hello函数的get请求,现在我想返回一个200的状态值")
    # 返回状态值200 封装返回
    return {"status": 200, "msg": "这是一个userlogin_hello函数的get请求,现在我想返回一个200的状态值"}

@router.get("/userlogin/requestlogin")
async def userlogin_requestlogin(username:str,password:str): # 接收用户名和密码
    if username == "admin" and password == "admin":
        print("当前进入到userlogin_requestlogin函数中,用户名和密码正确")
        return {"status": 200, "msg": "登录成功"}
    else:
        print("当前进入到userlogin_requestlogin函数中,用户名和密码错误")
        raise HTTPException(status_code=401, detail="用户名或密码错误")

# POST 请求,传递 JSON 或者表单数据
@router.post("/userlogin/requestlogindemo2")
async def userlogin_requestlogin_demo2(userlogin: UserLogin):  # 接收 JSON 或表单数据
    if userlogin.username == "admin" and userlogin.password == "admin":
        print("当前进入到userlogin_requestlogindemo2函数中,用户名和密码正确")
        return {"status": 200, "msg": "登录成功"}
    else:
        print("当前进入到userlogin_requestlogindemo2函数中,用户名和密码错误")
        raise HTTPException(status_code=401, detail="用户名或密码错误")

 在app.py文件中设置跨越访问,跨域访问在后面的文章中将会进行详细的说明和介绍。

# 允许跨域请求
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许所有来源
    allow_credentials=True,
    allow_methods=["*"],  # 允许所有HTTP方法
    allow_headers=["*"],  # 允许所有HTTP头
)

1.2 登录案例GET与POST的请求方式 

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--表单直接这样访问get的形式-->
<form action="http://127.0.0.1:8080/passport/user/loginget" method="get">
    <input type="text" name="username" value="admin">
    <input type="password" name="password" value="admin">
    <button type="submit">Login</button>
</form>
<form action="http://127.0.0.1:8080/passport/user/loginpost_form" method="post">
    <input type="text" name="username" value="admin">
    <input type="password" name="password" value="admin">
    <button type="submit">Login</button>
</form>
</body>
</html>

后端代码:

'''
我要进行接口测试,这个部分用于反馈请求信息
'''
import os
import shutil
from fastapi import APIRouter, HTTPException, Depends,Form,UploadFile,File,Request,Response
from pydantic import BaseModel
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
router = APIRouter()
class UserLogin(BaseModel):
    username: str
    password: str

loginuserinfo={}
@router.post("/passport/user/loginpost")
async def passport_user_login_post(userlogin: UserLogin):
    if userlogin.username == "admin" and userlogin.password == "admin":
        print("当前进入到passport_user_login_post函数中,用户名和密码正确")
        return {"status": 200, "msg": "登录成功"}
    else:
        print("当前进入到passport_user_login_post函数中,用户名和密码错误")
        return {"status": 401, "msg": "登录失败"}

@router.get("/passport/user/loginget")
async def passport_user_login_get(username:str,password:str,response:Response):
    if username == "admin" and password == "admin":
        print("当前进入到passport_user_login_get函数中,用户名和密码正确")
        # 登录成功之后需要把用户信息存放到cookie中
        # response = JSONResponse(content={"status": 200, "msg": "登录成功"})
        # response.set_cookie(key="username", value=username)
        # 由于是本地,因此用字典模仿一下
        loginuserinfo["username"] = username
        return {"status": 200, "msg": "登录成功"}
    else:
        print("当前进入到passport_user_login_get函数中,用户名和密码错误")
        return {"status": 401, "msg": "登录失败"}

@router.post("/passport/user/loginpost_form")
async def passport_user_login_post_form(username: str = Form(...), password: str = Form(...)):
    if username == "admin" and password == "admin":
        print("当前进入到passport_user_login_post_form函数中,用户名和密码正确")
        return {"status": 200, "msg": "登录成功"}
    else:
        print("当前进入到passport_user_login_post_form函数中,用户名和密码错误")
        return {"status": 401, "msg": "登录失败"}

效果如图所示:

 

二、请求文件

FastAPI 学习之路(十七)上传文件_fastapi 上传文件-CSDN博客

FastAPI 学习之路(十八)表单与文件_fastapi formdata-CSDN博客

前端代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>用户上传文件</title>
</head>
<body>
<form action="http://127.0.0.1:8080/passport/upload" method="POST" enctype="multipart/form-data">
    <input type="file" name="file">
    <button type="submit">Upload</button>
</form>
</body>
</html>

后端代码:

@router.post("/passport/upload")
async def passport_upload_file(file: UploadFile = File(...)):
    # 创建保存上传文件的目录
    UPLOAD_DIR = "uploaded_files"
    if not os.path.exists(UPLOAD_DIR):
        os.makedirs(UPLOAD_DIR)
    print("当前进入到passport_upload_file函数中,上传文件成功")
    # 构建保存文件的路径
    file_location = os.path.join(UPLOAD_DIR, file.filename)
    # 将文件保存到服务器
    with open(file_location, "wb") as buffer:
        shutil.copyfileobj(file.file, buffer)
        contents = await file.read()
    buffer.close()
    return {"filename": file.filename,"content_size": len(contents),"content_type": file.content_type}

 效果图

三、依赖项

3.1 请求体更新数据

FastAPI 学习之路(二十一)请求体 - 更新数据_fastapi更新数据-CSDN博客

我们都知道,去创建请求体,更新数据我们用PUT请求,我们去试着更新下数据。

我们有一组数据,我们要更新描述。

# 请求体更新数据的学习
from fastapi import FastAPI
from typing import List, Optional
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from fastapi import APIRouter
router = APIRouter()
class Item(BaseModel):
    name: Optional[str] = None
    description: Optional[str] = None
    price: Optional[float] = None
    tax: float = 10.5
    tags: List[str] = []
items = {
    "one": {
        "name": "apple", "price": 50.3
    }
}
@router.put("/demo8/items", response_model=Item)
def upadte_item(name: str, item: Item):
    update_item_encoded = jsonable_encoder(item)
    items[name] = update_item_encoded
    print(items)
    return update_item_encoded
@router.get("/demo8/items/{item_id}", response_model=Item)# 返回的类型模型Item
def read_item(item_id: str):
    return items[item_id]

 

3.2 什么是依赖注入? 

FastAPI 学习之路(二十二)依赖项-CSDN博客

FastAPI 提供了简单易用,但功能强大的依赖注入系统。这个依赖系统设计的简单易用,可以让开发人员轻松地把组件集成至 FastAPI。

       声明代码(本文中为路径操作函数 )运行所需的,或要使用的「依赖」的一种方式。

然后,由系统(本文中为 FastAPI)负责执行任意需要的逻辑,为代码提供这些依赖(注入依赖项)。

依赖注入常用于以下场景:

共享业务逻辑(复用相同的代码逻辑)
共享数据库连接
实现安全、验证、角色权限
等……
上述场景均可以使用依赖注入,将代码重复最小化。

接下来,我们学习一个非常简单的例子,尽管它过于简单,不过很实用。

# 依赖项
from fastapi import APIRouter
from fastapi import FastAPI, Depends
from typing import Optional
router = APIRouter()
def common_params(q: Optional[str] = None, skip: int = 0, limit: int = 10):
    return {"q": q, "skip": skip, "limit": limit}
@router.get("/demo9/items")
def read_items(commons: dict = Depends(common_params)):
    return commons
@router.get("/demo9/users")
def read_users(commons: dict = Depends(common_params)):
    return commons

依赖项函数的形式和结构与路径操作函数一样。

因此,可以把依赖项当作没有「装饰器」(即,没有 @app.get("/path") )的路径操作函数。

依赖项可以返回各种内容。

本例中的依赖项预期接收如下参数:

类型为 str 的可选查询参数 q
类型为 int 的可选查询参数 skip,默认值是 0
类型为 int 的可选查询参数 limit,默认值是 100
然后,依赖项函数返回包含这些值的 dict。

 接收到新的请求时,FastAPI 执行如下操作:

用正确的参数调用依赖项函数(「可依赖项」)
获取函数返回的结果
把函数返回的结果赋值给路径操作函数的参数  


FastAPI 兼容性 依赖注入系统如此简洁的特性,让 FastAPI 可以与下列系统兼容:

关系型数据库
NoSQL 数据库
外部支持库
外部 API
认证和鉴权系统
API 使用监控系统
响应数据注入系统

3.3 子依赖

FastAPI 学习之路(二十四)子依赖项_from fastapi import fastapi, depends-CSDN博客

FastAPI支持创建含子依赖项的依赖项。并且,可以按需声明任意深度的子依赖项嵌套层级。

FastAPI负责处理解析不同深度的子依赖项。

我们去实现一个简单的demo

from fastapi import FastAPI, Depends
from typing import Optional
 
app = FastAPI()
 
fake_db_items = [{"city": "beijing"}, {"city": "shanghai"}, {"city": "guangzhou"}]
 
def query_extractor(desc: Optional[str] = None):
    return desc
 
 
def extend_query(desc: str = Depends(query_extractor), name: Optional[str] = ""):
    if not desc:
        return name
    return desc
 
 
@app.get("/items")
def read_items(query: str = Depends(extend_query)):
   return query
 
 
if __name__ == '__main__':
    import uvicorn
    uvicorn.run("main:app", reload=True, debug=True)

3.4 路径操作装饰器依赖项

有时,我们并不需要在路径操作函数中使用依赖项的返回值。或者说,有些依赖项不返回值。

但仍要执行或解析该依赖项。对于这种情况,不必在声明路径操作函数的参数时使用 Depends,而是可以在路径操作装饰器中添加一个由 dependencies 组成的 list。

 我们看下,如何去实现。我们去校验下请求头中的token,请求的key。

from fastapi import FastAPI, Depends, Header, HTTPException
from typing import Optional
app = FastAPI()
fake_db_items = [{"city": "beijing"}, {"city": "shanghai"}, {"city": "guangzhou"}]
def verify_token(token: str = Header(...)):
    if token != "mrli_token":
        raise HTTPException(status_code=400, detail="token is invalid")
def verify_key(key: str = Header(...)):
    if key != "mrli_key":
        raise HTTPException(status_code=400, detail="key is invalid")
    return key
@app.get("/items", dependencies=[Depends(verify_token), Depends(verify_key)])
def read_items():
    return fake_db_items
if __name__ == '__main__':
    import uvicorn
    uvicorn.run("demo10_Depends:app", reload=True)

不传入token的时候

传入参数

3.5 全局依赖项 

FastAPI 学习之路(二十六)全局依赖项_fastapi中间件验证token-CSDN博客

有时,我们要为整个应用添加依赖项。通过与定义FastAPI 学习之路(二十五)路径操作装饰器依赖项

类似的方式,可以把依赖项添加至整个 FastAPI 应用。那么我们看下,如何去实现,比如我们全局都需要校验token。我们去看下,我们应该如何实现代码。

from fastapi import FastAPI, Depends, Header, HTTPException
from typing import Optional
 
fake_db_items = [{"city": "beijing"}, {"city": "shanghai"}, {"city": "guangzhou"}]
 
def verify_token(token: str = Header(...)):
    if token != "mrli_token":
        raise HTTPException(status_code=400, detail="token is invalid")
 
app = FastAPI(dependencies=[Depends(verify_token)])
 
@app.get("/items")
def read_items():
   return fake_db_items
 
 
@app.get("/users")
def read_users():
    return "this is test return user"
 
if __name__ == '__main__':
    import uvicorn
    uvicorn.run("main:app", reload=True, debug=True)

四、安全校验

4.1 安全校验基础知识

FastAPI 学习之路(二十七)安全校验_fastapi token校验-CSDN博客

你写API接口肯定你是希望是有权限的人才能访问,没有权限的人是不能访问的,那么我们应该如何去处理呢,我们可以用的验证方式有很多,我们这次分享的是用:OAuth2来认证。那么我们看下,需要怎么才能实现呢。我们现在的接口有一部分需要用OAuth2认证后才可以访问,另一部分可以随便去访问的,那么我们看下,我们应该如何去实现

需求:1.items接口任意都可以访问

           2.item接口需要认证后才可以访问。

from fastapi import FastAPI, Depends
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
app = FastAPI()
fake_db_items = [{"city": "beijing"}, {"city": "shanghai"}, {"city": "guangzhou"}]
@app.get("/items")
def read_items():
    return fake_db_items
@app.get("/item")
def read_users(city: str, token: str = Depends(oauth2_scheme)):
    for item in fake_db_items:
        if item["city"] == city:
            return item
    return {"msg": "not exist"}
if __name__ == '__main__':
    import uvicorn
    uvicorn.run("demo11_OAuth2:app", reload=True)

其实基于OAuth2来做处理其实是很简单的,我们只是写了一个简单的demo。我们并未实现自己的token值,也没有做真正的校验,接下来我们会慢慢深入 

 4.2 使用密码和 Bearer 的简单 OAuth2

FastAPI 学习之路(二十八)使用密码和 Bearer 的简单 OAuth2_fastapi bearer-CSDN博客

OAuth2 规定在使用(我们打算用的)「password 流程」时,客户端/用户必须将 username 和 password 字段作为表单数据发送。我们看下在我们应该去如何实现呢。

from fastapi import FastAPI, Depends, status, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from typing import Optional
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
fake_db_users = {
    "mrli": {
        "username": "mrli",
        "full_name": "mrli_hanjing",
        "email": "mrli@qq.com",
        "hashed_password": "mrli",
        "disabled": False
    }
}
app = FastAPI()
def fake_hash_password(password: str):
    """模拟将密码加密"""
    return password
class User(BaseModel):
    username: str
    email: Optional[str] = None
    full_name: Optional[str] = None
    disabled: Optional[bool] = None

class UserInDB(User):
    hashed_password: str
def get_user(db_users, username: str):
    if username in db_users:
        user_dict = db_users[username]
        return UserInDB(**user_dict)
def fake_decode_token(token):
    """我们模拟返回的token值就是username,所以下面可以直接传token"""
    user = get_user(fake_db_users, token)
    return user
def get_current_user(token: str = Depends(oauth2_scheme)):
    user = fake_decode_token(token)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid authentication",
            headers={"WWW-Autehticated": "Bearer"}
        )
    return user
@app.post("/token")
def login(form_data: OAuth2PasswordRequestForm = Depends()):
    """
    校验密码
    目前我们已经从数据库中获取了用户数据,但尚未校验密码。
    让我们首先将这些数据放入 Pydantic UserInDB 模型中。
    永远不要保存明文密码,因此,我们将使用(伪)哈希密码系统。
    如果密码不匹配,我们将返回同一个错误。
    """
    user_dict = fake_db_users.get(form_data.username)
    if not user_dict:
        raise HTTPException(status_code=400, detail="Invalid username or password")
    user = UserInDB(**user_dict)
    hashed_password = fake_hash_password(form_data.password)
    if hashed_password != user.hashed_password:
        raise HTTPException(status_code=400, detail="Invalid username or password")
    return {"access_token": user.username, "token_type": "bearer"}
@app.get("/users/me")
def read_users_me(current_user: User = Depends(get_current_user)):
    return current_user
if __name__ == '__main__':
    import uvicorn
    uvicorn.run("demo12_OAuth2:app", reload=True)

4.3 展示登录案例 

代码如下所示:

# app.py
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 允许跨域请求
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 允许所有来源
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
# 假设的用户数据库
fake_users_db = {
    "testuser": {
        "username": "testuser",
        "hashed_password": "fakehashedpassword"
    }
}
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# 获取令牌
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    '''
    OAuth2PasswordRequestForm:
        作用:它是一个表单模型,用于接收用户登录时提交的用户名和密码。
        工作原理:在使用此类时,FastAPI 会自动解析请求中的表单数据,并将其转化为
        OAuth2PasswordRequestForm 实例,使你能够方便地访问 username 和 password 字段。
    用法实例:
    async def login(form_data: OAuth2PasswordRequestForm = Depends()):
        username = form_data.username
        password = form_data.password
        # 进行用户验证
    '''
    print(f"输出form_data数据信息:{form_data}")
    print(f"输出form_data.username数据信息:{form_data.username}")
    print(f"输出form_data.password数据信息:{form_data.password}")
    user = fake_users_db.get(form_data.username)# 根据用户名获取用户信息,模拟从数据库中获取用户信息
    if not user or form_data.password != user['hashed_password']:# 如果用户不存在,或者密码错误的话,就会执行这个报错
        raise HTTPException(status_code=400, detail="Incorrect username or password")
    return {"access_token": user['username'], "token_type": "bearer"}
# 受保护的路由
@app.get("/users/me")
async def read_users_me(token: str = Depends(oauth2_scheme)):
    user = fake_users_db.get(token)
    print(f"输出token数据信息:{token}")
    print(f"输出user数据信息:{user}")
    if not user:
        raise HTTPException(status_code=401, detail="Invalid credentials")
    print(f"输出user['username']数据信息:{user['username']}")
    return {"username": user['username']}

if __name__ == '__main__':
    import uvicorn
    uvicorn.run("demo13_OAuth:app", reload=True)

前端代码如下所示:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Login Example</title>
</head>
<body>
    <h1>Login</h1>
    <form id="loginForm">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" required>
        <br>
        <label for="password">Password:</label>
        <input type="password" id="password" name="password" required>
        <br>
        <button type="submit">Login</button>
    </form>
    <pre id="response"></pre>

    <script>
        document.getElementById('loginForm').addEventListener('submit', async (event) => {
            event.preventDefault();
            const username = document.getElementById('username').value;
            const password = document.getElementById('password').value;

            const response = await fetch('http://127.0.0.1:8000/token', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded'
                },
                body: new URLSearchParams({
                    username: username,
                    password: password
                })
            });
            const data = await response.json();
            document.getElementById('response').textContent = JSON.stringify(data, null, 2);
        });
    </script>
</body>
</html>

 postman接口访问

OAuth2PasswordBearer

  • 作用:它是一个依赖项,帮助你从请求中提取 Bearer 令牌。

  • 工作原理:当用户访问需要认证的路由时,它会从请求的 Authorization 头中提取 Bearer 令牌。这个令牌通常是在用户登录后生成的,并在后续请求中用于身份验证。

OAuth2PasswordRequestForm

  • 作用:它是一个表单模型,用于接收用户登录时提交的用户名和密码。

  • 工作原理:在使用此类时,FastAPI 会自动解析请求中的表单数据,并将其转化为 OAuth2PasswordRequestForm 实例,使你能够方便地访问 usernamepassword 字段。

async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    username = form_data.username
    password = form_data.password
    # 进行用户验证

参考文献

FastAPI 学习之路(十六)Form表单_fastapi formdata-CSDN博客

FastAPI 学习之路(十七)上传文件_fastapi 上传文件-CSDN博客

FastAPI 学习之路(十八)表单与文件_fastapi formdata-CSDN博客

FastAPI 学习之路(十九)处理错误/异常_fastapi 异常-CSDN博客

FastAPI 学习之路(二十一)请求体 - 更新数据_fastapi更新数据-CSDN博客

FastAPI 学习之路(二十二)依赖项-CSDN博客

FastAPI 学习之路(二十三)用类作为依赖的注入_fastapi 依赖注入-CSDN博客

FastAPI 学习之路(二十四)子依赖项_from fastapi import fastapi, depends-CSDN博客

FastAPI 学习之路(二十五)路径操作装饰器依赖项_fastapi 路径操作装饰器-CSDN博客

FastAPI 学习之路(二十六)全局依赖项_fastapi中间件验证token-CSDN博客

FastAPI 学习之路(二十七)安全校验_fastapi token校验-CSDN博客

FastAPI 学习之路(二十八)使用密码和 Bearer 的简单 OAuth2_fastapi bearer-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欲游山河十万里

你的鼓励是我们创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值