主要内容

本页采用了机器翻译。点击此处可查看最新英文版本。

处理 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 标头分块发送数据。在分块传输编码中,虽然服务器以块的形式接收有效负载,但输入结构体会接收整个有效负载数据。

函数输入参量中的结构体包含以下字段。

字段名称数据类型维度描述
ApiVersiondouble1 x 3输入结构体模式的版本,格式为 <major> <minor> <fix>
Bodyuint81 x N请求负载
HeaderscellN x 2

HTTP 请求标头

元胞数组中的每个元素代表一个标题。每个元素都是一个键值对,其中键的类型为 char,值可以是 chardouble 类型。

HttpVersiondouble1 x 2HTTP 版本格式为 <major> <minor>
Methodchar1 x NHTTP 请求方法
Pathchar1 x N请求 URL 路径

由于部署的 MATLAB 函数可以接受 RESTful 请求中的自定义标头和有效负载,因此您可以根据请求标头数据改变 MATLAB 函数的行为。您可以使用函数输出参量中的结构体来返回带有自定义 HTTP 标头和有效负载的响应。如果有服务器处理错误,则会覆盖您可能设置的任何自定义 HTTP 标头。如果发生 MATLAB 错误,服务器将返回 HTTP 500 Internal Server Error 响应。结构体中的所有字段都是可选的。

输出参量中的结构体可以包含以下字段:

字段名称数据类型维度描述
ApiVersiondouble1 x 3输出结构体模式的版本,格式为 <major> <minor> <fix>
Bodyuint81 x N响应负载
HeaderscellN x 2

HTTP 响应标头

元胞数组中的每个元素代表一个标题。每个元素都是一个键值对,其中键的类型为 char,值可以是 chardouble 类型。

HttpCodedouble1 x 1HTTP 状态代码
HttpMessagechar1 x NHTTP 状态消息

配置 URL 路由

自定义 URL 路由允许服务器将请求 URL 中的路径映射到服务器上部署的任何可部署存档和 MATLAB 函数。您可以在服务器实例级别或可部署存档级别定义 URL 路由。

  • 实例级路由 - 在位于服务器实例上的单个 JSON 文件中定义 URL 路由。这些路由将请求 URL 映射到服务器上部署的所有存档中的 Web 处理程序函数。 (自 R2022a 起)

  • 存档特定路由 - 在其自己的 JSON 文件中为单个存档定义 URL 路由,并将文件打包到存档中。这些路由仅将请求 URL 映射到服务器上该存档中定义的 Web 处理程序函数。您可以使用特定于存档的路由来更好地组织路由并避免路由匹配冲突。 (自 R2023b 起)

您可以定义这些路由的组合,并且两种类型的路由都是可选的。

配置实例级路由

自 R2022a 起

在服务器实例级别,要指定请求 URL 到已部署的 MATLAB 函数的映射,您可以使用服务器上存在的 JSON 文件。该文件的默认名称为 routes.json,默认位置在 $MPS_INSTALL/config 文件夹中。您可以通过更改 main_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 数组中的 matchwebhandler 字段。

  • 客户端请求 URL 仅当在主机名和端口后包含斜杠时才匹配路由,如 http://host:port/

  • match 字段中,指定使用 ECMAScript 语法的正则表达式来匹配请求 URL 中的路径。

    • 如果请求 URL 与 match 字段中的多个正则表达式匹配,则选择从文件开头开始的第一个匹配项。因此,请按照从更具体到更一般的顺序指定模式匹配。

    • 如果请求 URL 的任何子字符串匹配,则正则表达式模式被视为匹配。例如,模式 a/ba/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
两种工作流都需要 MATLAB Compiler SDK™

  • archive_name - 您想要创建的可部署存档的名称。示例:myarchive

  • ArchiveRoutes.json - 包含存档特定路由的 JSON 文件的绝对或相对路径。示例:myarchive_routes.json

  • mfilename1 mfilename2 ... mfilenameN - 您想要打包到可部署存档的 MATLAB 函数文件,以空格分隔。示例: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",其中 name 是请求 URL 的查询参数中提供的值。例如,?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.productionServerCompilermcc 命令将这三个函数打包成可部署的存档。有关创建可部署存档的其他方法,请参阅针对 MATLAB Production Server 创建可部署存档

  • helloNameHandler.mhelloWorldHandler.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.ctfdefault.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=name" 格式的请求 URL 映射到 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",其中 name 是请求 URL 的查询参数中提供的值(例如 ?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 处理程序函数

helloHandlergoodbyeHandler 函数打包到同一个存档中并部署到服务器。在打包的存档中包含一个 JSON 文件,其中包含指向这些函数的 URL 路由。

  1. 创建一个特定于存档的 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/archive_name/hello?name=name" 的请求 URL 映射到 helloHandler 函数。

    • 最后两个路由将格式为 "localhost:9910/archive_name/goodbye” 或 "localhost:9910/archive_name/goodbye?name=name" 的请求 URL 映射到 goodbyeHandler 函数。

    • archive_name 是可部署存档的名称,您在下一步中创建它。

  2. helloHandler.mgoodbyeHandler.m 打包成名为 greet.ctf 的可部署存档。使用 compiler.build.productionServerArchive 函数的 RoutesFilemcc 命令的 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
  3. 通过将 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 不包含含有存档名称的路径段。

另请参阅

主题