让微信小程序与Web应用共享同一套API接口是完全可行的,关键在于设计一套通用、安全、跨平台兼容的后端API。以下是详细的实现方案和最佳实践:
1. 统一API设计原则
RESTful API设计
// 示例:用户相关API
GET /api/users/:id // 获取用户信息
POST /api/users/login // 用户登录
PUT /api/users/:id // 更新用户信息
DELETE /api/users/:id // 删除用户
统一响应格式
{
"code": 200,
"message": "success",
"data": {
"userInfo": {
"id": 1,
"name": "张三"
}
},
"timestamp": 1640995200000
}
2. 认证机制统一
JWT认证方案
// 后端统一处理
app.use(async (ctx, next) => {
const token = ctx.get('Authorization') ||
ctx.query.token ||
ctx.request.body.token;
if (token) {
try {
const decoded = jwt.verify(token, SECRET_KEY);
ctx.state.user = decoded;
} catch (error) {
ctx.status = 401;
ctx.body = { code: 401, message: 'Token无效' };
return;
}
}
await next();
});
微信小程序登录流程
// 小程序端
async function login() {
const res = await wx.login();
const response = await request('/api/auth/wx-login', {
method: 'POST',
data: { code: res.code }
});
// 存储token
wx.setStorageSync('token', response.data.token);
setToken(response.data.token);
}
// Web端
async function webLogin(credentials) {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
});
const data = await response.json();
localStorage.setItem('token', data.token);
}
3. 跨域配置
后端CORS配置
// Node.js + Koa示例
app.use(cors({
origin: function (ctx) {
// 允许小程序和Web域名
const origins = [
'https://your-web-domain.com',
'https://servicewechat.com'
];
if (origins.includes(ctx.header.origin)) {
return ctx.header.origin;
}
return 'https://your-web-domain.com'; // 默认
},
credentials: true,
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
}));
4. 请求封装统一
统一请求工具类
class ApiService {
constructor(baseURL) {
this.baseURL = baseURL;
this.platform = this.detectPlatform();
}
detectPlatform() {
if (typeof wx !== 'undefined') return 'miniapp';
if (typeof window !== 'undefined') return 'web';
return 'unknown';
}
async request(options) {
const config = {
url: this.baseURL + options.url,
method: options.method || 'GET',
header: {
'Content-Type': 'application/json',
'Authorization': this.getToken(),
'X-Platform': this.platform
},
...options
};
try {
let response;
if (this.platform === 'miniapp') {
response = await this.miniappRequest(config);
} else {
response = await this.webRequest(config);
}
return this.handleResponse(response);
} catch (error) {
return this.handleError(error);
}
}
async miniappRequest(config) {
return new Promise((resolve, reject) => {
wx.request({
...config,
success: resolve,
fail: reject
});
});
}
async webRequest(config) {
const response = await fetch(config.url, {
method: config.method,
headers: config.header,
body: config.data ? JSON.stringify(config.data) : undefined
});
return {
statusCode: response.status,
data: await response.json()
};
}
getToken() {
if (this.platform === 'miniapp') {
return wx.getStorageSync('token');
} else {
return localStorage.getItem('token');
}
}
handleResponse(response) {
if (response.statusCode === 200) {
if (response.data.code === 200) {
return response.data;
} else {
throw new Error(response.data.message);
}
} else {
throw new Error(`HTTP ${response.statusCode}`);
}
}
}
5. 环境配置管理
配置文件
// config.js
const ENV_CONFIG = {
development: {
apiBaseURL: 'http://localhost:3000',
debug: true
},
production: {
apiBaseURL: 'https://api.yourdomain.com',
debug: false
}
};
export default ENV_CONFIG[process.env.NODE_ENV || 'development'];
小程序配置
// project.config.json
{
"setting": {
"urlCheck": false
},
"env": {
"production": {
"apiBaseURL": "https://api.yourdomain.com"
},
"development": {
"apiBaseURL": "http://localhost:3000"
}
}
}
6. 安全性考虑
接口安全措施
// 频率限制
app.use(rateLimit({
windowMs: 15 * 60 * 1000, // 15分钟
max: 100 // 限制每个IP 100次请求
}));
// 参数验证
app.post('/api/users/login', validate({
body: {
username: Joi.string().min(3).max(30).required(),
password: Joi.string().min(6).required()
}
}), loginHandler);
// 敏感操作日志
app.use(async (ctx, next) => {
const startTime = Date.now();
await next();
console.log({
timestamp: new Date().toISOString(),
ip: ctx.ip,
method: ctx.method,
url: ctx.url,
userAgent: ctx.get('User-Agent'),
duration: Date.now() - startTime,
status: ctx.status
});
});
7. 实际使用示例
获取用户信息
// 服务层
const api = new ApiService('/api');
// 小程序页面
Page({
async onLoad() {
try {
const result = await api.request({
url: '/users/profile',
method: 'GET'
});
this.setData({ userInfo: result.data.userInfo });
} catch (error) {
wx.showToast({ title: error.message, icon: 'none' });
}
}
});
// Web组件
function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
api.request({ url: '/users/profile' })
.then(result => setUser(result.data.userInfo))
.catch(error => console.error(error));
}, []);
return <div>{user?.name}</div>;
}
8. 最佳实践总结
- 统一API规范:保持RESTful风格,统一错误码
- 灵活认证:支持多种登录方式(微信、手机号、邮箱等)
- 跨域处理:正确配置CORS,区分环境
- 错误处理:统一错误处理机制,提供友好提示
- 性能优化:合理使用缓存,减少重复请求
- 监控日志:记录关键操作,便于问题排查
通过以上方案,可以实现微信小程序和Web应用无缝共享同一套API接口,提高开发效率和系统维护性。
云小栈