开发一个微信小程序,难点通常不在页面能不能显示,而在页面、后端接口、登录态、隐私说明和正式发布配置能不能一起工作。开发者工具里预览成功,不代表体验版网络配置正确;拿到登录 code,也不代表用户授权了手机号、头像或位置。
本文以原生微信小程序客户端和 Django JSON API 为例,从列表与详情页走完接口约定、登录、隐私、测试和上线。若项目使用 Taro、uni-app 等框架,页面语法不同,但服务端鉴权、域名配置和发布检查仍然适用。通用交付方式可参考软件开发流程实战,网站内容与上线运维见企业网站开发实战;本文聚焦微信小程序特有的工程环节。
一条可复查的开发链路是:拆解页面和数据 → 定义 API → 配置合法域名 → 实现页面状态与服务端登录 → 完成隐私配置和体验测试 → 提交审核并验证正式版。
一、先明确客户端、后端和微信平台的边界
小程序负责交互和展示;自己的后端负责业务校验、数据权限和账号映射;微信平台提供小程序身份及开放能力。客户端代码会随小程序包分发,所以 AppSecret、数据库密码和第三方服务密钥只能放在服务端。
开始前先把用户动作、页面、接口和验收条件对应起来:
| 用户动作 | 页面与接口 | 验收条件 |
|---|---|---|
| 浏览列表 | 列表页、分页读取接口 | 加载中、空列表和网络错误都有明确状态 |
| 查看详情 | 详情页、按 ID 读取接口 | 无效 ID 返回未找到状态,不暴露堆栈 |
| 执行登录后操作 | 登录接口、受保护业务接口 | 未登录会被拒绝,重复请求不会重复产生副作用 |
先完成列表和详情,再增加登录或写入操作,可以把页面问题与鉴权问题分开排查。如果首版不需要个人信息,就不要为了演示登录额外收集手机号、头像或位置。
二、创建项目时区分环境
原生小程序通常由 app.json 声明页面,以各页面目录组织 JS、WXML、WXSS 和 JSON 配置。一个小型项目可以按职责组织:
miniprogram/
app.js
app.json
config/
index.js
services/
request.js
product.js
pages/
product-list/
product-detail/
components/
目录可以按团队习惯调整,重点是把网络请求和页面展示分开,避免每个页面重复拼接 URL、处理 401 和显示错误。
开发、体验和正式环境应使用各自真实的 API 配置。把 API 基址集中放在配置模块,确认体验版和正式版不会指向开发服务器。配置值必须来自已部署、可访问的 HTTPS 服务;不要把 localhost、临时内网地址或调试开关带入正式包。
还应记录 AppID、环境和基础库版本。AppID 是项目身份配置,AppSecret 是服务端秘密,不能因为 AppID 出现在项目配置里,就把 AppSecret 也放进客户端。
三、先定义 API 契约,再写页面
客户端与服务端先约定稳定的请求和响应结构。列表接口可以返回:
{
"results": [
{
"id": 42,
"title": "内容标题",
"summary": "供列表展示的简短摘要",
"cover_url": null
}
],
"page": 1,
"page_size": 20,
"has_next": false
}
摘要应始终使用约定的纯文本或 HTML 格式;空封面统一为 null;分页明确页码和是否还有下一页。错误响应使用稳定错误码和简短提示,不把 Python 异常、SQL 或服务器路径返回给客户端。
下面的请求层假设 API_BASE 从当前构建环境配置模块导入。正式构建前必须将它设置为当前环境真实的 API 基址:
import { API_BASE } from "../config";
export function request({ path, method = "GET", data }) {
if (!API_BASE) {
return Promise.reject(new Error("当前环境未配置 API 地址"));
}
const token = wx.getStorageSync("accessToken");
const header = token
? { Authorization: "Bearer " + token }
: {};
return new Promise((resolve, reject) => {
wx.request({
url: API_BASE + path,
method,
data,
header,
timeout: 10000,
success(response) {
if (response.statusCode >= 200 && response.statusCode < 300) {
resolve(response.data);
return;
}
const error = new Error("接口返回异常");
error.statusCode = response.statusCode;
reject(error);
},
fail(error) {
reject(error);
}
});
});
}
这只是请求封装,不包含重试。只对幂等的读取请求考虑有限重试;创建订单、提交表单等写操作,不能遇到超时就自动重发。服务端可以使用幂等键或业务唯一约束,避免重复记录。
页面应呈现真实加载状态,并分别处理错误和空列表:
import { request } from "../../services/request";
Page({
data: { items: [], loading: false, error: "" },
onLoad() {
this.loadItems();
},
async loadItems() {
this.setData({ loading: true, error: "" });
try {
const result = await request({
path: "/api/mini/v1/products?page=1&page_size=20"
});
this.setData({ items: result.results || [] });
} catch (error) {
this.setData({
error: error.statusCode === 401
? "登录状态已失效,请重新登录"
: "暂时无法加载,请检查网络后重试"
});
} finally {
this.setData({ loading: false });
}
}
});
有分页时,区分首次加载和追加加载,防止刷新时清空仍可用的内容;连续触发“加载更多”时,也要避免重复请求同一页。服务端分页可参考Django API 分页、缓存与数据库查询优化。
四、合法域名和 HTTPS 要在体验版验证
小程序使用 wx.request、上传或下载文件时,需要在微信公众平台配置对应类型的合法域名。上线前确认:
- 实际使用到的请求、上传、下载和 WebSocket 域名均已配置;
- HTTPS 证书有效、域名匹配,接口能从公网访问;
- DNS、反向代理路径和响应类型在体验版都正确;
- 体验环境和正式环境配置分离,正式包不会请求开发服务器。
开发者工具的调试设置可能放宽合法域名校验。本地预览成功并不能证明正式版本可请求。排查时先检查当前环境、公众平台合法域名、DNS、TLS 证书和代理路径。小程序网络问题不能只靠网页 CORS 配置解决。
五、登录 code 由服务端兑换
常见登录链路是:客户端调用 wx.login 获取临时 code,把 code 发给自己的后端;后端使用服务器保存的 AppID 和 AppSecret 调用微信 jscode2session,取得当前小程序用户标识;后端完成账号映射后,再签发自己系统的短期会话凭证。
wx.login()
-> 一次性临时 code
-> 自己的登录 API
-> Django 服务端调用 jscode2session
-> 按 (AppID, OpenID) 查找或创建账号
-> 签发自己的短期 access token
-> 客户端后续请求携带 Authorization
code 是临时凭证,只能使用一次。不能重复使用旧 code,也不能从客户端直接调用需要 AppSecret 的服务端接口。OpenID 是用户在特定小程序下的标识,不是密码或授权令牌;不同 AppID 下的 OpenID 不能假设相同。
下面用 Python 标准库展示服务端兑换的核心步骤。配置应从服务器环境或受限配置读取;不要在日志中记录完整请求 URL、AppSecret、code、session_key 或微信响应中的敏感字段。
import json
from urllib.error import HTTPError, URLError
from urllib.parse import urlencode
from urllib.request import urlopen
from django.conf import settings
class MiniProgramLoginError(Exception):
pass
def exchange_login_code(code):
if not code:
raise MiniProgramLoginError("缺少登录凭证")
params = {
"appid": settings.WECHAT_MINIAPP_APP_ID,
"secret": settings.WECHAT_MINIAPP_APP_SECRET,
"js_code": code,
"grant_type": "authorization_code",
}
endpoint = (
"https://api.weixin.qq.com/sns/jscode2session?"
+ urlencode(params)
)
try:
with urlopen(endpoint, timeout=5) as response:
payload = json.loads(response.read().decode("utf-8"))
except (HTTPError, URLError, TimeoutError) as exc:
raise MiniProgramLoginError("登录服务暂不可用") from exc
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise MiniProgramLoginError("登录服务返回了无效响应") from exc
if not isinstance(payload, dict):
raise MiniProgramLoginError("登录服务返回了无效响应")
if payload.get("errcode") != 0 or not payload.get("openid"):
raise MiniProgramLoginError("登录凭证无效或已过期")
return payload["openid"], payload.get("session_key")
此函数只应在服务端调用。若业务不需要微信侧数据解密,就不要持久化 session_key;确实需要时也要按最小范围保护。发给客户端的应是本站会话凭证,而不是 session_key。服务端还要校验令牌有效期、用户状态和资源权限;不能只凭客户端提交的 OpenID 判断操作合法。
短期 access token 可以存到小程序本地存储,但必须有过期时间和服务端撤销方式。AppSecret 永远只保存在服务端。登录也不等于用户同意收集手机号、头像或位置,这些能力需要分别设计。
六、把隐私授权放进功能设计
调用个人信息或敏感能力前,先确认功能是否真的需要该数据,并遵循微信当前的用户隐私保护流程:
- 列出每类个人信息对应的具体功能和用途;
- 在公众平台配置与实际功能一致的隐私保护指引;
- 在适当时机说明用途,并按平台要求取得授权;
- 用户拒绝时,仍提供不依赖该数据的可用路径;
- 后端只保存完成该功能所必需的数据,并按政策处理删除和留存。
不要在启动时一次性请求所有权限,也不要把登录成功表述成用户同意全部隐私条款。页面说明、隐私指引和实际 API 调用必须一致;功能变更后应重新检查。细节以微信官方小程序用户隐私保护指引为准。
七、体验版要覆盖失败路径
| 场景 | 应有结果 |
|---|---|
| 网络正常 | 页面内容出现,加载状态按时结束 |
| 断网、DNS 失败或接口超时 | 显示可重试提示,不假装成功 |
| 列表为空或详情 ID 无效 | 显示空状态或未找到状态 |
| token 过期、用户退出 | 受保护接口返回未授权,客户端重新登录 |
| 用户拒绝非必要权限 | 主要浏览功能仍可使用 |
| 连续点击提交或请求超时 | 不重复创建业务记录 |
| 图片失效或字段为空 | 布局不重叠、不溢出,文字仍能说明内容 |
| 正式域名配置错误 | 在体验阶段发现并修正 |
另外要在窄屏和较大字体下检查标题、按钮和错误提示,并确认敏感信息没有进入客户端日志。开发者工具可以补充检查,但网络、证书、设备差异和隐私流程仍应使用体验版验证。
八、审核发布后再做线上冒烟测试
正式提交前核对 AppID、环境配置、合法域名、隐私指引、页面入口、图片资源和审核说明。体验版验证后,通过微信公众平台提交审核;审核通过并发布后,用真实账号检查启动、列表、登录和关键业务操作。
后端接口应保持向后兼容。小程序版本发布需要平台流程,遇到客户端问题不一定能立即切回旧版;服务端可通过兼容旧字段、功能开关和分阶段启用,降低新旧客户端并存的风险。发布记录应保存版本号、后端版本、环境配置和降级步骤。审核与自动化发布细节可参考微信小程序 CI 文档。
常见故障排查
| 现象 | 优先检查 |
|---|---|
| 本地能请求,体验版失败 | 合法域名、当前环境配置、证书和 DNS |
| 登录偶发失败 | code 是否过期或复用、服务端超时和微信错误码 |
| 页面一直加载 | Promise 成功、失败和结束状态,服务端是否超时 |
| 只有正式版请求错误地址 | 发布包环境配置和公众平台配置是否一致 |
| 审核要求补充隐私信息 | 实际收集字段、隐私指引和授权时机是否一致 |
| 服务端返回正常但页面空白 | 字段名称、分页结构、空值类型和页面条件判断 |
排障日志保留请求 ID、路径、状态码和耗时即可;不要记录 code、token、AppSecret、手机号等秘密或个人信息。客户端提示应让用户知道下一步,详细堆栈留在受限的服务端日志中。
九、小程序页面不等于公开网页 SEO
小程序是微信平台内的应用。页面能否被微信搜索或其他搜索引擎发现,取决于平台规则、页面开放方式和实际抓取能力;不能把小程序路由直接当成已具备公开网页的 canonical、sitemap 或搜索排名。若文章、产品说明等内容需要参与公开网页搜索,应同时提供可访问、可索引且内容完整的网页,再通过搜索平台验证。
小程序与网站可以共享内容数据,但要分别验证访问入口、权限和索引信号。不要在小程序页面堆关键词,也不要承诺发布后必然收录或排名。站内站内链接审计实战介绍了公开网站的发现路径和内链检查。
发布前检查清单
- [ ] 页面、API 和验收条件已经对应。
- [ ] AppSecret 只在服务端配置,没有进入客户端包或日志。
- [ ] API 基址来自当前构建环境,体验版与正式版分别验证。
- [ ] 微信公众平台已配置真实 HTTPS 合法域名。
- [ ] 页面处理了加载、空数据、网络失败、未授权和服务端错误。
- [ ] 登录 code 由后端兑换,客户端使用本站会话凭证。
- [ ] 个人信息按功能最小化,并完成隐私说明与授权流程。
- [ ] 体验版覆盖断网、过期令牌、拒绝授权和重复提交。
- [ ] 正式发布后完成真实账号冒烟测试,并记录版本与后端配置。
小程序开发流程不止于页面编译成功。把 API 契约、服务端登录、合法域名、隐私边界、体验版失败路径和正式版冒烟测试连成闭环,才能让“能预览”变成“可维护地上线”。示例需要按项目真实的业务模型、API 配置和权限策略调整。
评论 (0)
暂无评论,快来抢沙发吧!