Defines all route objects, for example: source
, target
, and, destination
.
Application Router: Routes Properties
Property |
Type |
Mandatory |
Description |
---|---|---|---|
|
RegEx |
Yes |
Describes a regular expression that matches the incoming request URL. A request matches a particular route when its path contains the given pattern. To ensure the RegExp matches the complete path, use the following form:^$`.
|
Array of uppercase HTTP methods |
No |
HTTP methods that are served by this route; the supported methods are:
|
|
|
String |
No |
Defines how the incoming request path is rewritten for the corresponding destination or static resource. |
|
String |
No |
The name of the destination to which the incoming request is forwarded. The destination name can be a static string or a regular expression that defines how to dynamically fetch the destination name from the source property or from the host. For more information about additional destination properties, see Application Routes and Destinations. |
|
String |
No |
The name of the service to which the incoming request is forwarded. |
|
String |
No |
The name of the endpoint within the service to which the incoming request is forwarded. It must only be used in a route containing a service attribute. |
String |
No |
The directory from which application router serves static content (for example, from the application's |
|
|
Boolean |
No |
Defines from which subaccount the destination is retrieved. If |
Object |
No |
An object that contains the configuration for replacing placeholders with values from the environment.
|
|
|
String |
No |
The value can be The default If you use the value The If the value |
|
Boolean |
No |
Toggle whether this route needs CSRF token protection. The default value is “true”. The application router enforces CSRF protection for any HTTP request that changes state on the server side, for example: PUT, POST, or DELETE. |
|
Array/String/Object |
No |
The authorization scope required to access the target path. The scope itself is defined in the application's security descriptor ( |
|
String |
No |
A string representing the value of the Cache-Control header, which is set on the response when serving static resources. By default the Cache-Control header isn’t set. The cacheControl property is only effective when one of the following settings is performed:
|
|
String |
No |
The name of the identity provider you use if it’s provided in the definition of the route. If it isn’t provided, the route is authenticated with the default identity provider.
|
|
Boolean |
No |
Enables dynamic identity provider provisioning. If |
Route order is important. The first matching route will be used. Therefore, it is recommended to sort the routes from the most specific source to the more generic one. For example:
"routes": [ { "source": "^/sap/backend/employees(.*)$", "target": "$1", "destination": "sfsf" } , { "source": "^/sap/backend/(.*)$", "target": "$1", "destination": "erp", } ]
"routes": [ { "source": "^/sap/ui5/1(.*)$", "target": "$1", "destination": "ui5", "scope": "$XSAPPNAME.viewer", "authenticationType": "xsuaa", "csrfProtection": true } ]
The properties
service
,destination
, andlocalDir
are optional. However, at least one of them must be defined.
The httpMethods
option allows you to split the same path across different targets depending on the HTTP method. For example:
"routes": [ { "source": "^/app1/(.*)$", "target": "/before/$1/after", "httpMethods": ["GET", "POST"] } ]
This route only serves GET and POST requests. Any other method (including extension ones) gets a 405 Method Not Allowed response. The same endpoint can be split across multiple destinations depending on the HTTP method of the requests:
"routes": [ { "source": "^/app1/(.*)$", "destination" : "dest-1", "httpMethods": ["GET"] }, { "source": "^/app1/(.*)$", "destination" : "dest-2", "httpMethods": ["DELETE", "POST", "PUT"] } ]
This sample code routes GET requests to the target dest-1
, DELETE, POST and PUT to dest-2
, and any other method receives a 405 Method Not Allowed response. It’s also possible to specify catchAll
routes, namely routes that don’t specify httpMethods
restrictions:
"routes": [ { "source": "^/app1/(.*)$", "destination" : "dest-1", "httpMethods": ["GET"] }, { "source": "^/app1/(.*)$", "destination" : "dest-2" }} ]
In this sample code, GET requests are routed to dest-1
, and all of the rest are routed to dest-2
.
If there’s no route defined for serving static content via localDir
, a default route is created for “resources
” directory as follows:
{ "routes": [ { "source": "^/(.*)$", "localDir": "resources" } ] }
If there is at least one route using
localDir
, the default route isn’t added.
The replace
object configures the placeholder replacement in static text resources.
{ "replace": { "pathSuffixes": ["index.html"], "vars": ["escaped_text", "NOT_ESCAPED"] } }
The replace
keyword requires the following properties:
Replacement Properties for Static Resource URLs
Property |
Type |
Description |
---|---|---|
|
Array |
An array defining the path suffixes that are relative to |
|
Array |
A whitelist with the environment variables that are replaced in the files matching the suffix specified in |
The supported tags for replacing environment variables are: {{ENV_VAR}}
and {{{ENV_VAR}}}
. If such an environment variable is defined, it’s replaced; otherwise, it’s just an empty string.
Any variable that is replaced using two-brackets syntax
{{ENV_VAR}}
is HTML-escaped; the triple brackets syntax{{{ENV_VAR}}}
is used when the replaced values don’t need to be escaped and all values remain unchanged. For example, if the value of the environment variable isab"cd
the result isab"cd
.
If your application descriptor xs-app.json
contains a route like the one illustrated in the following example,
{
"source": "^/get/home(.*)",
"target": "$1",
"localDir": "resources",
"replace": {
"pathSuffixes": ["index.html"],
"vars": ["escaped_text", "NOT_ESCAPED"]
}
}
And your application uses the following index.html
start file:
<html> <head> <title>{{escaped_text}}</title> <script src="{{{NOT_ESCAPED}}}/index.js"/> </head> </html
Then, in the index.html
, {{escaped_text}}
and {{{NOT_ESCAPED}}}
are replaced with the value defined in the environment variables <escaped_text> and <NOT_ESCAPED>.
All
index.html
files are processed; if you want to apply the replacement only to specific files, you must set the path relative tolocalDir
. In addition, all files must comply with the UTF-8 encoding rules.
The content type returned by a request is based on the file extension specified in the route. The application router supports the following file types:
-
.json (application/json)
-
.txt (text/plain)
-
.html (text/html) default
-
.js (application/javascript)
-
.css (test/css)
The following table illustrates some examples of the pathSuffixes
properties:
Examples of the pathSuffixes Property
Example |
Result |
---|---|
|
All files with the extension |
|
For the suffix For the suffix |
|
All files with the name “ |
{ "source": "^/app1/(.*)$", "destination": "app-1" }
Since there is no target property for that route, no path rewriting will take place. If /app1/a/b
is received as a path, then a request to http://localhost:3001/app1/a/b
is sent. The source path is appended to the destination URL.
{ "source": { "path": "^/app1/(.*)$", "matchCase": false }, "destination": "app-1" }
The property
matchCase
must be boolean. It is optional and has a default value oftrue
.
{ "source": "^/app1/(.*)$", "target": "/before/$1/after", "destination": "app-1" }
{ "source": "^/odata/v2/(.*)$", "target": "$1", "service": "com.sap.appbasic.country", "endpoint": "countryservice" }
When a request with path /app1/a/b
is received, the path rewriting is done according to the rules in the target property. The request will be forwarded to http://localhost:3001/before/a/b/after
.
In regular expressions there is the term capturing group. If a part of a regular expression is surrounded with parenthesis, then what has been matched can be accessed using $ + the number of the group (starting from 1). In code sample, $1 is mapped to the (.*) part of the regular expression in the source property.
{ "source": "^/destination/([^/]+)/(.*)$", "target": "$2", "destination": "$1", "authenticationType": "xsuaa" }
If you have another destination configured, use this:
[ { "name" : "myDestination", "url" : "http://localhost:3002" } ]
When a request with the path /destination/myDestination/myTarget
is received, the destination will be replaced with the URL from "myDestination"
, the target will get "myTarget"
and the request will be redirected tohttp://localhost:3002/myTarget
.
You can use a dynamic value (regex) or a static string for
destination
andtarget
values.The Application Router first looks for the destination name in the
mainfest.yaml
file, and if it is not found, it looks for it in the destination service.
For legacy applications that do not support relative URL paths, you need to define your URL in the following way to enable the destination to be extracted from the host:
https://<tenant>-<destination>.<customdomain>/<pathofile>
To enable the application router to determine the destination of the URL host, a DESTINATION_HOST_PATTERN
attribute must be provided as an environment variable. For example, When a request with the path https://myDestination.some-approuter.someDomain.com/app1/myTarget
is received, the following route is used:
{ "source": "^/app1/([^/]+)/", "target": "$1", "destination": "*", "authenticationType": "xsuaa" }
In this example, the target will be extracted from the source and the ‘$1’
value is replaced with "myTarget"
. The destination value is extracted from the host and the "*"
value is replaced with "myDestination"
.
{ "source": "^/web-pages/(.*)$", "target": "$1", "localDir": "my-static-resources" }
If we receive a request with a path /web-pages/welcome-page.html
, the local file at my-static-resources/welcome-page.html
under the working directory will be served.
The capturing group used in the
target
property.
{ "source": "^/web-pages/", "localDir": "my-static-resources", "cacheControl": "public, max-age=1000,must-revalidate" }
{ "source": "^/index.html$", "service": "html5-apps-repo-rt", "authenticationType": "xsuaa", "cacheControl":"public,max-age=1000,must-revalidate" }
This option allows you to split the same path across different targets depending on the HTTP method. For example:
{ "source": "^/app1/(.*)$", "target": "/before/$1/after", "httpMethods": ["GET", "POST"] }
This route only supports GET
and POST
requests. Any other method (including extensions) will receive a 405 Method Not Allowed response. The same endpoint can be split across multiple destinations depending on the HTTP method of the requests:
{ "source": "^/app1/(.*)$", "destination" : "dest-1", "httpMethods": ["GET"] }, { "source": "^/app1/(.*)$", "destination" : "dest-2", "httpMethods": ["DELETE", "POST", "PUT"] }
In the setup above, GET
requests will be routed to "dest-1"
, and all the rest to "dest-2"
.
localDir
andhttpMethods
are not compatible.
An application specific scope uses the following format:
<application-name>.<scope-name>
It is possible to configure what scope the user needs to possess in order to access a specific resource. Those configurations are per route. The user should have at least one of the scopes in order to access the corresponding resource.
{ "source": "^/web-pages/(.*)$", "target": "$1", "scope": ["$XSAPPNAME.viewer", "$XSAPPNAME.reader", "$XSAPPNAME.writer"] }
For convenience if your route only requires one scope, the scope property can be a string instead of an array. The following configuration is also valid:
{ "source": "^/web-pages/(.*)$", "target": "$1", "scope": "$XSAPPNAME.viewer" }
You can configure scopes for different HTTP methods, such as GET
, POST
, PUT
, HEAD
, DELETE
, CONNECT
, TRACE
, PATCH
, andOPTIONS
. If some of the HTTP methods are not explicitly set, the behaviour for them is defined by the default property. In case there is no default property specified and the HTTP method is also not specified, the request is rejected by default.
{ "source": "^/web-pages/(.*)$", "target": "$1", "scope": { "GET": "$XSAPPNAME.viewer", "POST": ["$XSAPPNAME.reader", "$XSAPPNAME.writer"], "default": "$XSAPPNAME.guest" } }
The application router supports the $XSAPPNAME
placeholder. Its value is taken (and then substituted in the routes) from the UAA configuration.
The substitution is case sensitive.
You can use the name of the business application directly instead of using the $XSAPPNAME placeholder:
{ "source": "^/backend/(.*)$", "scope": "my-business-application.viewer" }
You can define several identity providers for different types of users. In this code example, there are two categories: hospital patients and hospital personnel:
-
patientsIDP – use for authenticating patients.
-
hospitalIDP – use for authenticating all hospital personnel (doctors, nurses etc..).
[ { "source": "^/patients/sap/opu/odata/(.*)", "target": "/sap/opu/odata$1", "destination": "backend", "authenticationType": "xsuaa", "identityProvider": "patientsIDP" }, { "source": "^/hospital/sap/opu/odata/(.*)", "target": "/sap/opu/odata$1", "destination": "backend", "authenticationType": "xsuaa", "identityProvider": "hospitalIDP" } ]
A patient who tries to log into the system will be authenticated by patientIDP, and a doctor who tries to log in will be authenticated by hospitalIDP.
If a user logs in as one identity and then wants to perform tasks for another identity type, the user must log out and log back in to the system.
Dynamic provisioning of the subscriber account identity provider is not supported.
Identity provider configuration is only supported in the client side logon redirect flow.
This is an example of a route where the value of identityProvider
is patientsIDP
and where dynamic identity provider provisioning is enabled by setting dynamicIdentityProvider
to true:
[ { "source": "^/patients/index.html", "target": "/patients-index.html", "service": "html5-apps-repo-rt", "identityProvider": "patientsIDP", "dynamicIdentityProvider": true } ]
In this example, the patientsIDP
value for the identityProvider
is replaced by hospitalIDP
if a request with sap_idp=hospitalIDP
is executed, for example, if the request is https://shiva.health-center-approuter.cfapps.hana.ondemand.com/healthreport/patients/index.html?sap_idp=hospitalIDP
.