Use project Tye to host Blazor WASM and ASP.NET Web API on a single origin to avoid CORS
Tags: dotnet, aspnet, blazor
Published: 2021-02-15 20:59:08

---
title: Use project Tye to host Blazor WASM and ASP.NET Web API on a single origin to avoid CORS
published: true
date: 2021-02-14 00:00:00 UTC
tags: dotnet, aspnet, blazor
canonical_url: https://swimburger.net/blog/dotnet/use-project-tye-to-host-blazor-wasm-and-aspdotnet-web-api-on-a-single-origin
cover_image: https://swimburger.net/media/ofzhqoz0/dotnet-project-tye-social.png?mode=crop&width=1000&height=420
---

Applications are often split up into a client and a server. The client renders the UI and communicates to the server through an API. But when the client and server are split up into separate projects and hosted at different origins ( **origin == scheme + host + port** ), it can be a hassle for the client to communicate with the server.

On the web specifically, communicating between different origins can be painful. By default, clients are not allowed to make ajax requests to a different origin. The server has to use **Cross-Origin Resource Sharing (CORS)** headers to allow cross-origin ajax requests.

Another way to resolve CORS challenges is to circumvent it entirely by having the client and server hosted on the same origin. If your technology stack allows for it, you can merge the client and server into a single project and host them on a single origin. If that's not possible, you can **use a reverse proxy to consolidate the client origin and the server origin under the proxy's origin**.

