很多自动化脚本做到后面,都会需要一个能被浏览器、手机或其他程序调用的入口:浏览器点一下触发任务、外部程序 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
声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。