后端框架一览
现在,我们对 Spring 与 Java 技术栈的学习正式结束。下一部分是基于 k8s 的微服务。但为了避免大量的 boilerplate,我们不再使用 Spring 开发。之后,我们会使用 Javascript 的 hono 框架来进行 k8s 微服务的实现。
在这一章中,我们会对后端框架进行一个简单的概览。这些框架都是后端开发中常用的框架,但是我们不会深入讲解。
现代的后端框架基本都是基于函数式编程的,而非像 Spring 一样面向对象并基于注解。诚然,Spring 现在也提供了函数式路由,但用的人很少。
Spring
我们前面已经学习过了 Java 和其技术栈上的 Spring 框架。
Express
Express 是一个简洁而灵活的 Node.js Web 应用程序框架。
首先创建一个 Node.js 项目,并安装 Express。这里我们一并带上 Typescript。
npm init -y
npm install express
npm install typescript
然后,创建一个app.ts
文件,
import express from 'express';
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
可以看到,函数式的后端开发是基于回调的。在express
中,使用app.get
来定义路由,然后传入一个回调函数。
express 的回调函数的参数是req
和res
,分别是请求和响应。在这里,我们返回了一个字符串。
req
对象表示 HTTP 请求,包含了请求的信息,例如 URL、HTTP 头和请求体。res
对象表示 HTTP 响应,用于发送响应数据。
如果要获取参数,
app.get('/hello/:name', (req, res) => {
res.send(`Hello, ${req.params.name}`);
});
app.get('/hello', (req, res) => {
const name = req.query.name;
res.send(`Hello, ${name}`);
});
app.post('/hello', (req, res) => {
const name = req.body.name;
res.send(`Hello, ${name}`);
});
如果要发送 JSON 或者其他数据,
app.get('/json', (req, res) => {
res.json({ message: 'Hello World!' });
});
app.get('/file', (req, res) => {
res.sendFile('path/to/file');
});
对于数据库 client,这里使用 postgres,只要,
import { Client } from 'pg';
const client = new Client();
client.connect();
app.get('/db', async (req, res) => {
const result = await client.query('SELECT * FROM table');
res.json(result.rows);
});
没有复杂的依赖注入,闭包的捕获代替了依赖注入。
Hono
Express 是 Node.js 的框架,而 Hono 可以说是 Bun 的代表框架。
Bun 是一个新兴的 JavaScript 运行时,比起 Node.js 强大很多。一方面,它速度快;另一方面,它还有许多内置特性,例如原生支持 Typescript。此外,Bun 目前与 Node.js 在除了 v8 引擎的所有方面都基本是完全兼容的。此外,Bun 的命令也和 Node.js 或 npm 基本一致,完全没有学习成本。
Hono 是一个新的 Web 后端框架,也是函数式的。当然,它也支持基于 Node.js。
const app = new Hono()
app.get('/', (c) => c.text('Hello Bun!'))
export default app
最后,使用bun run dev
启动服务即可。
这里的c
是一个上下文对象,它包含了请求和响应的所有信息。c.text
是一个快捷方法,用于返回文本。
如果要获取参数,
import { Hono } from 'hono'
const app = new Hono()
app.get('/hello/:name', async (c) => c.text(`Hello, ${c.req.param('name')}`))
app.get('/hello', async (c) => {
const name = c.req.query('name')
return c.text(`Hello, ${name}`)
})
app.post('/hello', async (c) => {
const body = await c.req.parseBody()
const name = body.name
return c.text(`Hello, ${name}`)
})
export default app
之后我们会使用 Hono 来开发 k8s 微服务。
Fiber
Fiber 是 Go 语言的 Web 框架之一,它是 Go 语言后端框架中并发最高的。
package main
import "github.com/gofiber/fiber/v2"
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Listen(":3000")
}
fiber 本质是对 Go 语言的net/http
包的封装。
如果要获取参数,
app.Get("/hello/:name", func(c *fiber.Ctx) error {
return c.SendString("Hello, " + c.Params("name"))
})
app.Get("/hello", func(c *fiber.Ctx) error {
return c.SendString("Hello, " + c.Query("name"))
})
app.Post("/hello", func(c *fiber.Ctx) error {
return c.SendString("Hello, " + c.Body("name"))
})
如果要发送 JSON 或者其他数据,
app.Get("/json", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "Hello, World!"})
})
app.Get("/file", func(c *fiber.Ctx) error {
return c.SendFile("path/to/file")
})
可以看出来,fiber 和 hono 非常相似。
如果要使用数据库 client,
import "github.com/jackc/pgx/v4"
func main() {
conn, err := pgx.Connect(context.Background(), "postgresql://user:password@localhost:5432/database")
if err != nil {
log.Fatalf("Unable to connect to database: %v\n", err)
}
defer conn.Close()
app.Get("/db", func(c *fiber.Ctx) error {
rows, err := conn.Query(context.Background(), "SELECT * FROM table")
if err != nil {
return err
}
defer rows.Close()
var result []string
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
return err
}
result = append(result, name)
}
return c.JSON(result)
})
app.Listen(":3000")
}
和 express 一样,fiber 也是使用回调捕获的方式来进行依赖注入。
FastAPI
Python 有三个主要的 Web 框架,分别是 Django、Flask 和 FastAPI。FastAPI 是最新的一个,也是最快的一个。
from fastapi import FastAPI, APIRouter
app = FastAPI()
router = APIRouter()
@router.get("/")
async def read_root():
return {"Hello": "World"}
app.include_router(router, prefix="/api")
@app.get("/ping")
async def pong():
return {"ping": "pong!"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
如果要获取参数,
@app.get("/hello/{name}")
async def hello(name: str):
return {"Hello": name}
@app.get("/hello")
async def hello(name: str):
return {"Hello": name}
class Item(BaseModel):
name: str
@app.post("/hello")
async def hello(item: Item):
return {"Hello": item.name}
如果要做依赖注入,需要使用Depends
,
from fastapi import Depends
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/db")
async def db(db: Session = Depends(get_db)):
return db.query(Table).all()