HttpServer 简介

HttpServer属于高级开发指南的内容,但是在开发使用大模型API的应用(例如MCP服务)时,可能需要使用到Web服务,所以这里简单介绍一下HttpServer,其更详细的使用可参考高级开发指南。

HttpServer是 Foxtable 内置的一个全局变量,作用是启动一个 HTTP 服务。启动后,浏览器(或任何 HTTP 客户端)访问这个端口,Foxtable 就会触发 HttpRequest 事件,你在事件里写代码决定返回什么。

一句话流程:启动 HttpServer → 浏览器发请求 → 触发 HttpRequest 事件 → 你写代码响应

最小示例

启动代码(可放在按钮事件Click、项目事件AfterOpenProject里):

HttpServer.Prefixes.Add("http://127.0.0.1/")
HttpServer.Start()

响应代码(网络监视器 → HttpRequest 事件):

e.WriteString("Hello World")

在浏览器输入地址 "http://127.0.0.1/",页面显示"Hello World"。

重要提示:必须以管理员身份启动Foxtable,才能开启Web服务。

启动与关闭服务

成员 类型 说明
HttpServer.Prefixes.Add(地址) 方法 绑定 IP 和端口
HttpServer.Start() 方法 启动服务
HttpServer.Close() 方法 关闭服务
HttpServer.IsRunning 属性 是否正在运行

例如:

If HttpServer.IsRunning Then
    HttpServer.Close()

End
If
HttpServer.Prefixes.Clear()
'清空原来绑定的IP
HttpServer.Prefixes.Add(
"http://127.0.0.1/")
HttpServer.Start()

绑定地址的写法:

' 默认端口 80,浏览器访问可省略端口号
HttpServer.Prefixes.Add(
"http://*/") 

'
只允许本机访问
HttpServer.Prefixes.Add(
"http://127.0.0.1/") 

'
只允许局域网访问
HttpServer.Prefixes.Add(
"http://192.169.10.100/") 

'
指定端口,末尾斜杠不可省略
HttpServer.Prefixes.Add(
"http://*:32177/")

可以一次绑定多个地址:

HttpServer.Prefixes.Add("http://*/")
HttpServer
.Prefixes.Add("http://*:32177/")
HttpServer
.Prefixes.Add("http://192.168.0.100:32188/")
HttpServer
.Start()

静态文件服务

成员 说明
HttpServer.WebPath 指定静态文件目录,目录中已存在的文件会被自动发送
HttpServer.Extensions 管理可自动发送的文件后缀名(必须小写、以`.` 开头)

设置 WebPath 后,请求的文件若在该目录下存在,系统直接发送,不再触发 HttpRequest 事件。

HttpServer.Prefixes.Add("http://*/")
HttpServer.WebPath =
"d:\web" ' 静态文件目录
HttpServer.Start()


此时访问"http://127.0.0.1/logo.png",若"d:\web\logo.png" 存在,会直接返回该图片,不再触发 HttpRequest 事件

通过 Extensions 管理可发送的后缀名,例如:

HttpServer.Extensions.Remove(".doc") ' 不再自动发送 .doc
HttpServer.Extensions.Add(
".table") ' 允许自动发送 .table

默认支持:.jpg .gif .png .bmp .js .css .html .htm .zip .rar .txt .json .svg .doc .docx .xls .xlsx 等。

HttpRequest 事件e参数成员总览

HttpRequest事件的e参数 为RequestEventArgs类型,下面的两个表格列出了其全部的属性和方法,后面会逐个细讲。

