很多自动化脚本做到后面,都会需要一个能被浏览器、手机或其他程序调用的入口:浏览器点一下触发任务、外部程序 POST 数据给 AHK、本机网页显示运行状态,或者让局域网设备读取一个结果。为这类需求再安装一套完整 Web 服务通常太重,直接让 AHK 监听 HTTP 会更顺手。
我整理的 HttpServer_v1 面向 AutoHotkey v1,底层使用 Windows HTTP.sys。它不依赖 Apache、Nginx 或额外的服务端程序,脚本注册 URL 后即可接收请求。库里已经处理了异步请求、请求正文、响应头、文件和流式发送等底层工作,调用时主要关注路由函数里的业务逻辑。
它适合用在哪里
- 给本机自动化脚本提供 HTTP API;
- 接收浏览器表单、JSON、URL 编码数据和文件上传;
- 做局域网内的小型控制接口或状态页面;
- 向浏览器返回文本、HTML、JSON、文件下载和二进制数据;
- 把耗时结果分段发送,制作简单的流式响应;
- 使用 HTTP.sys 的证书绑定能力提供 HTTPS。
请求对象里有什么
处理函数会收到 req 和 rsp 两个对象。req.Verb 是 GET、POST 等请求方法,RawUrl 保留原始地址,AbsPath 便于做路由判断,Queries 已经整理查询参数,Headers 可以读取请求头,RemoteAddress 用来判断访问来源。
请求正文保存在 Body 中,库还会按 Content-Type 尝试整理为 Content。JSON、application/x-www-form-urlencoded 和 multipart/form-data 可以直接得到对象结构;不适合自动解析的内容仍可从 Body 的指针和长度读取。文件上传要特别注意大小限制、文件名清理和保存目录权限。
响应不只是一段文字
rsp.Set() 用来设置 Content-Type、Content-Disposition 和自定义响应头,rsp.Call() 负责结束一次普通响应。直接返回普通对象时,库会按 JSON 处理;传入 HttpServer.File 可以发送文件,传入 Buffer 可以发送二进制,带 Call() 的流对象可以多次 rsp.Send() 分段输出。
错误状态也不需要手工拼完整 HTTP 报文。可以给 rsp.Call() 指定状态码,或抛出 HttpServer._StatusException(),让库返回相应状态。正式脚本仍建议统一记录异常,避免把内部路径和错误细节原样发给访问者。
HTTP.sys、权限和 HTTPS
HttpServer_v1 注册的是 Windows HTTP.sys URL。使用 127.0.0.1 和高位端口最适合本地工具;如果注册 URL 时提示拒绝访问,需要检查 URL ACL,或用管理员权限完成相应的 URL 保留。监听局域网地址时还要处理 Windows 防火墙。
HTTPS 不是把地址里的 http 改成 https 就结束了。证书与端口由 HTTP.sys 管理,需要先用管理员命令绑定证书,素材中的 HTTPS 示例已经列出测试证书、sslcert 绑定和删除命令。证书指纹、主机名和端口必须与实际环境对应。
先从本地接口开始
我更建议新手先绑定 127.0.0.1,不要一开始就开放到局域网或公网。公开监听前至少要补上身份验证、请求体大小限制、上传文件校验、来源检查和日志记录。这套库适合嵌入桌面工具和内部自动化,不等于已经配好安全策略的公网网站框架。
下载包还包含兼容性示例、文件下载与 POST/上传示例、HTTPS 示例,以及带控制面板的完整展示。下面的基础 demo 只开放 ping、query 和 echo 三个本地接口,方便先确认 HTTP.sys 与路由处理正常。
本文移植自 thqby 的 ahk2_lib,由本站适配为 AutoHotkey v1 版本,原作者及项目版权归其所有。
源项目地址:https://github.com/thqby/ahk2_lib
打包库和示例的下载地址:
demo代码片段展示:
#Requires AutoHotkey v1.1
#NoEnv
#Persistent
#SingleInstance Force
SetBatchLines, -1
#Include <HttpServer_v1>
HttpDemo_Server := new HttpServer()
try
{
HttpDemo_Server.Add("http://127.0.0.1:18133/", Func("HttpDemo_Handler"))
ToolTip, HTTP 服务已启动:`nhttp://127.0.0.1:18133/`n按 Esc 退出
SetTimer, HttpDemo_HideTip, -3000
}
catch e
{
MsgBox, 16, HttpServer_v1, % "启动失败:`n" e.Message
ExitApp
}
return
HttpDemo_Handler(req, rsp)
{
local path, body
path := req.AbsPath
body := ""
if (IsObject(req.Body) && req.Body.Size)
body := StrGet(req.Body.Ptr, req.Body.Size, "UTF-8")
if (path = "/" || path = "/ping")
{
rsp.Set("Content-Type", "text/plain; charset=utf-8")
rsp.Call("pong")
return
}
if (path = "/query")
{
rsp.Call({ok: true, name: req.Queries["name"], remote: req.RemoteAddress})
return
}
if (path = "/echo")
{
rsp.Set("Content-Type", "text/plain; charset=utf-8")
rsp.Call("method=" req.Verb "`nbody=" body)
return
}
rsp.Set("Content-Type", "text/plain; charset=utf-8")
rsp.Call("not found: " req.RawUrl, 404)
}
HttpDemo_HideTip:
ToolTip
return
Esc::ExitApp

评论(0)