Julia 教程 / Web 开发(Genie.jl)
Web 开发(Genie.jl)
Genie.jl 是 Julia 生态中全功能的 MVC Web 框架,类似 Python 的 Django 或 Ruby 的 Rails。它提供了路由、模板、数据库 ORM、WebSocket 等完整功能,让 Julia 也能胜任 Web 应用开发。
1. Genie.jl 概述
1.1 安装与创建项目
using Pkg
Pkg.add("Genie")
# 创建新项目
using Genie
Genie.Generator.newapp("MyWebApp")
# 或在当前目录初始化
Genie.Generator.newapp(".", fullstack=true)
1.2 项目结构
MyWebApp/
├── app/
│ ├── resources/ # 资源(模型、控制器)
│ ├── assets/ # 前端资源(CSS、JS)
│ └── layouts/ # 布局模板
├── config/
│ └── env/ # 环境配置
├── db/
│ └── migrations/ # 数据库迁移
├── public/ # 静态文件
├── bootstrap.jl # 启动入口
└── routes.jl # 路由定义
1.3 启动服务器
# 方式一:使用 Genie 模块
using Genie
Genie.loadapp()
up(8000, "0.0.0.0"; open_browser=true)
# 方式二:命令行
# julia --project=. bootstrap.jl
2. 路由定义
2.1 基本路由
# routes.jl
using Genie.Router
# GET 路由
route("/") do
"欢迎来到首页!"
end
# 带路径参数
route("/hello/:name") do
"你好, $(params(:name))!"
end
# 指定 HTTP 方法
route("/submit", method=POST) do
"收到 POST 请求"
end
# 路由到控制器动作
route("/users", UsersController, :index)
route("/users/:id", UsersController, :show)
2.2 路由参数与查询参数
route("/search") do
query = params(:q, "default") # 获取查询参数,带默认值
page = params(:page, 1)
"搜索: $query, 第 $page 页"
end
# 访问 /search?q=julia&page=2
2.3 路由分组
# API 版本路由
route("/api/v1/users") do
json(["Alice", "Bob"])
end
route("/api/v2/users") do
json(Dict("users" => ["Alice", "Bob"], "count" => 2))
end
3. 控制器
3.1 创建控制器
# app/resources/users/UsersController.jl
module UsersController
using Genie.Renderer.Json
using Genie.Renderer.Html
function index()
users = [
Dict("id" => 1, "name" => "Alice"),
Dict("id" => 2, "name" => "Bob")
]
json(users)
end
function show()
user_id = params(:id)
json(Dict("id" => user_id, "name" => "User $user_id"))
end
function create()
data = jsonpayload()
# 处理创建逻辑
json(Dict("status" => "created", "data" => data), status=201)
end
end
3.2 控制器渲染 HTML
module PagesController
using Genie.Renderer.Html
function home()
html(:pages, :home, title = "首页", name = "Julia")
end
function about()
html(:pages, :about, title = "关于")
end
end
4. 模板渲染
4.1 HTML 模板
<!-- app/resources/pages/views/home.jl.html -->
<h1>欢迎, <%= @name %>!</h1>
<p>这是使用 Genie.jl 构建的页面。</p>
<% if @name == "admin" %>
<p>管理员面板</p>
<% end %>
<ul>
<% for i in 1:5 %>
<li>项目 <%= i %></li>
<% end %>
</ul>
4.2 布局模板
<!-- app/layouts/app.jl.html -->
<!DOCTYPE html>
<html>
<head>
<title><%= @title %></title>
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
</nav>
<main>
<%% yield %>
</main>
<footer>
<p>© 2026 Julia Web App</p>
</footer>
</body>
</html>
4.3 JSON 响应
using Genie.Renderer.Json
# 简单 JSON
json(Dict("message" => "success"))
# 带状态码
json(Dict("error" => "未找到"), status=404)
# JSON 数组
json([1, 2, 3, 4, 5])
5. 中间件
5.1 请求日志中间件
using Genie.Requests
function log_middleware(handler)
function(req)
start_time = time()
response = handler(req)
elapsed = time() - start_time
println("$(req.method) $(req.path) - $(round(elapsed*1000, digits=2))ms")
return response
end
end
# 注册中间件
Genie.AppServer.register_middleware(log_middleware)
5.2 CORS 中间件
function cors_middleware(handler)
function(req)
response = handler(req)
# 添加 CORS 头
Genie.Responses.setheader(response, "Access-Control-Allow-Origin", "*")
Genie.Responses.setheader(response, "Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
return response
end
end
5.3 认证中间件
function auth_middleware(handler)
function(req)
token = Genie.Requests.getheader(req, "Authorization")
if isnothing(token) || !validate_token(token)
return Genie.Renderer.Json.json(
Dict("error" => "未授权"),
status=401
)
end
return handler(req)
end
end
6. 数据库集成(SearchLight.jl)
6.1 配置数据库
# config/database.yml
adapter: SQLite
database: db/app.sqlite
using SearchLight
# 初始化数据库
SearchLight.Configuration.init()
SearchLight.Migration.init()
6.2 定义模型
# app/resources/users/User.jl
module UserModel
using SearchLight
Base.@kwdef mutable struct User <: AbstractModel
id::DbId = DbId()
name::String = ""
email::String = ""
age::Int = 0
end
end
# 定义表结构
# db/migrations/001_create_users.jl
module CreateUsers
using SearchLight.Migration
function up()
create_table(:users) do
[
column(:id, :integer, primary_key=true, autoincrement=true)
column(:name, :string, not_null=true)
column(:email, :string, not_null=true, unique=true)
column(:age, :integer, default=0)
]
end
end
function down()
drop_table(:users)
end
end
6.3 CRUD 操作
using UserModel
# 创建
user = User(name="Alice", email="[email protected]", age=25)
save!(user)
# 查询
users = all(User)
user = findone(User, email="[email protected]")
young_users = find(User, SQLWhereExpression("age < ?", [30]))
# 更新
user.name = "Alice Smith"
save!(user)
# 删除
delete!(user)
7. WebSocket
7.1 WebSocket 路由
using Genie.WebChannels
# 定义 WebSocket 频道
route("/ws/chat") do
WebChannels.subscribe(params(:ws_client), :chat)
"已连接到聊天频道"
end
# 广播消息
function broadcast_message(msg::String)
WebChannels.broadcast(:chat, msg)
end
7.2 客户端 JavaScript
// 前端 WebSocket 连接
const ws = new WebSocket("ws://localhost:8000/ws/chat");
ws.onopen = () => {
console.log("已连接");
ws.send(JSON.stringify({ type: "join", user: "Alice" }));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
displayMessage(msg);
};
ws.onclose = () => {
console.log("连接关闭");
};
function sendMessage(text) {
ws.send(JSON.stringify({ type: "message", text: text }));
}
8. REST API 开发
8.1 完整 REST API
# app/resources/todos/TodosController.jl
module TodosController
using Genie.Renderer.Json
using Genie.Requests
# 内存数据存储(生产环境应使用数据库)
const TODOS = Dict{Int, Dict}()
const COUNTER = Ref(0)
function index()
json(collect(values(TODOS)))
end
function show()
id = parse(Int, params(:id))
if haskey(TODOS, id)
json(TODOS[id])
else
json(Dict("error" => "未找到"), status=404)
end
end
function create()
data = jsonpayload()
COUNTER[] += 1
todo = Dict(
"id" => COUNTER[],
"title" => get(data, "title", ""),
"done" => false
)
TODOS[COUNTER[]] = todo
json(todo, status=201)
end
function update()
id = parse(Int, params(:id))
if !haskey(TODOS, id)
return json(Dict("error" => "未找到"), status=404)
end
data = jsonpayload()
merge!(TODOS[id], data)
json(TODOS[id])
end
function delete()
id = parse(Int, params(:id))
if haskey(TODOS, id)
delete!(TODOS, id)
json(Dict("status" => "deleted"))
else
json(Dict("error" => "未找到"), status=404)
end
end
end
# routes.jl
route("/api/todos", TodosController, :index)
route("/api/todos/:id", TodosController, :show)
route("/api/todos", TodosController, :create, method=POST)
route("/api/todos/:id", TodosController, :update, method=PUT)
route("/api/todos/:id", TodosController, :delete, method=DELETE)
9. 部署
9.1 Docker 部署
# Dockerfile
FROM julia:1.11
WORKDIR /app
# 安装依赖
COPY Project.toml Manifest.toml ./
RUN julia -e 'using Pkg; Pkg.activate("."); Pkg.instantiate(); Pkg.precompile()'
# 复制应用代码
COPY . .
# 暴露端口
EXPOSE 8000
# 启动
CMD ["julia", "--project=.", "bootstrap.jl"]
# docker-compose.yml
version: "3"
services:
web:
build: .
ports:
- "8000:8000"
environment:
- GENIE_ENV=production
volumes:
- ./db:/app/db
9.2 Nginx 反向代理
# /etc/nginx/sites-available/myapp
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# WebSocket 支持
location /ws/ {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# 静态文件
location /static/ {
alias /app/public/;
expires 30d;
}
}
10. HTTP.jl — 轻量级替代
对于简单场景,HTTP.jl 更轻量:
using HTTP
using JSON3
# 定义路由处理函数
function hello(req)
name = HTTP.queryparams(req).get("name", "World")
return HTTP.Response(200, JSON3.write(Dict("message" => "Hello, $name!")))
end
function create_item(req)
body = JSON3.read(String(req.body))
# 处理业务逻辑
return HTTP.Response(201, JSON3.write(Dict("status" => "created", "data" => body)))
end
# 路由表
const ROUTER = HTTP.Router()
HTTP.register!(ROUTER, "GET", "/hello", hello)
HTTP.register!(ROUTER, "POST", "/items", create_item)
# 启动
HTTP.serve(ROUTER, "0.0.0.0", 8080)
Genie.jl vs HTTP.jl 对比
| 特性 | Genie.jl | HTTP.jl |
|---|---|---|
| 类型 | 全功能 MVC 框架 | 底层 HTTP 库 |
| 模板引擎 | ✅ 内置 | ❌ 需第三方 |
| ORM | ✅ SearchLight | ❌ 需手动 |
| 中间件 | ✅ 完整支持 | ✅ 基础支持 |
| WebSocket | ✅ 内置 | ✅ 支持 |
| 学习曲线 | 中等 | 低 |
| 适用场景 | 大型 Web 应用 | API 服务、微服务 |
业务场景
场景一:数据可视化 Web 应用
构建一个数据分析平台:后端使用 Julia 进行数值计算和数据处理,Genie.jl 提供 Web 界面,通过 REST API 返回计算结果,前端使用 Chart.js 绘制图表。
场景二:机器学习模型服务
将训练好的 Julia 机器学习模型部署为 Web 服务。使用 HTTP.jl 构建轻量级 API,接收 JSON 输入,返回预测结果。Docker 容器化部署。
场景三:实时监控仪表板
使用 Genie.jl 的 WebSocket 功能构建实时数据监控系统。后端定时采集传感器数据,通过 WebSocket 推送到前端仪表板,实现毫秒级更新。
总结
| 主题 | 关键要点 |
|---|---|
| Genie.jl | 全功能 MVC,类似 Rails |
| 路由 | route("/path", Controller, :action) |
| 控制器 | 返回 html() 或 json() |
| 模板 | .jl.html 文件,支持 ERB 语法 |
| 数据库 | SearchLight.jl ORM |
| WebSocket | WebChannels 模块 |
| 部署 | Docker + Nginx |
| 轻量替代 | HTTP.jl 适合简单 API |