Handle Custom Routes and Payloads in HTTP Requests
In MATLAB® Production Server™, you can use deployed MATLAB functions as web request handlers. These handlers allow customization of requests and route mapping:
Web request handlers for MATLAB Production Server provide flexible client-server communication.
Client programmers can send custom HTTP headers and payloads in RESTful requests to the server.
Server administrators can provide flexible mapping of the request URLs to deployed MATLAB functions.
Server administrators can provide static file serving.
MATLAB programmers can return custom HTTP headers, HTTP status codes, HTTP status messages, and payloads in functions deployed to MATLAB Production Server.
To create a web request handler, you write a MATLAB function that accepts and returns request data, then deploy it to the server. Then, you specify custom URL routes to that function in a JSON file on the server, in a JSON file packaged into the deployed archive that contains that function, or in both files.
Write MATLAB Functions for Web Request Handler
To work as web request handlers, the MATLAB functions that you deploy to the server must accept one scalar structure array as input and return one scalar structure array as output.
The structure in the function input argument provides information about the client
request. In their requests, clients can send custom HTTP headers and custom payloads.
There are no data format restrictions on the payload that the deployed function can
accept. For example, the function can accept raw data in binary or ASCII formats, CSV
data, or JSON data that is not in the schema specified by the MATLAB
Production Server RESTful API. Clients can also use the Transfer-Encoding:
chunked
header to send data in chunks. In chunked transfer encoding,
though the server receives the payload in chunks, the input structure receives payload
data in its entirety.
The structure in the function input argument contains the following fields.
Field Name | Data Type | Dimensions | Description |
---|---|---|---|
ApiVersion | double | 1 x 3 | Version of the input structure schema in the format
<major> <minor> <fix> |
Body | uint8 | 1 x N | Request payload |
Headers | cell | N x 2 | HTTP request headers Each element in the cell array
represents a header. Each element is a key-value pair, where the key
is of type |
HttpVersion | double | 1 x 2 | HTTP version in the format <major>
<minor> |
Method | char | 1 x N | HTTP request method |
Path | char | 1 x N | Path of request URL |
Since the deployed MATLAB function can accept custom headers and payloads in RESTful requests, you
can vary the behavior of the MATLAB function depending on the request header data. You can use the structure
in the function output argument to return a response with custom HTTP headers and
payload. Server processing errors, if any, override any custom HTTP headers that you
might set. If a MATLAB error occurs, the server returns an HTTP 500 Internal Server
Error
response. All fields in the structure are optional.
The structure in the output argument can contain the following fields:
Field Name | Data Type | Dimensions | Description |
---|---|---|---|
ApiVersion | double | 1 x 3 | Version of the output structure schema in the format
<major> <minor> <fix> |
Body | uint8 | 1 x N | Response payload |
Headers | cell | N x 2 | HTTP response headers Each element in the cell
array represents a header. Each element is a key-value pair, where
the key is of type |
HttpCode | double | 1 x 1 | HTTP status code |
HttpMessage | char | 1 x N | HTTP status message |
Configure URL Routes
Custom URL routes allow the server to map the path in request URLs to any deployable archive and MATLAB function deployed on the server. You can define URL routes at the server instance level or at the deployable archive level.
Instance-Level Routes — Define URL routes in a single JSON file located on the server instance. These routes map request URLs to web handler functions across all archives deployed on the server. (since R2022a)
Archive-Specific Routes — Define URL routes for an individual archive within its own JSON file and package the file into the archive. These routes map request URLs only to web handler functions defined within that archive on the server. You can use archive-specific routes to better organize routes and avoid route matching conflicts. (since R2023b)
You can define a combination of these routes, and both types of routes are optional.
Configure Instance-Level Routes
Since R2022a
At the server instance level, to specify the mapping of a request URL to a
deployed MATLAB function, you can use a JSON file present on the server. The default
name of the file is routes.json
and its default location is in
the
folder. You
can change the file name and its location by changing the value of the $MPS_INSTALL
/configroutes-file
property in the main_config
server
configuration file. You must restart the server after making any updates to
routes.json
.
When the server starts, it tries to read the routes.json
file.
If the file does not exist or contains errors, the server does not start and writes
an error message to the main.log
file present in the folder that
the log-root
property
specifies.
The default routes.json
file contains a
version
field with a value of 1.0.0
, and
an empty pathmap
field. version
specifies the
schema version of the file. You do not need to change its value. To allow custom
routes, edit the file to specify mapping rules in the pathmap
array. Each object in the array corresponds to a URL route.
The instance-level routes JSON file follows this schema.
{ "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>" } } ] } |
To specify a URL mapping rule, use the match
and
webhandler
fields from the pathmap
array.
The client request URL matches a route only if it includes a slash after the hostname and port, as in
http://
.host
:port
/In the
match
field, specify a regular expression that uses ECMAScript grammar to match the path in a request URL.If the request URL matches multiple regular expressions in the
match
field, the first match starting from the beginning of the file is selected. Therefore, specify pattern matches in order from more specific to more general.The regular expression patterns are considered a match if any substring of the request URL is a match. For example, the pattern
a/b
matchesa/b
,/a/b
, and/x/a/b/y
. However, you can use the regular expression anchors^
and$
to match positions before or after specific characters. For example, the pattern^a/b$
only matchesa/b
.You can specify regular expressions that match query parameters in the request URL. For example,
^/clientrequest($|\\W+)
matches both/clientrequest
and/clientrequest
followed by a non-word character and one or more additional characters, such as/clientrequest?foo=bar
. The non-word character expression\\W
requires an extra backslash to escape the JSON.Asynchronous request execution using the MATLAB Production Server RESTful API is not supported. Request execution is synchronous. For more information about the MATLAB Production Server RESTful API, see RESTful API for MATLAB Function Execution.
In the
webhandler
field, use thecomponent
field to specify the name of the deployable archive and thefunction
field to specify the name of the deployed function for the request URL to execute.
Configure Archive-Specific Routes
Since R2023b
Using archive-specific routes, you can organize routes by deployable archive name and avoid potential route matching conflicts that can occur when specifying all routes in the instance-level routes JSON file.
Using archive-specific routes requires MATLAB Production Server R2023b or later and MATLAB Runtime R2023b or later.
Note
If a client request URL matches a route in both the instance-level routes JSON file specified by the
routes-file
configuration property and in an archive-specific routes file, the instance-level route takes precedence.If you define archive-specific routes, then your server instance must include an instance-level routes JSON file, even if you do not specify any routes in this file. Do not delete this file or comment out the
routes-file
configuration property.
The routes JSON file for archive-specific routes follows this schema.
{ "version": "1.0.0", "pathmap": [ { "match": "<regular_expression>", "webhandler": { "function": "<name_of_deployed_function>" } }, { "match": "<regular_expression>", "webhandler": { "function": "<name_of_deployed_function>" } } ] } |
This schema is identical to the one defined for instance-level routes, except that
the component
field for specifying the deployable archive is
optional. If specified, this field accepts as input only an empty string
(""
) or the name of the deployable archive, without the
.ctf
extension.
For example, for routes packaged into archive file
myarchive.ctf
, these webhandler
specifications are valid and equivalent.
"webhandler": { "function": "myfunction", "component": "myarchive" }
"webhandler": { "function": "myfunction", "component": "" }
"webhandler": { "function": "myfunction" }
Use the component
field to transfer routes defined
in the instance-level routes JSON file into the archive-specific routes file without
having to modify the pathmap
object.
Archive-specific routes follow similar matching rules as instance-level routes. However, matching starts at the second path delimiter and potentially matches only if the client request URL includes the archive name, followed by a slash.
http://host:port/archive_name/
To package an instance-level routes JSON file into a deployable archive, use the
RoutesFile
option for compiler.build.productionServerArchive
(MATLAB Compiler SDK).
compiler.build.productionServerArchive({mfilename1 mfilename2 ... mfilenameN}, ... ArchiveName='archive_name', ... RoutesFile='ArchiveRoutes.json')
ROUTES
syntax of the mcc
(MATLAB Compiler) function.
mcc -U -W 'CTF:archive_name,ROUTES:ArchiveRoutes.json' mfilename1 mfilename2 ... mfilenameN
— Name of the deployable archive you want to create. Example:archive_name
myarchive
.
— Absolute or relative path to the JSON file containing archive-specific routes. Example:ArchiveRoutes.json
myarchive_routes.json
.
— MATLAB function files, separated by spaces, that you want to package into the deployable archive. Example:mfilename1 mfilename2 ... mfilenameN
myfunction1.m myfunction2.m
.
Deploy Web Request Handlers with Instance-Level Routes
Since R2022a
This example shows how to deploy web request handlers in which the defined URL route mappings apply across all archives deployed on the server instance.
Prerequisites
You have a server instance running at the default host and port:
localhost:9910
. For information on starting a server, see Start Server Instance Using Command Line.Your server instance uses the default instance-level route JSON file, as specified by the
routes-file
configuration property. The default routes JSON file is located at$MPS_INSTALL/config/route.json
.Your server instance uses the default auto-deployment folder, as specified by the
auto-deploy-root
configuration property. The default auto-deploy folder is located at$MPS_INSTALL/auto_deploy
.
Write Web Handler Functions
Write three MATLAB functions that act as web request handlers. These functions use the
input argument structure request
, whose fields provide
information about the request headers and body. Each function also constructs and
returns a structure response
that has fields containing a success
HTTP code, a status message, custom headers, and a message body.
This function returns the text
"Hello,
in the response body, wherename
"
is a value provided in the query parameter of a request URL. For example,name
?name=MathWorks
. Save this function code to a file namedhelloNameHandler.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
This function displays the text
"Hello, World"
. Save this function code to a file namedhelloWorldHandler.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
This function acts as a default handler and provides information about the other routes. Save this function code to a file named
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
Package Web Handler Functions
Package the three functions into deployable archives by using
compiler.build.productionServerCompiler
or the
mcc
command. For other ways to create deployable archives,
see Create Deployable Archive for MATLAB Production Server.
Package
helloNameHandler.m
andhelloWorldHandler.m
into a deployable archive namedhello.ctf
.compiler.build.productionServerArchive({'helloNameHandler.m','helloWorldHandler.m'},... 'ArchiveName','hello');
mcc -U -W 'CTF:hello' helloNameHandler.m helloWorldHandler.m
Package
defaultHandler.m
into a deployable archive nameddefault.ctf
.compiler.build.productionServerArchive({'defaultHandler.m'},... 'ArchiveName','default');
mcc -U -W 'CTF:default' defaultHandler.m
Deploy Web Handler Functions
Deploy the hello.ctf
and default.ctf
archives to your server instance by copying them into the
$MPS_INSTALL/auto_deploy
folder. For additional ways to
deploy archives to a server instance, see Deploy Archive to MATLAB Production Server.
Configure Instance-Level Routes
In the $MPS_INSTALL/config
folder, update the
routes.json
file to map client requests to the deployed
functions. Replace the existing JSON with the following.
{ "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" } } ] } |
The
"^/hello\\?name=(\\w+|$)"
route maps request URLs of the format"localhost:9910/hello?name=
to thename
"helloNameHandler
function.The
"^/hello$"
route maps request URLs of the format"localhost:9910/hello"
to thehelloWorldHandler
function.The
"^/"
route maps request URLs that contain a slash (for example,"localhost:9910/"
,"localhost:9910/goodbye"
and"localhost:9910/a/b/c"
) to thedefaultHandler
function.
Restart the server instance for the changes to take effect. See mps-restart
.
Send Client Requests to Web Handler Functions
Use a client of your choice to send HTTP requests to the deployed functions, such
as cURL
or a web browser.
The following command uses cURL
to call one of the deployed
functions from the system command line. The output includes information about the
response, including the body text and the custom headers.
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
This table shows additional sample request URLs you can try and which routes they match.
Request URL | Matching Behavior |
---|---|
http://localhost:9910/ | Matches the "^/" route entry. |
http://localhost:9910/hello | Matches the "^/hello" route entry. |
http://localhost:9910/hello2 | Matches the "^/" route entry and not
"^/hello$" because the request URL does not
end with the string "hello" . |
http://localhost:9910/hello?name=MathWorks | Matches the "^/hello\\?name=(\\w+|$)" route
entry. |
http://localhost:9910/hello?foo=bar&name=MathWorks | Matches the "^/" route entry and not
"^/hello\\?name=(\\w+|$)" because the request
URL can specify the name query parameter
only. |
Deploy Web Request Handlers with Archive-Specific Routes
Since R2023b
This example shows how to deploy web request handlers in which the URL route mappings apply only to a specific archive deployed on the server.
Prerequisites
You have a server instance running at the default host and port:
localhost:9910
. For information on starting a server, see Start Server Instance Using Command Line.Your server instance uses the default instance-level route JSON file, as specified by the
routes-file
configuration property. The default routes JSON file is located at$MPS_INSTALL/config/route.json
.Your server instance uses the default auto-deployment folder, as specified by the
auto-deploy-root
configuration property. The default auto-deploy folder is located at$MPS_INSTALL/auto_deploy
.
In addition, to run this example, you must use MATLAB Production Server R2023b or later and MATLAB Runtime R2023b or later.
Write Web Handler Functions
Write two MATLAB functions that act as web request handlers. These functions use an
input argument structure request
, whose fields provide
information about the request headers and body. Each function also constructs and
returns a structure response
that has fields containing a success
HTTP code, a status message, custom headers, and a message body.
This function displays the text
"Hello,
, wherename
"
is a value provided in the query parameter of a request URL (for example,name
?name=MathWorks
). If the request does not contain a query parameter, it displays"Hello"
instead. Save this function code to a file namedhelloHandler.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
This function is similar to the previous function but displays
"Goodbye,
orname
""Goodbye"
instead. Save this function code to a file namedgoodbyeHandler.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
Package and Deploy Web Handler Functions
Package the helloHandler
and goodbyeHandler
functions into the same archive and deploy it to the server. Include in the packaged
archive a JSON file containing the URL routes to these functions.
Create an archive-specific JSON file to define the routes to the two functions. Save the following JSON to a file named
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" } } ] }
The first two routes map request URLs of the format
"localhost:9910/
" orarchive_name
/hello"localhost:9910/
to thearchive_name
/hello?name=name
"helloHandler
function.The last two routes map request URLs of the format
"localhost:9910/
" orarchive_name
/goodbye"localhost:9910/
to thearchive_name
/goodbye?name=name
"goodbyeHandler
function.
is the name of the deployable archive, which you create in the next step.archive_name
Package
helloHandler.m
andgoodbyeHandler.m
into a deployable archive namedgreet.ctf
. Use theRoutesFile
of thecompiler.build.productionServerArchive
function or theROUTES
syntax of themcc
command to package thehello.json
routes file into the archive.compiler.build.productionServerArchive({'helloHandler.m', 'goodbyeHandler.m'}, ... ArchiveName='greet', ... RoutesFile='greet.json')
mcc -U -W 'CTF:greet,ROUTES:greet.json' helloHandler.m goodbyeHandler.m
Deploy the
greet.ctf
archive to your server instance by copying it into the$MPS_INSTALL/auto_deploy
folder of your server instance. You do not need to restart the server for your changes to take effect.
Send Client Requests to Web Handler Functions
Send HTTP requests to the deployed functions using a client of your choice, such
as cURL
or a web browser.
The following command uses cURL
to call one of the deployed
functions from the system command line. The output includes information about the
response, including the body text and the custom headers.
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
This table shows additional sample request URLs you can try and which routes they match.
Request URL | Matching Behavior |
---|---|
http://localhost:9910/greet/hello | Matches the "^/hello$" route entry. |
http://localhost:9910/greet/hello?name=MATLAB | Matches the "^/hello\\?name=(\\w+|$)" route
entry. |
http://localhost:9910/greet/goodbye | Matches the "^/goodbye$" route entry. |
http://localhost:9910/greet/goodbye?name=MATLAB | Matches the "^/goodbye\\?name=(\\w+|$)" route
entry. |
http://localhost:9910/hello | Does not match any route entries because the request URL does not include the path segment containing the archive name. |