06-路由与控制器开发
# 第6章 路由与控制器开发
## 6.1 路由机制
### 6.1.1 URL格式
Xiuno BBS使用伪静态URL,格式为: `route-action-subaction.htm`
```
https://example.com/thread-123-1.htm
│ │ │
route action subaction
param(0) param(1) param(2)
```
**参数获取**:
```php
param(0); // 路由标识,如 'thread'
param(1); // 第一参数,如 123(主题ID)
param(2); // 第二参数,如 1(页码)
param('key'); // 查询参数,如 ?key=value
```
### 6.1.2 路由分发流程
```
请求 → index.php → _include(index.inc.php)
→ param(0) 获取路由标识
→ switch(param(0)) {
case 'thread': include route/thread.php; break;
case 'forum': include route/forum.php; break;
case 'user': include route/user.php; break;
// ... 核心路由
// hook: index_route_case_end.php ← 插件在此注册新路由
}
```
## 6.2 注册新路由
### 6.2.1 前台路由注册
通过`hook/index_route_case_end.php`注册:
```php
**关键**: 必须使用`_include()`加载路由文件,否则hook/overwrite机制不会生效。
### 6.2.2 普通路由注册
通过创建`route/mypage.php`注册路由:
```php
$uid), array('id'=>-1), $page, $pagesize);
// 渲染页面
include _include(APP_PATH.'plugin/my_plugin/view/htm/myfeature.htm');
} elseif($method == 'POST') {
// 处理表单提交
$input = param('input_field');
my_table_create(array('uid'=>$uid, 'field'=>$input));
message(0, '操作成功');
}
}
```
**前台URL格式**: `mypage-myfeature.htm`
### 6.2.2 挂靠在现有路由注册
通过`hook/my_end.php`注册个人中心子路由(更多路由可见route文件夹里的其他路由文件里的相似hook点):
```php
$uid), array('id'=>-1), $page, $pagesize);
// 渲染页面
include _include(APP_PATH.'plugin/my_plugin/view/htm/myfeature.htm');
} elseif($method == 'POST') {
// 处理表单提交
$input = param('input_field');
my_table_create(array('uid'=>$uid, 'field'=>$input));
message(0, '操作成功');
}
}
```
这样,**个人中心URL格式**: `my-myfeature.htm`
### 6.2.3 后台路由注册
通过`hook/admin_index_route_case_end.php`注册:
```php
-1), $page, $pagesize);
$total = my_table_count();
$pagination = pagination(url('mypage-list-{page}'), $total, $page, $pagesize);
include _include(APP_PATH.'plugin/my_plugin/view/htm/mypage_list.htm');
} elseif ($action == 'create') {
if ($method == 'GET') {
include _include(APP_PATH.'plugin/my_plugin/view/htm/mypage_create.htm');
} elseif ($method == 'POST') {
$title = param('title');
$content = param('content');
if (empty($title)) {
message(-1, '标题不能为空');
}
my_table_create(array(
'uid' => $uid,
'title' => $title,
'content' => $content,
'create_date' => time(),
'create_ip' => ip2long($ip),
));
message(0, '创建成功');
} elseif ($action == 'delete') {
$id = param(2, 0);
my_table_delete($id);
message(0, '删除成功');
} else {
// 默认动作
include _include(APP_PATH.'plugin/my_plugin/view/htm/mypage.htm');
}
```
### 6.3.2 param() 函数详解
`param()`是获取用户输入的核心函数,自动进行安全处理:
```php
param($key, $default = '', $filter = TRUE);
```
| 用法 | 说明 | 安全处理 |
|------|------|---------|
| `param(0)` | 获取URL第1段 | 返回字符串 |
| `param(1)` | 获取URL第2段 | 返回字符串 |
| `param(2, 0)` | 获取URL第3段,默认0 | 返回字符串 |
| `param('key')` | 获取查询参数 | htmlspecialchars防XSS |
| `param('key', '')` | 获取查询参数,默认空 | htmlspecialchars防XSS |
**类型转换**:
```php
$tid = param(1, 0); // 期望整数,默认0
$page = param(2, 1); // 期望整数,默认1
$name = param('name',''); // 字符串
```
> **坑**: `param()`返回的数字参数有可能是字符串类型。需要整数时必须强制转换: `$tid = intval(param(1, 0));`
### 6.3.3 特殊参数函数
尽量少用
```php
param_word($key) // 仅保留 [a-zA-Z0-9_]
param_base64($key) // 解码Base64参数
param_json($key) // 解码JSON参数
param_url($key) // 解码XiunoPHP特殊URL编码
```
## 6.4 请求与响应
### 6.4.1 判断请求方法
```php
global $method;
if ($method == 'GET') {
// 显示页面
} elseif ($method == 'POST') {
// 处理表单
}
```
### 6.4.2 返回消息
`message()`函数根据请求类型自动返回JSON或HTML:
```php
message(0, '操作成功'); // 成功
message(-1, '操作失败'); // 失败
message(1, jump('跳转', url('xxx'), 1)); // 跳转
```
**AJAX请求**: 返回JSON `{"code":0, "message":"操作成功"}`
**普通请求**: 返回HTML消息页面
### 6.4.3 登录检查
```php
user_login_check(); // 未登录则跳转登录页
```
### 6.4.4 权限检查
```php
$gid = $user['gid'];
if ($gid != 1) {
message(-1, '无权限访问');
}
// 或使用版块权限检查
if (!forum_access_user($fid, $gid, 'allowthread')) {
message(-1, '无权限发帖');
}
```
## 6.5 URL生成
### 6.5.1 url() 函数
```php
url('thread-123.htm'); // 生成主题链接
url('my-mypage.htm'); // 个人中心链接
url('my-mypage-{page}', array('page'=>2)); // 带参数链接
url('admin-myadmin.htm'); // 后台链接
```
> **坑**: 在HTML属性中使用`url()`时,使用单引号避免格式化问题: `href='= url("xxx"); ?>'`
### 6.5.2 分页生成
```php
$pagination = pagination(url('mypage-list-{page}'), $total, $page, $pagesize);
```
生成Bootstrap风格的分页HTML。
其中:
- `$total` 总记录数
- `$page` 当前页码
- `$pagesize` 每页显示记录数
## 6.6 AJAX交互
### 6.6.1 后端处理
```php
if ($method == 'POST') {
$tid = param('tid', 0);
// 处理逻辑...
message(0, '操作成功');
}
```
### 6.6.2 前端调用
```javascript
// 使用jQuery (Xiuno BBS内置jQuery)
$.xpost(url('my-mypage'), {tid: tid}, function(code, message) {
if (code == 0) {
// 成功
} else {
// 失败: message为错误信息
}
});
// 或使用原生fetch
fetch(url('my-mypage'), {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'tid=' + tid
}).then(r => r.json()).then(data => {
if (data.code == 0) {
// 成功
}
});
```
### 6.6.3 HTMX交互
备用方案,如果要开发HTMX交互的话
```html
```