|
| 1 | +Production web applications are commonly deployed behind HTTP proxies. While |
| 2 | +many modern web services use cloud platforms that abstract away the need to |
| 3 | +manage HTTP proxies, there are still scenarios where managing load balancing |
| 4 | +and proxy rules is necessary. This is especially true when deploying |
| 5 | +applications using platforms like [Kubernetes](https://kubernetes.io/). |
| 6 | + |
| 7 | +Deploying web applications behind proxies often requires configuring routing |
| 8 | +based on the properties of incoming web requests. The most common examples |
| 9 | +include routing based on the HTTP **host** header, the prefix of the **URL |
| 10 | +path**, or a combination of both. |
| 11 | + |
| 12 | +This page provides an overview of the features provided by BlackSheep to handle |
| 13 | +these scenarios. |
| 14 | + |
| 15 | +### Routing based on hostnames |
| 16 | + |
| 17 | +```mermaid |
| 18 | +graph LR |
| 19 | + client1["Client 1"] -->|HTTP GET https://orders.neoteroi.xyz/order/123| proxy["Routing rules"] |
| 20 | + client2["Client 2"] -->|HTTP POST https://orders.neoteroi.xyz/order| proxy["Routing rules"] |
| 21 | + client3["Client 3"] -->|HTTP GET https://users.neoteroi.xyz/user/123| proxy["Routing rules"] |
| 22 | +
|
| 23 | + subgraph "HTTP Proxy" |
| 24 | + direction TB |
| 25 | + proxy |
| 26 | + end |
| 27 | +
|
| 28 | + subgraph "Servers" |
| 29 | + A["Orders Web API<br>orders.neoteroi.xyz"] |
| 30 | + B["Users Web API<br>users.neoteroi.xyz"] |
| 31 | + C["Consents Web API<br>consents.neoteroi.xyz"] |
| 32 | + end |
| 33 | +
|
| 34 | + proxy -->| orders.neoteroi.xyz | A |
| 35 | + proxy -->| users.neoteroi.xyz | B |
| 36 | + proxy -->| consents.neoteroi.xyz | C |
| 37 | +
|
| 38 | + %% Note |
| 39 | + classDef note stroke:#000,stroke-width:1px; |
| 40 | + note["Example: *.neoteroi.xyz is the wildcard domain used by an HTTP Proxy. |
| 41 | + Several domains are configured to point to the same proxy.<br> |
| 42 | + Requests are routed to different backend services based on subdomains."]:::note |
| 43 | + proxy -.-> note |
| 44 | +``` |
| 45 | + |
| 46 | +Routing based solely on the **host** header generally does not introduce |
| 47 | +complications for backend web applications. However, it does require additional |
| 48 | +maintenance to manage multiple domain names and TLS settings, and routing |
| 49 | +rules. |
| 50 | + |
| 51 | +### Routing based on paths |
| 52 | + |
| 53 | +Path-based routing allows a proxy server to forward requests to different |
| 54 | +backend services based on a prefix of the URL path. This is particularly useful |
| 55 | +when hosting multiple applications or services under the same domain. |
| 56 | + |
| 57 | +```mermaid |
| 58 | +graph LR |
| 59 | + client1["Client 1"] -->|HTTP GET https://api.neoteroi.xyz/order/123| proxy["Routing rules"] |
| 60 | + client2["Client 2"] -->|HTTP POST https://api.neoteroi.xyz/order| proxy["Routing rules"] |
| 61 | + client3["Client 3"] -->|HTTP GET https://api.neoteroi.xyz/user/123| proxy["Routing rules"] |
| 62 | +
|
| 63 | + subgraph "HTTP Proxy" |
| 64 | + direction TB |
| 65 | + proxy |
| 66 | + end |
| 67 | +
|
| 68 | + subgraph "Servers" |
| 69 | + A["Orders Web API"] |
| 70 | + B["Users Web API"] |
| 71 | + C["Consents Web API"] |
| 72 | + end |
| 73 | +
|
| 74 | + proxy -->| /orders/* | A |
| 75 | + proxy -->| /users/* | B |
| 76 | + proxy -->| /consents/* | C |
| 77 | +
|
| 78 | + %% Note |
| 79 | + classDef note stroke:#000,stroke-width:1px; |
| 80 | + note["Example: api.neoteroi.xyz is the domain of an HTTP Proxy.<br> |
| 81 | + Depending on the first portion of the URL path,<br/>the HTTP Proxy forwards the request to the appropriate server."]:::note |
| 82 | + proxy -.-> note |
| 83 | +``` |
| 84 | + |
| 85 | +When deploying behind proxies in this manner, it is crucial to ensure that the |
| 86 | +application properly handles being exposed at a specific path. While this works |
| 87 | +well for most REST APIs, it can lead to complications with redirects and for |
| 88 | +applications that include user interfaces. |
| 89 | + |
| 90 | +The following diagram illustrates the problem of redirects, if the path prefix |
| 91 | +is not handled properly. |
| 92 | + |
| 93 | +```mermaid |
| 94 | +sequenceDiagram |
| 95 | + autonumber |
| 96 | + actor User |
| 97 | + participant Proxy as HTTP Proxy<br>(Exposes /example/) |
| 98 | + participant Backend as Backend Server<br>(Exposed at /) |
| 99 | +
|
| 100 | + User->>Proxy: HTTP GET https://example.com/example/dashboard |
| 101 | + Proxy->>Backend: HTTP GET /dashboard |
| 102 | + Backend-->>Proxy: HTTP 302 Redirect to /sign-in |
| 103 | + Proxy-->>User: HTTP 302 Redirect to /sign-in |
| 104 | +
|
| 105 | + note over User: The user is redirected to<br>https://example.com/sign-in,<br>which is incorrect because<br>the prefix /example/ is missing. |
| 106 | +
|
| 107 | + User->>Proxy: HTTP GET https://example.com/sign-in |
| 108 | + Proxy-->>User: HTTP 404 Not Found |
| 109 | +``` |
| 110 | + |
| 111 | +/// details | The example of API Gateway |
| 112 | + type: info |
| 113 | + |
| 114 | +API Gateways like [AWS API Gateway](https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/api-routing-path.html) |
| 115 | +and [Azure API Management](https://learn.microsoft.com/en-us/azure/api-management/api-management-key-concepts) |
| 116 | +use path based routing to expose many APIs behind the same domain name. |
| 117 | +Path based routing generally does not cause complications for REST APIs, but |
| 118 | +likely causes complications for web apps serving HTML documents and |
| 119 | +implementing interactive sign-in. |
| 120 | + |
| 121 | +/// |
| 122 | + |
| 123 | +--- |
| 124 | + |
| 125 | +`BlackSheep` offers two ways to deal with this scenario: |
| 126 | + |
| 127 | +- One approach, defined by the `ASGI` specification, involves specifying a |
| 128 | + `root_path` in the `ASGI` server. This information is passed in the scope of |
| 129 | + web requests. This method is ideal for those who prefer not to modify the |
| 130 | + path at which web servers handle requests, and to configure the proxy server |
| 131 | + to strip the extra prefix when forwarding requests to backend services |
| 132 | + (applying URL rewrite). |
| 133 | +- The second approach involves configuring a prefix in the application router |
| 134 | + to globally change the prefix of all request handlers. The global prefix can |
| 135 | + also be set using the environment variable `APP_ROUTE_PREFIX`. This method |
| 136 | + assumes that modifying the path handled by the web server is desirable to |
| 137 | + align it with the path handled by the HTTP proxy server, and it is ideal |
| 138 | + when using URL rewrite is not easy. |
| 139 | + |
| 140 | +For both options, `BlackSheep` handles the information provided by `root_path` |
| 141 | +or the application router prefix in some specific ways. |
| 142 | +For example, the `get_absolute_url_to_path` defined in `blacksheep.messages` |
| 143 | +will handle the information and return an absolute URL to the server |
| 144 | +according to both scenarios. |
| 145 | + |
| 146 | +| Feature | Description | |
| 147 | +| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | |
| 148 | +| `request.base_path` | Returns the `base_path` of a web request, when the ASGI scope includes a `root_path`, or a route prefix is used. | |
| 149 | +| `blacksheep.messages.get_absolute_url_to_path` | Returns an absolute URL path to a given destination, including the current `root_path` or route prefix. Useful when working with redirects. | |
| 150 | +| OpenAPI Documentation | Since version `2.1.0`, it uses relative links to serve the OpenAPI Specification files (YAML and JSON), and relative paths to support any path prefix. | |
| 151 | + |
| 152 | +/// details | Jinja2 template helper |
| 153 | + type: tip |
| 154 | + |
| 155 | +The BlackSheep MVC template includes an example of helper function to |
| 156 | +[render absolute paths](https://github.com/Neoteroi/BlackSheep-MVC/blob/88b0672a0696d4bef4775203fae086173fd9b0fc/%7B%7Bcookiecutter.project_name%7D%7D/app/templating.py#L26) |
| 157 | +in Jinja templates. |
| 158 | + |
| 159 | +/// |
0 commit comments