处理 HTTP 请求中的自定义路由和负载
在 MATLAB® Production Server™ 中,您可以使用已部署的 MATLAB 函数作为 Web 请求处理程序。这些处理程序允许自定义请求和路由映射:
MATLAB Production Server 的 Web 请求处理程序提供了灵活的客户端-服务器通信。
客户端程序员可以将自定义的 HTTP 标头和有效负载通过 RESTful 请求发送到服务器。
服务器管理员可以提供请求 URL 到已部署的 MATLAB 函数的灵活映射。
服务器管理员可以提供静态文件服务。
MATLAB 程序员可以在部署到 MATLAB Production Server 的函数中返回自定义 HTTP 标头、HTTP 状态代码、HTTP 状态消息和有效负载。
要创建 Web 请求处理程序,您需要编写一个接受并返回请求数据的 MATLAB 函数,然后将其部署到服务器。然后,您可以在服务器上的 JSON 文件中、在打包到包含该函数的部署存档中的 JSON 文件中或两个文件中指定该函数的自定义 URL 路由。
为 Web 请求处理程序编写 MATLAB 函数
要作为 Web 请求处理程序工作,您部署到服务器的 MATLAB 函数必须接受一个标量结构体数组作为输入并返回一个标量结构体数组作为输出。
函数输入参量中的结构体提供了有关客户端请求的信息。在请求中,客户端可以发送自定义 HTTP 标头和自定义有效负载。已部署函数可以接受的有效负载没有数据格式限制。例如,该函数可以接受二进制或 ASCII 格式的原始数据、CSV 数据或不在 MATLAB Production Server RESTful API 指定的架构中的 JSON 数据。客户端还可以使用 Transfer-Encoding: chunked
标头分块发送数据。在分块传输编码中,虽然服务器以块的形式接收有效负载,但输入结构体会接收整个有效负载数据。
函数输入参量中的结构体包含以下字段。
字段名称 | 数据类型 | 维度 | 描述 |
---|---|---|---|
ApiVersion | double | 1 x 3 | 输入结构体模式的版本,格式为 <major> <minor> <fix> |
Body | uint8 | 1 x N | 请求负载 |
Headers | cell | N x 2 | HTTP 请求标头 元胞数组中的每个元素代表一个标题。每个元素都是一个键值对,其中键的类型为 |
HttpVersion | double | 1 x 2 | HTTP 版本格式为 <major> <minor> |
Method | char | 1 x N | HTTP 请求方法 |
Path | char | 1 x N | 请求 URL 路径 |
由于部署的 MATLAB 函数可以接受 RESTful 请求中的自定义标头和有效负载,因此您可以根据请求标头数据改变 MATLAB 函数的行为。您可以使用函数输出参量中的结构体来返回带有自定义 HTTP 标头和有效负载的响应。如果有服务器处理错误,则会覆盖您可能设置的任何自定义 HTTP 标头。如果发生 MATLAB 错误,服务器将返回 HTTP 500 Internal Server Error
响应。结构体中的所有字段都是可选的。
输出参量中的结构体可以包含以下字段:
字段名称 | 数据类型 | 维度 | 描述 |
---|---|---|---|
ApiVersion | double | 1 x 3 | 输出结构体模式的版本,格式为 <major> <minor> <fix> |
Body | uint8 | 1 x N | 响应负载 |
Headers | cell | N x 2 | HTTP 响应标头 元胞数组中的每个元素代表一个标题。每个元素都是一个键值对,其中键的类型为 |
HttpCode | double | 1 x 1 | HTTP 状态代码 |
HttpMessage | char | 1 x N | HTTP 状态消息 |
配置 URL 路由
自定义 URL 路由允许服务器将请求 URL 中的路径映射到服务器上部署的任何可部署存档和 MATLAB 函数。您可以在服务器实例级别或可部署存档级别定义 URL 路由。
实例级路由 - 在位于服务器实例上的单个 JSON 文件中定义 URL 路由。这些路由将请求 URL 映射到服务器上部署的所有存档中的 Web 处理程序函数。 (自 R2022a 起)
存档特定路由 - 在其自己的 JSON 文件中为单个存档定义 URL 路由,并将文件打包到存档中。这些路由仅将请求 URL 映射到服务器上该存档中定义的 Web 处理程序函数。您可以使用特定于存档的路由来更好地组织路由并避免路由匹配冲突。 (自 R2023b 起)
您可以定义这些路由的组合,并且两种类型的路由都是可选的。
配置实例级路由
自 R2022a 起
在服务器实例级别,要指定请求 URL 到已部署的 MATLAB 函数的映射,您可以使用服务器上存在的 JSON 文件。该文件的默认名称为 routes.json
,默认位置在
文件夹中。您可以通过更改 $MPS_INSTALL
/configmain_config
服务器配置文件中的 routes-file
属性值来更改文件名及其位置。对 routes.json
进行任何更新后,您必须重新启动服务器。
当服务器启动时,它会尝试读取 routes.json
文件。如果该文件不存在或包含错误,则服务器不会启动,并将错误消息写入 log-root
属性指定的文件夹中的 main.log
文件。
默认的 routes.json
文件包含一个 version
字段,其值为 1.0.0
,以及一个空的 pathmap
字段。version
指定文件的架构版本。您不需要改变其值。要允许自定义路由,请编辑文件以在 pathmap
数组中指定映射规则。数组中的每个对象对应一个 URL 路由。
实例级路由 JSON 文件遵循此模式。
{ "version": "1.0.0", "pathmap": [ { "match": "<regular_expression>", "webhandler": { "component": "<name_of_deployable_archive>", "function": "<name_of_deployed_function>" } }, { "match": "<regular_expression>", "webhandler": { "component": "<name_of_deployable_archive>", "function": "<name_of_deployed_function>" } } ] } |
要指定 URL 映射规则,请使用 pathmap
数组中的 match
和 webhandler
字段。
客户端请求 URL 仅当在主机名和端口后包含斜杠时才匹配路由,如
http://
。host
:port
/在
match
字段中,指定使用 ECMAScript 语法的正则表达式来匹配请求 URL 中的路径。如果请求 URL 与
match
字段中的多个正则表达式匹配,则选择从文件开头开始的第一个匹配项。因此,请按照从更具体到更一般的顺序指定模式匹配。如果请求 URL 的任何子字符串匹配,则正则表达式模式被视为匹配。例如,模式
a/b
与a/b
、/a/b
和/x/a/b/y
匹配。但是,您可以使用正则表达式锚点^
和$
来匹配特定字符之前或之后的位置。例如,模式^a/b$
仅匹配a/b
。您可以指定与请求 URL 中的查询参数匹配的正则表达式。例如,
^/clientrequest($|\\W+)
与/clientrequest
和/clientrequest
匹配,后跟一个非单词字符以及一个或多个其他字符,例如/clientrequest?foo=bar
。非单词字符表达式\\W
需要额外的反斜杠来转义 JSON。不支持使用 MATLAB Production Server RESTful API 执行异步请求。请求执行是同步的。有关 MATLAB Production Server RESTful API 的更多信息,请参阅 用于 MATLAB 函数执行的 RESTful API。
在
webhandler
字段中,使用component
字段指定可部署存档的名称,使用function
字段指定请求 URL 要执行的部署函数的名称。
配置存档特定的路由
自 R2023b 起
使用特定于存档的路由,您可以按可部署存档名称组织路由,并避免在实例级路由 JSON 文件中指定所有路由时可能发生的潜在路由匹配冲突。
使用特定于存档的路由需要 MATLAB Production Server R2023b 或更高版本以及 MATLAB Runtime R2023b 或更高版本。
注意
如果客户端请求 URL 与
routes-file
配置属性指定的实例级路由 JSON 文件和存档特定的路由文件中的路由匹配,则实例级路由优先。如果您定义特定于存档的路由,那么您的服务器实例必须包含实例级路由 JSON 文件,即使您未在此文件中指定任何路由。请勿删除此文件或注释
routes-file
配置属性。
档案特定路由的路由 JSON 文件遵循此模式。
{ "version": "1.0.0", "pathmap": [ { "match": "<regular_expression>", "webhandler": { "function": "<name_of_deployed_function>" } }, { "match": "<regular_expression>", "webhandler": { "function": "<name_of_deployed_function>" } } ] } |
此模式与为实例级路由定义的模式相同,只是用于指定可部署存档的 component
字段是可选的。如果指定,该字段仅接受空字符串(""
)或可部署存档的名称作为输入,不带 .ctf
扩展名。
例如,对于打包到存档文件 myarchive.ctf
中的路由,这些 webhandler
规范是有效且等效的。
"webhandler": { "function": "myfunction", "component": "myarchive" }
"webhandler": { "function": "myfunction", "component": "" }
"webhandler": { "function": "myfunction" }
使用 component
字段将实例级路由 JSON 文件中定义的路由转移到存档特定的路由文件中,而无需修改 pathmap
对象。
档案特定路由遵循与实例级路由类似的匹配规则。但是,匹配从第二个路径分隔符开始,并且只有当客户端请求 URL 包含存档名称(后跟斜杠)时才可能匹配。
http://host:port/archive_name/
要将实例级路由 JSON 文件打包到可部署存档中,请使用 compiler.build.productionServerArchive
(MATLAB Compiler SDK) 的 RoutesFile
选项。
compiler.build.productionServerArchive({mfilename1 mfilename2 ... mfilenameN}, ... ArchiveName='archive_name', ... RoutesFile='ArchiveRoutes.json')
mcc
(MATLAB Compiler) 函数的 ROUTES
语法。 mcc -U -W 'CTF:archive_name,ROUTES:ArchiveRoutes.json' mfilename1 mfilename2 ... mfilenameN
- 您想要创建的可部署存档的名称。示例:archive_name
myarchive
。
- 包含存档特定路由的 JSON 文件的绝对或相对路径。示例:ArchiveRoutes.json
myarchive_routes.json
。
- 您想要打包到可部署存档的 MATLAB 函数文件,以空格分隔。示例:mfilename1 mfilename2 ... mfilenameN
myfunction1.m myfunction2.m
。
使用实例级路由部署 Web 请求处理程序
自 R2022a 起
此示例显示如何部署 Web 请求处理程序,其中定义的 URL 路由映射适用于部署在服务器实例上的所有存档。
前提条件
您有一个在默认主机和端口上运行的服务器实例:
localhost:9910
。有关启动服务器的信息,请参阅 使用命令行启动服务器实例。您的服务器实例使用默认的实例级路由 JSON 文件,如
routes-file
配置属性所指定。默认路由 JSON 文件位于$MPS_INSTALL/config/route.json
。您的服务器实例使用默认的自动部署文件夹,如
auto-deploy-root
配置属性所指定的。默认自动部署文件夹位于$MPS_INSTALL/auto_deploy
。
编写 Web 处理程序函数
编写三个 MATLAB 函数作为 Web 请求处理程序。这些函数使用输入参量结构体 request
,其字段提供有关请求标头和正文的信息。每个函数还构建并返回一个结构体 response
,该结构体具有包含成功 HTTP 代码、状态消息、自定义标头和消息正文的字段。
此函数返回响应正文中的文本
"Hello,
,其中name
"
是请求 URL 的查询参数中提供的值。例如,name
?name=MathWorks
。将此函数代码保存到名为helloNameHandler.m
的文件中。function response = helloNameHandler(request) params = extractAfter(request.Path,"?"); name = extractBetween(params,"name=",regexpPattern("($|&)")); data = char(sprintf("Hello, %s", name{1})); response = struct( ... 'ApiVersion',[1 0 0], ... 'HttpCode',200, ... 'HttpMessage','OK', ... 'Headers', {{'Server' 'WebFunctionTest/1'; ... 'X-MyHeader' 'foobar'; ... 'X-Request-Body-Len' sprintf('%d', length(request.Body)); ... 'Content-Type' 'text/plain';}}, ... 'Body', unicode2native(data,'ISO-8859-1')); end
该函数显示文本
"Hello, World"
。将此函数代码保存到名为helloWorldHandler.m
的文件中。function response = helloWorldHandler(request) data = 'Hello, World'; response = struct( ... 'ApiVersion',[1 0 0], ... 'HttpCode',200, ... 'HttpMessage','OK', ... 'Headers', {{'Server' 'WebFunctionTest/1'; ... 'X-MyHeader' 'foobar'; ... 'X-Request-Body-Len' sprintf('%d', length(request.Body)); ... 'Content-Type' 'text/plain';}}, ... 'Body', unicode2native(data,'ISO-8859-1')); end
该函数充当默认处理程序并提供有关其他路由的信息。将此函数代码保存到名为
defaultHandler.m
的文件中。function response = defaultHandler(request) data = 'Use routes /hello and /hello?name=<yourname>.'; response = struct( ... 'ApiVersion',[1 0 0], ... 'HttpCode',200, ... 'HttpMessage','OK', ... 'Headers', {{'Server' 'WebFunctionTest/1'; ... 'X-MyHeader' 'foobar'; ... 'X-Request-Body-Len' sprintf('%d', length(request.Body)); ... 'Content-Type' 'text/plain';}}, ... 'Body', unicode2native(data,'ISO-8859-1')); end
封装 Web 处理程序函数
使用 compiler.build.productionServerCompiler
或 mcc
命令将这三个函数打包成可部署的存档。有关创建可部署存档的其他方法,请参阅针对 MATLAB Production Server 创建可部署存档。
将
helloNameHandler.m
和helloWorldHandler.m
打包成名为hello.ctf
的可部署存档。compiler.build.productionServerArchive({'helloNameHandler.m','helloWorldHandler.m'},... 'ArchiveName','hello');
mcc -U -W 'CTF:hello' helloNameHandler.m helloWorldHandler.m
将
defaultHandler.m
打包成名为default.ctf
的可部署存档。compiler.build.productionServerArchive({'defaultHandler.m'},... 'ArchiveName','default');
mcc -U -W 'CTF:default' defaultHandler.m
部署 Web 处理程序函数
将 hello.ctf
和 default.ctf
存档复制到 $MPS_INSTALL/auto_deploy
文件夹,以将其部署到您的服务器实例。有关将存档部署到服务器实例的其他方法,请参阅 将存档部署至 MATLAB Production Server。
配置实例级路由
在 $MPS_INSTALL/config
文件夹中,更新 routes.json
文件以将客户端请求映射到已部署的函数。用以下内容替换现有的 JSON。
{ "version": "1.0.0", "pathmap": [ { "match": "^/hello\\?name=(\\w+|$)", "webhandler": { "component": "hello", "function": "helloNameHandler" } }, { "match": "^/hello$", "webhandler": { "component": "hello", "function": "helloWorldHandler" } }, { "match": "^/", "webhandler": { "component": "default", "function": "defaultHandler" } } ] } |
"^/hello\\?name=(\\w+|$)"
路由将"localhost:9910/hello?name=
格式的请求 URL 映射到name
"helloNameHandler
函数。"^/hello$"
路由将"localhost:9910/hello"
格式的请求 URL 映射到helloWorldHandler
函数。"^/"
路由将包含斜杠的请求 URL(例如"localhost:9910/"
、"localhost:9910/goodbye"
和"localhost:9910/a/b/c"
)映射到defaultHandler
函数。
重新启动服务器实例以使更改生效。请参阅 mps-restart
。
将客户端请求发送到 Web 处理程序函数
使用您选择的客户端向已部署的函数发送 HTTP 请求,例如 cURL
或 Web 浏览器。
以下命令使用 cURL
从系统命令行调用已部署的函数之一。输出包括有关响应的信息,包括正文和自定义标题。
curl -v http://localhost:9910/hello?name=MathWorks
* Trying 127.0.0.1:9910... * Connected to localhost (127.0.0.1) port 9910 (#0) > GET /hello?name=MathWorks HTTP/1.1 > Host: localhost:9910 > User-Agent: curl/8.0.1 > Accept: */* > < HTTP/1.1 200 OK < Server: WebFunctionTest/1 < X-MyHeader: foobar < X-Request-Body-Len: 0 < Content-Type: text/plain < Content-Length: 16 < Connection: Keep-Alive < Hello, MathWorks* Connection #0 to host localhost left intact
下表显示了您可以尝试的其他示例请求 URL 以及它们匹配的路由。
请求 URL | 匹配行为 |
---|---|
http://localhost:9910/ | 匹配 "^/" 路由条目。 |
http://localhost:9910/hello | 匹配 "^/hello" 路由条目。 |
http://localhost:9910/hello2 | 匹配 "^/" 路由条目而不是 "^/hello$" ,因为请求 URL 不是以字符串 "hello" 结尾。 |
http://localhost:9910/hello?name=MathWorks | 匹配 "^/hello\\?name=(\\w+|$)" 路由条目。 |
http://localhost:9910/hello?foo=bar&name=MathWorks | 匹配 "^/" 路由条目而不是 "^/hello\\?name=(\\w+|$)" ,因为请求 URL 只能指定 name 查询参数。 |
使用存档专用路由部署 Web 请求处理程序
自 R2023b 起
此示例显示如何部署 Web 请求处理程序,其中 URL 路由映射仅适用于服务器上部署的特定存档。
前提条件
您有一个在默认主机和端口上运行的服务器实例:
localhost:9910
。有关启动服务器的信息,请参阅 使用命令行启动服务器实例。您的服务器实例使用默认的实例级路由 JSON 文件,如
routes-file
配置属性所指定。默认路由 JSON 文件位于$MPS_INSTALL/config/route.json
。您的服务器实例使用默认的自动部署文件夹,如
auto-deploy-root
配置属性所指定的。默认自动部署文件夹位于$MPS_INSTALL/auto_deploy
。
此外,要运行此示例,您必须使用 MATLAB Production Server R2023b 或更高版本以及 MATLAB Runtime R2023b 或更高版本。
编写 Web 处理程序函数
编写两个 MATLAB 函数作为 Web 请求处理程序。这些函数使用输入参量结构体 request
,其字段提供有关请求标头和正文的信息。每个函数还构建并返回一个结构体 response
,该结构体具有包含成功 HTTP 代码、状态消息、自定义标头和消息正文的字段。
该函数显示文本
"Hello,
,其中name
"
是请求 URL 的查询参数中提供的值(例如name
?name=MathWorks
)。如果请求不包含查询参数,则会显示"Hello"
。将此函数代码保存到名为helloHandler.m
的文件中。function response = helloHandler(request) params = extractAfter(request.Path,"?"); name = extractBetween(params,"name=",regexpPattern("($|&)")); if isempty(name) || strcmp(name, "") data = 'Hello'; else data = char(sprintf("Hello, %s", name{1})); end response = struct( ... 'ApiVersion',[1 0 0], ... 'HttpCode',200, ... 'HttpMessage','OK', ... 'Headers', {{'Server' 'WebFunctionTest/1'; ... 'X-MyHeader' 'foobar'; ... 'X-Request-Body-Len' sprintf('%d', length(request.Body)); ... 'Content-Type' 'text/plain';}}, ... 'Body', unicode2native(data,'ISO-8859-1')); end
此函数与上一个函数类似,但显示
"Goodbye,
或name
""Goodbye"
。将此函数代码保存到名为goodbyeHandler.m
的文件中。function response = goodbyeHandler(request) params = extractAfter(request.Path,"?"); name = extractBetween(params,"name=",regexpPattern("($|&)")); if isempty(name) || strcmp(name, "") data = 'Goodbye'; else data = char(sprintf("Goodbye, %s", name{1})); end response = struct( ... 'ApiVersion',[1 0 0], ... 'HttpCode',200, ... 'HttpMessage','OK', ... 'Headers', {{'Server' 'WebFunctionTest/1'; ... 'X-MyHeader' 'foobar'; ... 'X-Request-Body-Len' sprintf('%d', length(request.Body)); ... 'Content-Type' 'text/plain';}}, ... 'Body', unicode2native(data,'ISO-8859-1')); end
打包并部署 Web 处理程序函数
将 helloHandler
和 goodbyeHandler
函数打包到同一个存档中并部署到服务器。在打包的存档中包含一个 JSON 文件,其中包含指向这些函数的 URL 路由。
创建一个特定于存档的 JSON 文件来定义这两个函数的路由。将以下 JSON 保存到名为
greet.json
的文件中。{ "version": "1.0.0", "pathmap": [ { "match": "^/hello$", "webhandler": { "function": "helloHandler" } }, { "match": "^/hello\\?name=(\\w+|$)", "webhandler": { "function": "helloHandler" } }, { "match": "^/goodbye$", "webhandler": { "function": "goodbyeHandler" } }, { "match": "^/goodbye\\?name=(\\w+|$)", "webhandler": { "function": "goodbyeHandler" } } ] }
前两个路由将格式为
"localhost:9910/
” 或archive_name
/hello"localhost:9910/
的请求 URL 映射到archive_name
/hello?name=name
"helloHandler
函数。最后两个路由将格式为
"localhost:9910/
” 或archive_name
/goodbye"localhost:9910/
的请求 URL 映射到archive_name
/goodbye?name=name
"goodbyeHandler
函数。
是可部署存档的名称,您在下一步中创建它。archive_name
将
helloHandler.m
和goodbyeHandler.m
打包成名为greet.ctf
的可部署存档。使用compiler.build.productionServerArchive
函数的RoutesFile
或mcc
命令的ROUTES
语法将hello.json
路由文件打包到存档中。compiler.build.productionServerArchive({'helloHandler.m', 'goodbyeHandler.m'}, ... ArchiveName='greet', ... RoutesFile='greet.json')
mcc -U -W 'CTF:greet,ROUTES:greet.json' helloHandler.m goodbyeHandler.m
通过将
greet.ctf
存档复制到服务器实例的$MPS_INSTALL/auto_deploy
文件夹中,将其部署到服务器实例。您不需要重新启动服务器以使更改生效。
将客户端请求发送到 Web 处理程序函数
使用您选择的客户端(例如 cURL
或 Web 浏览器)向已部署的函数发送 HTTP 请求。
以下命令使用 cURL
从系统命令行调用已部署的函数之一。输出包括有关响应的信息,包括正文和自定义标题。
curl -v http://localhost:9910/greet/hello?name=MathWorks
* Trying 127.0.0.1:9910... * Connected to localhost (127.0.0.1) port 9910 (#0) > GET /hello?name=MathWorks HTTP/1.1 > Host: localhost:9910 > User-Agent: curl/8.0.1 > Accept: */* > < HTTP/1.1 200 OK < Server: WebFunctionTest/1 < X-MyHeader: foobar < X-Request-Body-Len: 0 < Content-Type: text/plain < Content-Length: 16 < Connection: Keep-Alive < Hello, MathWorks* Connection #0 to host localhost left intact
下表显示了您可以尝试的其他示例请求 URL 以及它们匹配的路由。
请求 URL | 匹配行为 |
---|---|
http://localhost:9910/greet/hello | 匹配 "^/hello$" 路由条目。 |
http://localhost:9910/greet/hello?name=MATLAB | 匹配 "^/hello\\?name=(\\w+|$)" 路由条目。 |
http://localhost:9910/greet/goodbye | 匹配 "^/goodbye$" 路由条目。 |
http://localhost:9910/greet/goodbye?name=MATLAB | 匹配 "^/goodbye\\?name=(\\w+|$)" 路由条目。 |
http://localhost:9910/hello | 与任何路由条目不匹配,因为请求 URL 不包含含有存档名称的路径段。 |
另请参阅
主题
- 测试 Web 请求处理程序 (MATLAB Compiler SDK)
- 针对 MATLAB Production Server 创建可部署存档
- 将存档部署至 MATLAB Production Server