![A diagram: web browser points to reverse proxy server. Reverse proxy server points to two web servers. The first web server is an API server. The second server is a frontend website server. The diagram visualizes how the HTTP requests from a web browser are sent to the reverse proxy. If the requested path starts with "/API/", the proxy forwards the request to the API server. Otherwise, the request is sent to the Client Web Server which serves the frontend of your application.](https://swimburger.net/media/no5fagrc/reverseproxy.svg)

The diagram above visualizes how the HTTP requests from a web browser are sent to the reverse proxy. If the requested path starts with '_/API/_', the proxy forwards the request to the API server. Otherwise, the request is sent to the Client Web Server which serves the frontend of your application.

You can pick any option for your frontend client, backend server, and reverse proxy server. It does not matter because they all understand the same language: **HTTP**. In this walkthrough, you'll learn how to communicate between a **Blazor WebAssembly (WASM)** client and an **ASP.NET Web API**  **without CORS**.

In a previous tutorial, you learned [how to achieve this using Microsoft's new reverse proxy "YARP"](https://swimburger.net/blog/dotnet/use-yarp-to-host-client-and-api-server-on-a-single-origin "Use YARP to host client and API server on a single origin"). You then used Project Tye, Microsoft's experimental developer tool to spin up the client, server, and proxy with a single command. You can also use **Project Tye**'s built-in proxy server instead of YARP.

In this walkthrough, the client and server will be hosted on a single origin by using Tye's ingress feature.

![A diagram: web browser points to the Tye Ingress. Tye Ingress points to two web servers. The first web server is an ASP.NET Core WebAPI server. The second server is a Blazor WebAssembly server. The diagram visualizes how the HTTP requests from a web browser are sent to the Tye Ingress. If the requested path starts with "/API/", the proxy forwards the request to the ASP.NET Core WebAPI server. Otherwise, the request is sent to the Client Web Server which serves the Blazor WebAssembly application.](https://swimburger.net/media/brbashxb/reverseproxytye.svg?width=500&height=228.4688995215311)

This is how the [Tye docs define an Ingress](https://github.com/dotnet/tye/blob/master/docs/recipes/ingress.md):

> For modern cloud runtime environments like Kubernetes, it's typical to keep the vast majority of services isolated from public internet traffic. Usually, there's a single (or few) point of entry for public traffic using a highly configurable proxy server.   
>   
> This approach is referred to as ingress. The proxy server and surrounding management infrastructure are referred to as an ingress controller and the configurations of routing rules are referred to as ingresses.

This sounds exactly like a reverse proxy, but Tye will refer to it as an Ingress throughout their documentation and configuration.

## Prerequisites

To follow along you need the following tech:

- .NET Core 3.1 or up ([installation instructions](https://dotnet.microsoft.com/download))
- Windows PowerShell or PowerShell Core ([installation instructions](https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell))
- OS which supports the above tech such as Windows, Mac, or Linux

You can find all the source code for the end result on [GitHub](https://github.com/Swimburger/TyeClientServerSingleOrigin).

## Create the Blazor WASM client

Before creating the client, create a folder where all projects will reside using the following commands:

```powershell
mkdir TyeClientServerSingleOrigin
cd TyeClientServerSingleOrigin
```

Create a solution file using the dotnet command-line interface (CLI):

```powershell
dotnet new sln
```

Create the Blazor WASM client:

```powershell
dotnet new blazorwasm -n Client -o Client
```

**-n**: specifies the name of the project
**-o**: specifies the location where the project will be created

Using the command above, you created a Blazor WASM project named 'Client' which is also used as the default namespace. This project is created in a subfolder also named 'Client'.

Add the client project to the solution file:

```powershell
dotnet sln add Client
```

When you take a look at the FetchData component at _Client\Pages\FetchData.razor_, you can see the client is requesting data about the weather from a static JSON file:

```powershell
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
```

Update this line of code to fetch the data from `api/WeatherForecast` instead:

```powershell
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("api/WeatherForecast");
```

This API endpoint does not exist yet, but you will create this soon.

## Create the ASP.NET Core Web API server

Create the Web API project using the dotnet CLI:

```powershell
dotnet new webapi -n Server -o Server
```

Using the command above, you created a Web API project named 'Server' which is also used as the default namespace. This project is created in a subfolder also named 'Server'.

Add the server project to the solution file:

```powershell
dotnet sln add Server
```

When you try to run the client and server at the same time, an error will be shown because they use the same port by default. The ports used by these projects are specified at '_Properties\launchSettings.json_'. To prevent port collision, replace ' **https://localhost:5001;http://localhost:5000**' with ' **https://localhost:5003;http://localhost:5002**' at '_Server\Properties\launchSettings.json_'. Or run these PowerShell commands:

```powershell
$launchSettingsContents = Get-Content -path ./Server/Properties/launchSettings.json -Raw
$launchSettingsContents = $launchSettingsContents -replace 'https://localhost:5001;http://localhost:5000','https://localhost:5003;http://localhost:5002'
Set-Content -Value $launchSettingsContents -Path ./Server/Properties/launchSettings.json
```

There is a single controller that comes out of the box with the WebAPI template: WeatherForecastController.cs  
This controller generates random weather forecasts and luckily for you matches the data model used in the Blazor WASM client. The WeatherForecast API can be requested at '_/WeatherForecast_', but the client will request it at '_/api/WeatherForecast_'. The reverse proxy you will create next will suffix the API with '_/api_'.

## Tying everything up with Tye

Install Tye as a .NET global tool:

```powershell
dotnet tool install -g Microsoft.Tye --version "0.6.0-alpha.21070.5"
```

You can find the most recent version of [Tye at Nuget](https://www.nuget.org/packages/Microsoft.Tye).

Initialize tye using this command:

```powershell
tye init
```

This will create a configuration YAML file '_tye.yaml_'. Update the YAML file to match the content below:

```yaml
name: tyeclientserversingleorigin
services:
- name: client
  project: Client/Client.csproj
  bindings:
  - port: 5001
    protocol: https
- name: server
  project: Server/Server.csproj
  bindings:
  - port: 5003
    protocol: https
ingress:
  - name: ingress
    bindings:
      - port: 8080
        protocol: https
    rules:
      - path: /api
        service: server
      - path: /swagger
        preservePath: true
        service: server
      - service: client
```

Supposedly, you should be able to omit the binding in the tye configuration, but some things were very buggy when doing so. So for now you should specify the port and protocol for the client, server, and ingress.  
This YAML file will configure tye to

- run the client at _https://localhost:5001_
- run the server at _https://localhost:5003_
- run an ingress (reverse proxy) at **_https://localhost:8080_** with the following rules
  - any request starting with path _/api_ will be routed to the server service.   
By default, the defined path will be trimmed. HTTP requests to _https://localhost:8080/api/weatherforecast_ will be proxied to _https://localhost:5003/weatherforecast_. Note the _/api_ prefix has been removed.
  - any request starting with path _/swagger_ will be routed to the server service. The 'preservePath' property will ensure that '/swagger' will not be trimmed. HTTP requests to _https://localhost:8080/swagger_ will be proxied to _https://localhost:5003/swagger_.  
Swagger's JavaScript will request data relative to the root, not relative to _/api_, so you need to add this rule to fix that.
  - any request which didn't match any of the previous rules will be proxied to the client service. For example, _https://localhost:8080/counter_ will be routed to _https://localhost:5001/counter_.

Run this command to run all the services and ingress:

```powershell
tye run
```

Open a browser and navigate to _ **https://localhost:8080** _. The Blazor UI should appear.   
When you navigate to the Fetch Data section, the weather forecasts should be updating with random results every time you refresh because the data is coming from the server ASP.NET WebAPI project.

You can also navigate to tye's dashboard at **_localhost:8000_** _:_

 ![Screenshot of the project Tye dashboard](https://swimburger.net/media/sydd1iqw/tye-dashboard.png)

In addition to finding the URL for each service, you can also watch the log streams of each service.

## Summary

API's need to provide CORS headers to explicitly allow ajax requests from web clients hosted on a different origin. Reverse proxies can merge the API origin and client origin onto a single origin. Using Microsoft's experimental Project Tye, you can configure an ingress/proxy to forward requests from '_/api_' to the Web API, and all other requests to the Blazor WASM client.

You can learn more about project Tye on [its GitHub repository](https://github.com/dotnet/tye).