许多开发者都是从开发简单的项目开始学习编程的,比如待办事项应用、计算器以及基本的CRUD应用程序。这些项目在初期确实很有帮助,因为它们能帮助你理解编程语言的工作原理,也能让你有信心去尝试构建更复杂的东西。但对很多开发者来说,他们的进步就到此为止了。
现实世界中的应用程序并不仅仅是用来在屏幕上展示数据而已。它们要解决实际问题,与真实用户打交道,还要处理那些并不总按计划发展的情况。而这正是许多开发者遇到困难的地方。
当我查看初级开发者的简历时,我会发现一个共同的现象:他们的项目列表里往往都是些看起来非常相似的初学者级应用。在如今的就业市场上,这样的简历通常是不够的。雇主希望看到你能开发出真正有用的东西,那些人们会实际使用的产品。
本文的目的就是帮助你摆脱那些像待办事项应用、计算器以及基本的CRUD应用程序这类简单的“Hello World”级别的项目。读完这篇文章后,你会明白如何去构建那些能够解决实际问题的真实应用程序,并且会感受到自己离现实世界中的软件开发工作更近了一步。
你可能会想知道,为什么应该听我的建议。
在过去的10年里,我花了大量时间来开发、修改和重新开发各种软件。我进行了无数次尝试,也遭遇过很多失败,但最终还是开发出了那些每天都有成千上万的人在使用的应用程序。几周前,我自己发布了一款SaaS产品,亲眼见证了它在实际使用中的表现:在高峰时段,有超过一千名用户在使用它,而且系统从未出现过崩溃的情况。
我之所以分享这些经历,是因为我也曾经和你现在处于同样的境地。
如果你继续阅读下去,你将会:
-
从那些真正被用户使用的应用程序的开发经验中学习
-
通过多年来的错误与教训,了解哪些事情是绝对不能做的
-
掌握哪些方法才是真正有效的,以及如何让自己作为初级开发者脱颖而出
-
消除那些会阻碍你进步的常见误解
这篇文章并不是在讲理论,而是要教你如何去开发实际的产品。
为了更好地说明这一点,我们将一起构建一个能够解决现实问题、并被真实用户使用的应用程序。在这个过程中,你会学会如何产生有价值的应用想法,如何使用简单的工具来实现它们,以及如何让这些应用服务于大量的用户。
如果这听起来是你感兴趣的内容,那么我们就开始吧。
目录
你可能认为自己懂得还不够多
在开始之前,我想先澄清初学者们最常犯的误解。
许多开发者认为,自己目前掌握的知识还不足以开始开发实际应用。他们觉得需要学习更多的JavaScript、Python或其他编程框架才能做好准备。甚至有些人认为,只有学习了React,一切才会变得顺理成章。
这种想法非常普遍,但实际上并不正确。
如果你会编写HTML、CSS,并且能用任何编程语言编写简单的循环或函数,那么你已经具备了开发实际应用所需的大部分基础知识。这个教程就是证明这一点。
我写这篇文章的目的,不是为了劝你先去学习更多知识,而是想向你展示:仅凭你现在掌握的知识,你已经能够做出很多事情来了。
如何寻找实际应用的开发思路
初学者们经常会问这样一个问题:“我应该开发什么样的应用程序呢?”
虽然这个问题很少被人们讨论,但它的重要性远超大多数人的想象。
有一个简单的原则:去开发那些已经存在的东西。
看看你每天使用的那些应用程序,尤其是那些功能简单的工具——那些能够很好地完成某一项具体任务的软件。如果你发现自己经常使用某个应用,那么这个应用肯定是在解决某种实际问题。
例如,人们会使用背景去除工具来清理图片;他们会使用网址缩短服务来分享链接;他们还会用笔记应用来记录一些灵光一现的想法。这些想法虽然并不新颖,但它们确实具有实用性。
你不需要去发明什么全新的东西,而是需要理解某个问题,并为此找到可行的解决方案。
当你复制现有的工具时,你就会自然而然地了解实际应用程序的架构结构。同时,你开发的这些项目也会在简历上显得很有价值,因为它们解决的是人们普遍面临的问题。
而这正是我们在这个教程中要做的。
我们要开发什么?
我现在就可以告诉你,我们不会再开发另一个“Hello World”示例程序了。我们将开发一个用于去除图片背景的工具。
这种工具是人们真正会使用的。设计师用它来处理图片,内容创作者用它来制作缩略图,而开发者也会将类似的功能集成到他们的产品中。这个工具可以处理真实的文件、真实的数据,并产生实际的结果。
如果你想在开始开发之前就先看看最终效果,你可以在这里尝试这个已经可运行的应用程序:https://iamspruce.github.io/background-remover/