属性 类型 说明
e.Path String 请求路径,分隔符是 `\`
e.Host String 客户端访问的 IP 或域名
e.Port Integer 客户端访问的端口号
e.Values 字典 GET + POST 的全部提交数据
e.GetValues 字典 仅 GET 方式的数据
e.PostValues 字典 仅 POST 方式的数据
e.PlainText String 收到的纯文本数据
e.Files 字典 上传的文件
e.Cookies 字典 收到的 Cookie
e.Headers 字典 客户端请求头
e.ResponseHeaders 字典 设置的响应头
e.StatusCode Integer HTTP 状态码
e.CacheTime Integer 缓存时间(秒)
e.ResponseEncoding String 响应编码,默认 utf-8
e.Request HttpListenerRequest 底层请求对象
e.Response HttpListenerResponse 底层响应对象
方法 说明
e.WriteString(text) 发送文本(默认 html)
e.WriteString(text, contentType) 发送指定内容类型的文本
e.WriteFile(filePath) 发送本地文件
e.SaveFile(key, uploadFile, localFile) 保存上传的文件
e.Redirect(url) 跳转
e.AppendCookie(name, value) 写入 Cookie
e.WriteDataTable(...) 发送 DataTable
e.WriteBookAsHTML(...) 发送 Excel 报表为网页
e.WriteBookAsPDF(...) 发送 Excel 报表为 PDF

开启Web服务

接下来我们开始实例讲述,请先新建一个项目,在项目事件AfterOpenProject加上代码,用于开启Web服务并指定静态文件存储目录:

HttpServer.Prefixes.Add("http://*/")
HttpServer
.WebPath = "d:\web"  '指定静态文件存储位置
HttpServer
.Start()

我们将这个含有Web服务的项目称之为"服务端项目"。
通常我们是通过浏览器访问Web服务的,但很多时候不是通过浏览器,而是通过代码访问Web服务。
需要特别注意的是:服务端项目开启WEB服务后,不可以在服务端项目通过代码访问这个Web服务,只能在其他项目通过代码访问之
所以如果要通过代码测试Web服务的话,还需要另外创建一个项目,我们称之为"客户端项目"
HttpServer和HttpRequest都是运行在服务端项目的,而用于测试web服务的HttpClient则是运行在客户端项目,接下来可能不会特别说明,切记切记。

获取请求信息

属性 类型 说明
e.Host String 返回客户端访问的 IP 或域名
e.Path String 返回请求路径,分隔符是 `\`

例如将 服务端项目的 HttpRequest事件代码设置为:

e.WriteString(e.Host & vbCrLf & e.Path & vbCrLf & e.Port)

在客户端项目的命令窗口执行:

Dim hc As New HttpClient("http://127.0.0.1/fox/china.htm")
Return
hc.GetData()

显示的结果为:
 
127.0.0.1
fox\china.htm
80

e.Port Integer 返回客户端访问的端口号

我们可以根据Host、Path和Port分发内容,例如将服务端的HttpRequest事件代码设置为:

Select Case e.Host
   
Case "127.0.0.1" '本机访问
       
Select Case e.Path
           
Case ""
               
Select Case e.Port
                   
Case 80
                        e.WriteString(
"本机默认页面")
                   
Case 32188
                        e.WriteString(
"本机32188端口默认页面")
               
End Select
           
Case "about.htm"
                e.WriteString(
"本机关于页面")
       
End Select
   
Case "192.168.0.100" '局域网访问
        e.WriteString(
"来自局域网 的访问者")
   
Case Else '互联网访问
        e.WriteString(
"来自互联网 的访问者")
End
Select



获取请求数据
 

属性 类型 说明
e.GetValues 字典 仅 GET 方式的数据,是Values的子集

服务端项目的 HttpRequest事件代码设置为:

Dim
name As String
Dim
age As Integer
If
e.GetValues.ContainsKey("name") Then
    name = e.GetValues(
"name")
End
If
If
e.GetValues.ContainsKey("age") Then
   
Integer.TryParse(e.GetValues("age"), age)
End
If
e.WriteString(
"姓名:" & name & ",年龄:" & age)

客户端命令窗口执行:

Dim
hc As New HttpClient("http://127.0.0.1/test.htm?name=yang&age=20")
Return
hc.GetData()

显示的结果为:
姓名:yang,年龄:20

e.PostValues 字典 仅 POST 方式的数据,是Values的子集

例如服务端HttpRequest事件代码:

Select Case e.Path
   
Case "accept.htm"
       
Dim name As String = e.PostValues("name")
       
Dim age As String = e.PostValues("age")
        e.WriteString( name &
"," & age)
End
Select

客户端命令窗口执行:

Dim
hc As New HttpClient("http://127.0.0.1/accept.htm")
hc.FormData.Add(
"name", "yang")
hc.FormData.Add(
"age", 20)
Return
hc.GetData()

显示的结果为:yang,20

e.Values 字典 返回GET + POST 的全部提交数据,是GetValues和PostValues的合计

例如服务端HttpRequest事件代码:

Select Case e.Path
   
Case "accept.htm"
       
Dim sb As New StringBuilder
        sb.AppendLine(
"通过GET方式提交的数据:")
       
For Each key As String In e.GetValues.Keys
            sb.AppendLine(key &
":" & e.GetValues(key))
       
Next
        sb.AppendLine(vbCrLf &
"通过POST方式提交的数据:")
       
For Each key As String In e.PostValues.Keys
            sb.AppendLine(key &
":" & e.PostValues(key))
       
Next
        sb.AppendLine(vbCrLf &
"二者合集:")
       
For Each key As String In e.Values.Keys
            sb.AppendLine(key &
":" & e.Values(key))
       
Next
        e.WriteString(sb.ToString)

End
Select

客户端命令窗口执行:

Dim hc As New HttpClient("http://127.0.0.1/accept.htm?id=1&page=10")
hc.FormData.Add(
"name", "yang")
hc.FormData.Add(
"age", 30)
Return
hc.GetData()

显示的结果为:

通过GET方式提交的数据:
id:1
page:10

通过POST方式提交的数据:
name:yang
age:30

二者合集:
name:yang
age:30
id:1
page:10

e.PlainText String 收到的纯文本数据,当客户端以 application/json、text/plain 等类型 POST 时,请求体会落在 e.PlainText(不会进 PostValues)

例如服务端HttpRequest事件代码:

Dim jo As JObject = JsonConvert.DeserializeObject(e.PlainText)
Dim
name As String = jo("name")
Dim
age As Integer = jo("age")
e.WriteString(
"姓名:" & name & ", 年龄:" & age)

客
户端命令窗口执行:

Dim hc As New HttpClient("http://127.0.0.1/accept.htm")
Dim
jo As JObject = New JObject() From {
                        {
"name", "yang"},
                        {
"age", 20}
                    }
hc.Content = jo.ToString()

Return
hc.GetData()

显示的结果为:姓名:yang, 年龄:20

文件的上传与下载

属性/方法 说明
e.WriteFile(filePath) 发送本地文件到客户端

下面是一个通过代码从服务端下载文件到客户端的例子。

服务端HttpRequest事件代码:

Select Case e.Path
   
Case "report"
        e.WriteFile(
"d:\web\focas.pdf")
End
Select

客户端命令窗口执行:

Dim hc As New HttpClient("http://127.0.0.1/report/")
If
hc.GetFile("c:\test\focas.pdf") Then
    MessageBox.show(
"文件下载成功", "提示")
Else

    MessageBox.show(
"文件下载失败", "提示")
End
If

执行后会将下载好的文件保存为"c:\test\focas.pdf"

e.Files 字典,包括客户端上传的全部文件,键为文件上传组件的name属性,值是一个字符串集合,包括用户通过这个上传组件上传的所有文件名
e.SaveFile(key, uploadFile, localFile) 保存客户端上传的文件。

下面是一个通过代码从客户端上传文件到服务端的例子。

服务端HttpRequest事件代码:


Select
Case e.Path
   
Case "upload.htm"
       
Dim name As String = e.PostValues("name")
       
For Each key As String In e.Files.Keys
           
For Each fl As String In e.Files(key)
                 e.SaveFile(key, fl,
"d:\web\uploadfiles\" & fl)
           
Next
       
Next
        e.WriteString(
"员工[" & name & "]的资料已经上传成功")
End
Select

客户端命令窗口执行:

Dim hc As New HttpClient("http://127.0.0.1/upload.htm")
hc.FormData.Add(
"name", "张三")
hc.Files.Add(
"photo", "c:\test\photo.png")
Dim
ret As String = hc.GetData()
MessageBox.Show(ret)

执行后,如果文件上传成功,会提示:员工[张三]的资料已经上传成功

发送响应

方法/属性 说明
e.WriteString(text) 发送文本,例如:
e.WriteString("<h1>标题</h1>")
e.WriteString(text, contentType) 发送指定内容类型的文本,例如:
e.WriteString(
"{""code"":0,""msg"":""ok""}", "application/json")
e.WritePDFCreator(Creator, FileName, Inline) 将PDFCreator生成的PDF文档发送到客户端,参数:

Creator: 要发送的PDFCreator
FileName: 客户端浏览器下载此文件时使用的文件名
InLine: 可选参数,逻辑型,是否直接在浏览器显示PDF文件,默认为True,设为False将直接下载。

服务端HttpRequest事件代码:

Dim pdc As New PDFCreator()
Dim
rect As RectangleF = pdc.PageRectangle()
rect.Inflate( - 72, - 72)
'
Dim
fnt As New Font("微软雅黑", 12)
pdc.DrawString(
"Hello Foxtable!", fnt, Brushes.Black, rect)
e.WritePDFCreator(pdc,
"test.pdf")

客户端命令窗口执行:

Dim htc As New HttpClient("http://127.0.0.1")
Dim
file As String = "c:\test\123.pdf"
If
htc.GetFile(file) Then
    Process.Start(file)

End
If

执行后会下载并显示一个pdf文件。

e.WriteWordCreator(Creator, FileName, Inline)
 
将WordCreator生成的Word文档发送到客户端,参数:

Creator: 要发送的WordCreator
FileName: 客户端浏览器下载此文件时使用的文件名
InLine: 可选参数,逻辑型,是否直接在浏览器显示Word文件,默认为True,设为False将直接下载。

服务端HttpRequest事件代码:

Dim wdc As New WordCreator()
Dim
txt As String
For
i As Integer = 1 To 5
    txt = txt &
"Foxtable不仅是一个优秀的应用软件,同时又是一个高效率的开发工具。"
Next
With
wdc.AddParagraph(txt)
    .ForeColor = Color.Green
    .Font =
New Font("宋体", 9)
    .FirstLineIndent = 20
    .SpaceBetweenLines = 10
    .Alignment = Word.RtfHorizontalAlignment.Justify

End
With
e.WriteWordCreator(wdc,
"test.docx")

客户端命令窗口执行:

Dim htc As New HttpClient("http://127.0.0.1")
Dim
file As String = "c:\test\123.docx"
If
htc.GetFile(file) Then
    Process.Start(file)

End
If

执行后会下载并显示一个word文件。

e.Redirect(url) 跳转到另一个地址,例如:
e.Redirect("http://www.foxtable.com")
跳转到本站页面:
e.Redirect("about.htm")
e.StatusCode HTTP 状态码

一般无需设置;需要返回特定状态码时使用,例如:

e.StatusCode = 404
e.WriteString(
"页面不存在")

e.CacheTime 缓存时间(秒)。

让浏览器在指定时间内使用缓存,不再重复请求,例如:

If e.Path = "logo.png" Then
    e.CacheTime = 3600 
' 缓存 1 小时
    e.WriteFile(
"d:\web\logo.png")
End
If
e.ResponseEncoding 响应编码,默认 utf-8

一般无需设置,如果网页内容是别的编码(如 `gb2312`),需显式设置,否则可能乱码。

e.ResponseEncoding = "gb2312"
e.WriteString(
"<meta charset='gb2312'>中文内容")

收发Cookie

属性 说明
e.Cookies 字典,从服务端收到的Cookie
e.AppendCookie(name, value) 方法,向客户端写入Cookie

服务端HttpRequest事件代码:

Dim Verified As Boolean
If
e.Cookies.ContainsKey("username") AndAlso e.Cookies.ContainsKey("password") Then
    Dim username As String  = e.Cookies("username")
    Dim password As String  = e.Cookies("password")
    If username =
"
张三" AndAlso password = "888" Then
        Verified  = True
    End
If

End
If
If
Verified = False Then
    e.AppendCookie("error",
"
用户身份验证失败!") '通过Cookie向客户端发送信息.
    Return
End
If
Select
Case e.Path
    Case "gettime.htm"
        e.WriteString(Date.Now)

End
Select

客户端命令窗口执行:

Dim hc As New HttpClient("http://127.0.0.1/gettime.htm")
hc
.sCookies.Add("username","张三") '通过Cookie向服务端传递信息.
hc
.sCookies.Add("password","888")
Dim
val As String = hc.GetData()
If
hc.rCookies.ContainsKey("error") Then
    MessageBox.show(hc.rCookies("error"),
"
错误",  MessageBoxButtons.OK, MessageBoxIcon.Warning)
Else
    Dim dt As Date = val
    MessageBox.Show(dt)

End
If

提示:
1、cookie名区分大小写。
2、HttpClient的SCookies属性表示要发送给服务端的Cookies,
rCookies表示从服务端收到的Cookies,二者的类型都是字典

请求头和响应头

HttpRequest和头部信息相关的属性有:

属性 类型 说明
e.Headers 字典 客户端发来的请求头
e.ResponseHeaders 字典 发送到客户端的响应头

HttpClient也有两个完全同名的属性:

属性 类型 说明
Headers 字典 发送到服务端的的请求头
ResponseHeaders 字典 服务端发来的响应头

Headers是请求头,方向是从客户端到服务端;ResponseHeaders是响应头,方向是从服务端到客户端。

下面是个小例子,
服务端HttpRequest事件代码:

Dim author As String = UrlDecode(e.Headers("author")) '获取客户端发来的头部信息
Dim
age As String = e.Headers("age")
e.ResponseHeaders.Add(
"brief", URLEncode("越努力越幸运")) '向客户端发送头部信息
e.ResponseHeaders.Add(
"pages", 600)
e.WriteString(
"你好," & author)

客户端命令窗口执行:

Dim htc As New HttpClient("http://127.0.0.1")
htc.Headers.Add(
"author", UrlEncode("舒淇")) '向服务端附加头部信息
htc.Headers.Add(
"age", 30)
Dim
ret As String = htc.GetData()
Dim
brief As String = UrlDecode(htc.ResponseHeaders("brief")) '获取服务 端发来的头部信息
Dim
pages As Integer = htc.ResponseHeaders("pages")
Output.Show(ret & vbCrLf & brief & vbCrLf & pages)

执行后显示的结果是:

你好,舒淇
越努力越幸运
600

异步执行

如果服务端的访问量比较大, 可以考虑异步执行HttpRequest事件代码,关于异步编程,可以参考:异步编程

要改为异步执行,可以分两步修改代码:

1、将HttpRequest事件的代码移到一个自定义函数中:

Dim
e As RequestEventArgs = Args(0)
'原来的HttpRequest事件代码直接复制到这里,不用做任何改变

2、
HttpRequest事件代码改为:
'''Async
e.AsyncExecute =
True '通知需要异步执行或直接通过底层对象处理本次访问
Await
Functions.ExecuteAsync("自定义函数名", e)
e.Handled =
True '通知系统已经处理完毕

下面是一个简单的示例:

1、服务端项目新建一个自定义函数,名称为"Sum",代码为:

Dim e As RequestEventArgs = Args(0)
Select
Case e.Path
   
Case "Sum.htm"
        
Dim total As Long = 0
        
For i As Integer = 1 To 100000
             total = total + i
        
Next
        e.WriteString(
"结果:" & total)
End
Select

2、服务端的Httprequest事件代码为:

'''Async
e.AsyncExecute =
True '通知需要异步执行或直接通过底层对象处理本次访问
Await
Functions.ExecuteAsync("Sum", e)
e.Handled =
True '通知系统已经处理完毕

3、客户端命令窗口执行:

Dim htc As New HttpClient("http://127.0.0.1/sum.htm")
Dim
ret As String = htc.GetData()
Return
ret

或:


'''
Async
Dim
htc As New HttpClient("http://127.0.0.1/sum.htm")
Dim
ret As String = Await htc.GetDataAsync()
Return
ret

执行后会显示"结果:5000050000"

底层对象

底层对象适合需要精细控制的专业开发者,相关属性有:

属性 类型 说明
e.Request HttpListenerRequest 底层请求对象

例如通过底层对象读取请求头:

e.AsyncExecute = True '如果要使用底层对象,必须首先将AsyncExecute设置为True
Dim
auth As String = e.Request.Headers("Authorization")
e.WriteString("Authorization = " & auth)
e.Handled = True '使用结束后,无需手动清理资源,将Handled属性设置为True即可

e.Response HttpListenerResponse 底层响应对象

例如用底层对象精确写字节流:

e.AsyncExecute = True '如果要使用底层对象,必须首先将AsyncExecute设置为True
Dim
response As System.Net.HttpListenerResponse = e.Response
response.ContentType =
"application/json"
Dim
bytes As Byte() = System.Text.Encoding.UTF8.GetBytes("{""ok"":true}")
response.OutputStream.Write(bytes, 0, bytes.Length)

e.Handled =
True '使用结束后,无需手动清理资源,将Handled属性设置为True即可

 


本页地址:http://www.foxtable.com/webhelp/topics/7234.htm