我们会故意使用一些简单的工具来开发这个应用程序。前端使用纯HTML、CSS和JavaScript,后端则使用Python。不需要任何复杂的框架或设置流程。
在继续之前,让我先澄清一个常见的误解。
许多开发人员认为,如果一款应用程序想要得到重视,就必须使用复杂的框架来构建。但在过去10年里,我为世界各地的客户开发应用程序时,从来没有一位客户问过我使用了什么框架。
他们只关心一件事:这个应用程序能否正常运行,能否解决他们的需求?
用户并不关心你的应用程序是用什么技术构建的,他们只关心它是否能发挥作用。如果使用HTML、CSS和JavaScript就能解决问题,那就没有必要花费数月时间去学习新的框架。
现在我们已经明确了开发这款应用程序的目的以及它所要解决的问题,是时候开始编写代码了。
先决条件
在开始编写代码之前,我们先简单了解一下你需要准备什么。
本教程并不适合完全初学者,但也不是非常高级的内容。如果你之前曾经开发过一些小项目,并且想尝试制作真正功能完备的应用程序,那么你来对地方了。
你应该已经掌握的知识
你需要具备以下基础能力:
-
基础的HTML知识:了解输入框、按钮、图片和div元素的作用。
-
基础的CSS知识:能够对页面进行样式设置,使其看起来美观。
-
基础的JavaScript知识:知道如何监听按钮点击事件,并使用
fetch发送请求。
这些基础知识就足以让你跟随本教程进行学习了。
你不需要使用React或其他前端框架。
后端开发知识
对于后端的开发,你并不需要成为Python专家。
你只需要了解以下几点:
-
Python可以用来运行服务器。
-
服务器能够接收用户的请求。
-
服务器能够向用户返回响应结果。
其他相关内容会在后续讲解中详细说明。
所需工具
请确保你已经安装了以下工具:
-
Python 3.9或更高版本。
-
Git。
- 像VS Code这样的代码编辑器。
- 一款浏览器。
你不需要具备Docker相关知识或云开发经验。
GitHub账户(非常重要)
我们将直接通过GitHub来部署后端服务,因此你需要拥有一个GitHub账户。
如果你之前从未将项目上传到GitHub或托管过项目,我已经写了一篇适合初学者的指南,你可以先阅读那篇文章再回来继续学习。
快速检查你的准备情况
本教程并不是那种可以照搬就能完成的教程。在实际开发过程中,你肯定会遇到各种错误,程序也可能会出故障——这些都是正常现象,因为真正的应用程序就是这样开发和测试的。
现在我们已经明确了你需要准备什么,是时候开始编写代码了。<我们先从后端开始讨论。这是一个非常重要的决定,因此我们需要仔细地对其进行说明。>
步骤1:搭建后端
什么是后端?为什么我们需要它?
后端是一种在服务器上运行的程序,它负责应用程序中的各项繁重任务。
在我们的项目中,这些繁重任务就是图像处理。从图片中去除背景需要使用一些浏览器无法运行的库。浏览器的设计目的是为了保障安全性和方便用户操作,而不是用于这类处理工作。
因此,我们才需要后端。
后端的功能包括:
-
接收用户上传的图片
-
去除图片中的背景
-
将处理后的图片传回给用户
之后,前端程序会与这个后端进行交互。
有意保持简单性
因为这可能是你的第一个真正的项目,所以我们会尽量让后端的实现变得简单。
-
只使用一种编程语言(Python)
-
仅使用一个后端文件
-
不采用复杂的文件夹结构
-
不涉及任何高级概念
这样做是有意为之的。现实世界中的应用程序并不一定一开始就要非常复杂,它们通常都是从小规模开始逐步发展的。
项目结构
创建一个名为“background-remover”的新文件夹:
background-remover
在这个文件夹内,再创建一个用于存放后端代码的文件夹:
background-remover/
backend/
进入backend文件夹:
cd background-remover/backend
现在创建一个虚拟环境:
python -m venv env
虚拟环境的存在可以将这个项目所依赖的库与计算机上的其他程序分开来。这是一种标准做法,在实际项目中也很常见。
接下来激活这个虚拟环境:
对于macOS或Linux系统:
source env/bin/activate
对于Windows系统:
env\Scripts\activate
现在创建一个用于存放应用程序代码的文件夹:
mkdir api
目前,你的项目结构应该如下所示:
background-remover/
backend/
env/
api/
在这个阶段,项目的结构看起来可能还比较简单,这是正常的。
安装FastAPI
我们将使用FastAPI来构建后端API。
请同时安装FastAPI和Uvicorn——后者是用于运行我们应用程序的服务器:
pip install fastapi uvicorn
FastAPI允许我们用很少的代码清晰地定义各种接口,这正符合我们的需求。
创建第一个后端文件
在“api”文件夹内,创建一个名为“main.py”的文件。
添加以下代码:
from fastapi import FastAPI
app = FastAPI()
@app.get("/health")
def health():
return {"status": "ok"}
让我们暂停一下,了解一下这段代码的作用。
-
我们创建了一个 FastAPI 应用程序。
-
我们添加了一个名为
/health的端点。 -
这个端点只是返回一条信息而已。
在开发实际功能之前,开发者总是会先确认他们的服务器是否能够正常运行。而这个端点正是用于实现这一目的的。
运行服务器
在 backend 文件夹中,启动服务器:
uvicorn api.main:app --reload
现在打开一个新的终端窗口,然后运行以下命令:
curl http://localhost:8000/health
你应该会看到如下结果:
"status":"ok"}
这一刻非常重要。
现在你已经完成了以下步骤:
-
运行中的后端服务器
-
一个真实的 HTTP 端点
-
由你自己的代码生成的响应结果
实际上,真正的后端服务就是从这样的步骤开始的。
为什么我们先从这里开始
此时你可能会疑惑:为什么我们不直接开始处理背景去除功能呢?
原因很简单:如果服务器都无法正常运行,那么其他任何事情都毫无意义。
通过首先创建这个健康检查端点,我们消除了不确定性——我们知道服务器是能够正常运行的。接下来我们要添加的任何功能,都是建立在这个已经确认可以正常工作的基础上进行的。
现在基础已经搭建完成,我们可以开始开发实际的功能了。
步骤 2:将背景去除功能添加到后端
在开始编写代码之前,我们需要先澄清一个常见的误解。
关于机器学习的一个常见误解
当人们听到“背景去除”这个概念时,往往会认为机器学习技术对他们来说过于复杂、难以掌握。
但在实际的开发过程中,情况几乎从来都不是这样的。
你并不需要自己构建机器学习模型,而是可以使用其他人开发的库,并专注于如何正确地使用这些库。
我们在这里所做的正是这样的事情。
安装背景去除相关库
请安装以下所需的软件包:
pip install rembg pillow onnxruntime
-
rembg用于处理背景去除功能。 -
pillow可帮助我们处理图像文件。
对于我们当前的目的来说,你并不需要了解这些库内部的运作机制,只需要知道如何使用它们即可。
添加背景去除功能端点
现在请更新main.py文件,使其内容如下:
from fastapi import FastAPI, UploadFile, File
from rembg import remove
from PIL import Image
import io
app = FastAPI()
@app.get("/health")
def health():
return {"status": "ok"}
@app.post("/remove_bg")
async def remove_bg(file: UploadFile = File(...)):
image_bytes = await file.read()
image = Image.open(io.BytesIO(image_bytes))
output = remove(image)
buffer = io BytesIO()
output.save(buffer, format="PNG")
buffer.seek(0)
return buffer.getvalue()
让我们仔细解释一下这个流程。
-
该端点接收用户上传的图片文件。
-
系统会将图片读入内存进行处理。
-
随后会去除图片中的背景部分。
-
处理后的结果会被保存为带有透明效果的PNG格式文件。
-
最后,处理完成的图片会被返回给客户端。
这个端点是我们整个应用程序的核心部分。
使用curl测试该端点
在开发前端之前,我们先直接测试后端功能。
请运行以下命令:
curl -X POST \
-F "file=@person.jpg" \
http://localhost:8000/remove-bg \
--output result.png
打开result.png文件,如果发现图片中的背景已经被去除,那就说明后端功能已经开发完成。
至此,你已经构建了一个能够:
-
接收用户输入的文件
-
处理这些文件数据
-
返回有意义的结果
这才是真正意义上的后端服务。
我们目前的进展
让我们暂停一下,总结一下迄今为止所完成的工作:
-
我们搭建了一个后端服务器。
-
确认该服务器能够正常运行。
-
为系统添加了实际可用的功能。
-
在没有前端界面的情况下对后端功能进行了测试。
这正是真正的开发人员的工作方式。
在下一节中,我们将构建一个简单的前端界面,让它与这个后端进行交互,从而让用户能够使用它。
步骤3:构建前端界面(用户实际看到的内容)
目前,我们的后端已经可以正常运行了。
它能够接收图片、去除背景图像,然后将处理后的结果发送回来。但现阶段,只有开发人员才能使用它,因为使用它需要输入终端命令。
为了让非技术领域的用户也能使用这个系统,我们就需要一个前端界面。
前端界面其实就是用户在浏览器中看到并与之交互的部分。
澄清关于前端界面的一个常见误解
很多初学者认为,构建前端界面首先就需要学习某种框架。
这种想法是错误的。
框架在后期的开发中确实会起到帮助作用,但它们并不是构建实际应用程序的必备工具。从根本上来说,每一个前端界面都是由HTML、CSS和JavaScript构成的。
因此,在这里我们使用的就是纯粹的HTML、CSS和JavaScript。不使用React,也不需要任何构建工具或复杂的配置流程,只使用最基础的技术即可。
我们的前端界面会实现哪些功能
我们的前端界面只有一个任务:它会:
-
允许用户选择一张图片
-
将选中的图片发送到后端
-
接收处理后的图片结果
-
在屏幕上显示处理后的图片
-
让用户能够下载处理后的图片
就是这些功能。
如果它能正确地完成这些任务,那么它就是一个真正的前端界面。
构建前端界面
回到你的项目根目录,创建三个文件:
index.html
styles.css
app.js
这种简单的结构非常常见。每个文件都有明确的职责,这样代码就更容易理解了。
编写HTML页面
打开index.html文件,然后添加以下内容:
<!DOCTYPE html>
<html>
<head>
<title>背景去除器</title>
<link" rel="stylesheet" href="styles.css" />
</head>
<body>
<h1>背景去除器</h1>
<input" type="file" id="imageInput" />
<button" id="removeBtn">去除背景</button>
<div" class="result">
<img" id="resultImage" />
div>
<a" id="downloadLink" download>下载图片</a>
<script" src="app.js"></script>
</body>
</html>
目前,这个页面还不会有什么特别的功能,这也是意料之中的事。
HTML只是用来描述页面上应该显示哪些内容,而页面的实际行为是由JavaScript实现的。等我们为页面添加一些样式之后,才会加入JavaScript代码。
添加一些基本样式
打开styles.css文件,然后添加以下内容:
body {
font-family: sans-serif;
max-width: 600px;
margin: 40px auto;
}
button {
margin-top: 10px;
}
.result {
margin-top: 20px;
}
img {
max-width: 100%;
}
#downloadLink {
display: none;
margin-top: 10px;
}
我们这样做并不是为了让页面看起来很花哨,而是为了让页面更易于阅读、更便于使用。很多内部使用的工具,其界面效果其实也不比这个好多少。(当然,如果你愿意的话,以后随时都可以改进这些样式。)
将前端与后端连接起来
现在我们要编写JavaScript代码,让所有功能都能正常运行。
打开app.js文件,然后添加以下代码:
const imageInput = document.getElementById("imageInput";
const removeBtn = document.getElementById("removeBtn";
const resultImage = document.getElementById("resultImage";
const downloadLink = document.getElementById("downloadLink");
removeBtn.addEventListener("click", async () => {
const file = imageInput.files[0];
if (!file) {
return;
}
const formData = new FormData();
formData.append("file", file);
const response = await fetch("http://localhost:8000/remove-bg", {
method: "POST",
body: formData,
});
const blob = await response.blob();
const imageUrl = URL.createObjectURL(blob);
resultImage.src = imageUrl;
downloadLink.href = imageUrl;
downloadLink.style.display = "inline";
});
让我们放慢节奏,仔细解释一下这里发生了什么:
-
我们读取用户选择的图片。
-
然后将其封装在
FormData对象中,以便发送到后端。 -
接着我们将这个数据发送到我们的
/remove-bg接口。 -
之后我们会收到处理后的图片结果。
-
最后我们将处理后的图片显示出来,并提供下载链接。
这就是前端与后端真正开始交互的时刻。
使用实时服务器运行前端应用
在测试之前,请确保你的后端服务仍在运行中。
现在,不要直接打开index.html文件,而是使用VS Code中的实时服务器扩展功能。
如果还没有安装这个扩展,只需进入VS Code的扩展管理界面,搜索“Live Server”并进行安装。之后右键点击index.html文件,选择“用实时服务器打开”即可。
这样就会为前端应用启动一个小型本地服务器。
为什么这样做很重要呢?
许多浏览器的功能在通过服务器提供文件时表现得更佳,而不是直接从文件系统读取文件。使用实时服务器还能让前端应用的运行方式更接近真实环境中的情况。
在本地测试完整的应用程序
现在请选择一张图片并点击按钮进行操作。
如果一切正常,那么将会发生以下步骤:
-
图片会被发送到后端。
-
图片中的背景元素会被去除。
-
处理后的结果会显示在页面上。
-
随后会出现下载链接。
请稍作思考——你刚刚完成了以下事情:
-
开发了一个能够处理实际数据的后端服务。
-
设计了一个可以与后端交互的前端界面。
-
构建了一个能够在本地运行的完整应用程序。
这已经远远超出了一个简单的“Hello World”项目的范围。

消除另一个误解
有些初学者看到这样的代码会想:“这么简单的程序,怎么可能是真正的应用程序呢?”
这其实又是一个常见的误解。真正意义上的应用程序并不是由复杂程度来定义的,而是由它们的实用性来决定的。如果你的应用程序能够解决实际问题,并且人们能够使用它,那么它就是一款真正的应用程序。
在下一节中,我们将把这个应用程序放到互联网上,让任何人都可以使用它。
步骤4:将后端服务部署到互联网上
目前,你的后端服务只是运行在你的电脑上。
这意味着:
-
它只能为你个人使用。
-
如果你关闭笔记本电脑,后端服务也会停止运行。
-
如果其他人打开了你的前端应用,它们也无法访问后端服务。
为了解决这个问题,我们需要在一台始终处于在线状态的电脑上运行后端程序。
这个过程被称为部署。
理解部署的简单方法
部署并不意味着需要编写新的代码。
它的含义很简单:不要在笔记本电脑上运行后端程序,而是将其放在一台从不关闭的电脑上运行。
我们接下来所做的所有事情,都是为了实现这一目标。
为什么不使用Netlify或Vercel
你可能会想知道,为什么我们不使用Netlify或Vercel。这些平台确实很棒,但它们主要适用于前端开发。
当你的应用程序符合以下条件时,这些平台的效果最佳:
-
使用静态的HTML、CSS和JavaScript文件
-
使用React或Vue等前端框架
-
仅包含小型无服务器功能
但我们的后端程序不同。它是一个Python服务器,它的功能包括:
-
持续运行
-
接收图片上传
-
处理图片文件
-
使用复杂的原生库来去除图片背景
这种类型的后端程序需要一台真正的服务器,而不是轻量级的无服务器功能。
因此,Netlify和Vercel并不适合我们的需求。
为什么选择使用Cloud Run
所以我们选择了Cloud Run。
Cloud Run允许我们在谷歌的基础设施上运行真正的后端服务器,而无需我们自己管理服务器。
我们选择它的原因包括:
-
它支持完整的Python后端程序
-
可以直接从GitHub上进行部署
-
它会自动处理扩展和服务器配置问题
-
能够很好地应对高负载场景
-
对初学者也非常友好
最重要的是,使用Cloud Run,你完全可以无需学习任何云相关命令或CI/CD流程,就能完成后端程序的部署。
为Cloud Run准备后端环境
在开始部署之前,我们必须确保后端程序已经准备好。
创建requirements.txt文件
在backend文件夹中,创建一个名为requirements.txt的文件。
在其中添加以下内容:
fastapi
uvicorn
rembg
pillow
onnxruntime
这个文件会告诉Cloud Run需要安装哪些Python库。
创建GitHub仓库
由于Cloud Run是直接从GitHub进行部署的,因此我们的代码必须存储在GitHub上。
在项目根目录下,执行以下命令:
git init
git add .
git commit -m "初始背景去除项目"
git branch -M main
git remote add origin YOUR_REPO_URL
git push -u origin main
这个唯一的仓库将同时用于后端和前端的代码存储。
在Cloud Run上部署后端服务
现在打开您的浏览器,访问Google Cloud Console。
1. 创建新项目
打开Google Cloud Console,然后点击“新建项目”。为该项目起一个名称(例如:background-remover),接着点击“创建”。

2. 打开Cloud Run
使用顶部的搜索栏,搜索Cloud Run,然后打开该服务。

Cloud Run会自动启用所需的API。
系统会要求您设置计费信息。Google会为您提供300美元的免费额度,这对于完成本教程来说已经足够了。
3. 开始创建服务
点击“创建服务”,然后选择“从仓库持续部署代码”。

4. 连接您的GitHub账户
选择GitHub作为仓库提供者,并登录您的GitHub账户。随后在您的GitHub账户上安装Google Cloud Build。请仅选择您想要部署的仓库。

这样,Google Cloud就可以自动构建并部署您的代码了。
5. 选择仓库
接下来,请选择您刚刚安装了Google Cloud Build的仓库,并选择main分支。

6. 配置构建过程
现在来到关键步骤:构建代码环境。
将此值设置为:
backend
这样Cloud Run就会知道:
“我的后端代码存储在
backend文件夹中。”
对于入口命令,请输入以下内容:
uvicorn api.main:app --host 0.0.0.0 --port 8080
这就是Cloud Run启动FastAPI服务器的方式。

7. 配置容器资源
我们的后端运行的是一个需要大量计算资源的模型,因此必须增加资源配置。
具体操作如下:
-
将内存从512 MB调整到2 GB
-
将CPU核心数设置为4

这样就能确保模型能够正常运行了。
8. 部署
现在点击创建按钮。
Cloud Run将会执行以下操作:
-
构建你的应用程序
-
安装所需依赖项
-
创建一个容器
-
将其部署到互联网上
你会看到显示构建和部署过程的日志信息。

这个过程可能需要几分钟时间,这是正常的。
检查后端是否已成功部署
部署完成后,Cloud Run会提供一个公共URL供你使用。
测试一下:
curl https://YOUR_CLOUD_RUN_URL/health
如果看到如下结果:
"status":"ok"}
那么说明你的后端已经成功部署到互联网上了。
稍等片刻……
你刚刚成功部署了一个真正的后端服务,恭喜!
更新前端以使用已部署的后端
打开app.js文件。
将以下内容:
http://localhost:8000/remove-bg
替换为:
https://YOUR_CLOUD_RUN_URL/remove-bg
保存文件后,重新加载前端页面。
步骤5:让后端与前端协同工作
目前我们已经有两部分代码了:
-
一个部署在互联网上的后端服务
-
一个在浏览器中运行的前端应用
现在我们需要让这两部分能够相互配合、协同工作。
打开前端页面,选择一张图片,然后点击按钮——你会发现它仍然无法正常工作。这是预期中的现象。
这里发生了什么?
你的前端程序运行在某个地址上,而后端程序则运行在另一个地址上。
浏览器对这一点有非常严格的规定。默认情况下,浏览器会阻止一个网站向另一个网站发送请求,除非后端明确允许这样做。这是一种安全机制。
这个规则被称为CORS。
澄清关于CORS的一个常见误解
当初学者遇到CORS错误时,他们往往会认为系统出了问题。
其实并没有什么问题。CORS只是在告诉浏览器:“我需要后端来确认我的前端程序是否被允许与之进行通信。”
因此,我们只需要让后端知道:“允许我的前端程序发送请求是可以的。”
只允许我们的前端程序发送请求(而不是所有请求)
我们不应该允许来自任何地方的请求,而应该只允许我们的前端程序发送请求。这是一个值得尽早掌握的好习惯。
打开backend/api/main.py文件。
在文件开头添加以下代码:
from fastapi.middleware.cors import CORSMiddleware
在创建FastAPI应用之后,再添加以下代码:
app.add_middleware(
CORSMiddleware,
allow_origins=[
"http://127.0.0.1:5500",
"http://localhost:5500"
],
allow_methods=["POST"],
allow_headers=["*"],
)
为什么选择这些URL地址呢?
如果你使用了Live Server扩展,那么你的前端程序通常会运行在5500端口上。这些就是浏览器在本地使用的地址。
我们是在告诉后端:“只接受来自这个前端程序的请求。”
这正是我们想要做的。
重新部署后端程序
每当你修改后端代码时,都需要重新部署它。
由于我们的后端程序是部署在Cloud Run上,并且设置了自动部署功能,因此重新部署非常简单,你只需要将修改后的代码推送上去即可。
在项目根目录下运行以下命令:
git add .
git commit -m "添加CORS配置"
git push
Cloud Run会自动检测到这些变化,并重新部署后端程序。
等待部署完成。一旦部署完成,你的后端程序就会允许来自本地前端程序的请求了。
再次进行测试
在浏览器中重新加载你的前端页面。
选择一张图片并点击按钮,这次应该能够正常工作了。
你刚刚处理了一个所有生产环境中的应用程序都会遇到的真实浏览器安全规则。这一点本身就已经是一次非常重要的学习经历。
步骤6:将前端程序发布到互联网上(使用GitHub Pages)
目前,你的前端应用仅能在你的电脑上运行。
就像之前的后端一样,这意味着其他人无法使用它。
让我们来解决这个问题吧。
为什么选择GitHub Pages?
我们的前端是由以下内容构成的:
-
仅包含HTML、CSS和JavaScript代码
-
不包含任何后端代码
-
不需要进行任何构建步骤
这样的架构非常适合使用GitHub Pages。GitHub Pages可以免费托管静态网站,而且对初学者来说也非常友好。
准备前端代码以供部署
请确保所有前端文件都位于`frontend`文件夹中:
frontend/
index.html
styles.css
app.js
打开`app.js`文件,确认其中使用的后端URL是Cloud Run的URL,而不是`localhost`。
fetch("https://YOUR_CLOUD_RUN_URL/remove-bg", {
"method": "POST",
"body": formData,
});
保存文件后,就可以进行下一步操作了。
将前端代码推送到GitHub
我们之前已经创建了一个GitHub仓库,现在可以直接使用它。
在项目根目录下执行以下命令:
git add .
git commit -m "添加前端代码并准备部署到GitHub Pages"
git push
启用GitHub Pages功能
-
登录你的GitHub仓库。
-
进入“设置”页面。
-
选择“Pages”选项。
在“Source”设置中,选择以下选项:
-
分支:main
-
文件夹路径:/(root)
保存所有设置。几秒钟后,GitHub会为你提供一个URL地址,这个地址就是你在互联网上可访问的前端应用。
更新实时前端应用的CORS配置
现在前端应用已经上线运行了,回到`backend/api/main.py`文件中,将其中的本地域名替换为你的GitHub Pages URL:
allow_origins=[
"https://YOUR_GITHUB_USERNAME.github.io"
]
提交并推送这些更改:
git add .
git commit -m "更新CORS配置以适应GitHub Pages"
git push
Cloud Run会自动重新部署后端服务。
最终测试
打开你的GitHub Pages页面,尝试上传图片、去除背景图像,然后下载处理后的结果文件。
现在所有功能都已经正常运行了。
你目前已经完成了什么?
让我们明确一下你刚刚完成的所有步骤:
你:
-
构建了一个真正的后端服务
-
将其部署到了互联网上
-
构建了前端应用
-
也将它部署到了互联网上
-
解决了实际生产环境中的问题
-
正确地连接了所有组件
这并不是一个演示项目,而是一个真正的应用程序。
最后的思考:你刚刚构建的东西非常重要
此刻,值得停下来回顾一下你自己实际完成了什么。
你并不只是按照步骤操作,也没有机械地复制代码——你真正打造了一个完整的应用程序。
让我们明确一下你所取得的成就
你最初只拥有一些基础的工具和想法而已。
完成这个教程后,你将会:
-
构建了一个能够处理真实数据的后端系统
-
毫无畏惧、也不需要过度思考地使用了机器学习工具
-
将后端服务部署到了互联网上
-
使用纯HTML、CSS和JavaScript构建了前端界面
-
正确地将前端与后端连接起来
-
将这两部分都部署完成,使真实用户能够使用它们
这就是真正应用程序的构建方式,只不过规模较小、更易于管理而已。
为什么这不再是一个“初学者项目”
很多项目都被称作初学者项目,但它们的目的仅仅是在屏幕上展示一些内容而已。
但这个项目不同。
你的应用程序:
-
能够接收真实的用户输入
-
能够执行实际的运算或功能
-
运行在真正的服务器上
-
遵循真实的浏览器规则进行运行
-
可以与其他人共享
这就是学习语法与实际开发软件之间的区别。
这个教程中最重要的一点
从这个项目中你真正应该收获的,并不是你构建的这个项目本身。
而是这样的认识:在开始之前,你并不需要“掌握更多的知识”。
你是通过实际开发来学习的,在遇到问题时才去解决问题;在理解某些概念时也是通过实践来加深理解的。
经验就是这样积累起来的。在构建真正的应用程序之前,没有人拥有相关经验;而一旦完成了多个这样的项目,也就自然积累了足够的经验。
接下来你可以做什么
这个项目并不是终点,而只是一个起点。
利用你目前已经掌握的知识,你可以尝试以下这些方向:
-
改进用户界面
-
添加加载提示和更完善的反馈机制
-
限制图片的大小或文件类型
-
实现简单的速率限制功能
-
再构建一个能够解决实际问题的小工具
你现在还不必急于使用框架。
如果你能再完成几个这样的项目,那么当你真正接触到框架时,它们就会变得容易理解多了。
最后的思考
如果这是你第一个真正的开发项目,你应该为自己感到骄傲。
你跳过了教程阶段,亲手开发出了实用的工具,并将其发布到了互联网上。许多开发者永远都迈不出这一步。
既然你已经做到了这一点,接下来的步骤就会变得更加容易了。
所以,请继续努力吧!
源代码与实时演示
如果你想深入了解整个项目,或在此基础上进行进一步开发,这里可以找到所有你需要的资源。
如果有任何疑问,你可以通过X平台联系我,我的账号是@sprucekhalifa。我经常撰写这类实用的技术文章。