11

Medhat Elmasry

 3 years ago
source link: https://blog.medhat.ca/search/label/ASP.NET
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Showing posts with label ASP.NET. Show all posts
Showing posts with label ASP.NET. Show all posts

Saturday, December 12, 2020

Exploring GitHub Codespaces

In this tutorial I will introduce you to GitHub Codespaces. We will first create an ASP.NET Core MVC application on your local computer. We will then push the application to GitHub. Once the application source code is on GitHub, we will use Visual Studio Code in GitHub Codespaces to modify the app and test it out - all in the cloud.

What is GitHub codespaces?

GitHub codespaces is an online development environment, hosted by GitHub and powered by Visual Studio Code. It allows you to develop entirely in the cloud. Codespaces is currently in limited public beta and subject to change.

You can signup for access to GitHub Codespaces at: https://github.com/features/codespaces/signup

Let's get started.

1) Create a repository in GitHub. I named mine MvcOnCodespaces.

2) Create an ASP.NET Core MVC application on your local computer. These are the commands I used to create the application named MvcOnCodespaces.

Create a directory for your application

mkdir MvcOnCodespaces

Change to the directory you just created.

cd MvcOnCodespaces

At the moment, the default version of .NET Core that is available on GitHub Codespaces is version 3.1. Therefore, to ensure that we create an application that uses .NET Core 3.1, we will create a global.json file specifying .NET Core version as follows:

dotnet new globaljson --sdk-version 3.1.401

NOTE: Find out the version of .NET Core 3.1 that exists on your computer using command:

dotnet --list-sdks 

Use the appropriate version in the 'dotnet new globaljson ..." command.

This was necessary for me to do because the default version of .NET Core on my computer was 5.0 at the time of writing this post. 

Now we can create an ASP.NET Core MVC 3.1 app with:

dotnet new mvc

If you inspect your .csproj file, you will find that it, indeed, targets .NET Core 3.1 (netcoreapp3.1).

netcoreapp3.1
At this point, you can go ahead and delete global.json because it served its purpose and we do not need it anymore.
3) Before we push our ASP.NET Core MVC application to GitHub, we need to have a .gitignore file. To create an appropriate .gitignore file, enter the following command in a terminal window:
dotnet new gitignore
Thereafter, create a local git repository, add all your source code files to it and commit your changes with these commands:
git init
git add .
git commit -m "1st commit"
4) Push your source-code to GitHub with the instructions on your repository for pushing existing code:
an existing repository from the command line

4) Create a Codespace. In your GitHub repository, click on Code followed by "Open with Codespaces".

Open with Codespaces

On the next dialog, click on the "+ New codespace" button.

+ New codespace

At the top right side you will see a progress bar that indicates that a Codespace is being prepared for you.

preparing your codespace
Click on Yes when you see this dialog:
required assets to build and debug
You will find yourself in familiar territory with VS Code running in your browser. Wait until all the activity in the lower pane settles down and you see a Finished statement.
Online VS Code

Querying the .NET environment in your Codespace

Let us query the .NET Core environment in a terminal window. Click on the TERMINAL tab.
Terminal
In the terminal window, type:
dotnet --list-sdks
The list of SDKs at the time of writing this article were as shown below:
dotnet --list-sdks

Build & run your web app

You can also go ahead and build with: dotnet build
dotnet build
To run your application, hit CTRL F5. In the "DEBUG CONSOLE" pane, do a "CTRL Click" on the https://localhost:5001 link.
Ctrl Click
Port forwarding happens and the web app opens in a separate tab in your browser.
Port forwarding in codespaces
Let's make a change to our application. Edit Views/Shared/_Layout.cshtml in the Codespace. Around line 32, add the following style to the main <div> tag to change the background color to gold:
style="background-color: gold;"
css style
Stop and restart the application. This is done by clicking on the stop button first.
Stop application
Thereafter, hit CTRL F5. After the application restarts, go to the other tab that has theweb app and refresh the page. You will see the style change that we made.
CSS style change

Syncing source code

Git reminds us that there are three changes that happened to our code.
Git changes
Stage changes with the following:
Stage all changes
Next, let us commit staged changes:
Commit stages
Enter a message:
git commit message
Finally, push the changes:
git push

Debugging

Let us see if we can debug the application in GitHub Codespaces. Stop the application. Open Controllers/HomeController.cs in the online VS Code editor. Add some code to the Index() action method as follows:
breakpoint
Add a breakpoint on the line with statement 'return View()'.
Run your application in Debug mode by hitting F5. If you refresh the web app in the other tab, the app will stop at the breakpoint, as expected.
stop at breakpoint
You can use the debug controls to: Continue, Step Over, Step Into, Step Out, Restart and Stop
debug controls

Cleanup

Delete the codespace you created once you determine that you do not need it anymore. Click on the Codespaces tab, click the ... (three dots) on the right side of the codepace,  then choose delete.
delete github codespace

Conclusion

I hope this journey through the world of GitHub Codespaces gave you a good understanding of what is possible with this new cloud service.

Friday, July 24, 2020

Deploy Blazor SPA & Azure Functions API to Azure Static Web Apps (preview)

We have a new Azure service named "Azure Static Web Apps" that is, currently, in preview. This service was announced by Microsoft at Build 2020. It is essentially a lightweight solution for deploying a Single Page Application (SPA) together with an API service based on Azure Functions to a single site. This means that the SPA and the Azure Functions API reside in the same web application on Azure.

You can access the source code for this walkthrough from https://github.com/medhatelmasry/blazor-az-static-web-apps.

Related Video: https://youtu.be/zU1GeIUx9TY

In this post I will give you a step-by-step tutorial on how to develop an application with the following ingredients:
  • a Cosmos backend database
  • an Azure Functions REST API based on Node.js
  • a client-side Blazor application that represents the SPA
  • the API & SPA will be placed in a GitHub repository. A GitHub Actions workflow will then be used to deploy the code to a Azure Static Web App service
  • VS Code will be the editor used so that this tutorial will work on any OS including Linux, macOS and Windows
Here is a diagram of the eventual solution:
Untitled1.png
Whenever our code on GitHub is changed, a workflow kicks in that deploys the API and SPA to Azure Static Web Apps.

Before we proceed, it is assumed that you have the following:
  • a Microsoft Azure account
  • a GitHub account
  • Node.js is installed on your local computer
  • Postman will be used for testing our REST API
  • VS Code Editor is installed on your local computer

Disclaimer:

This is not a tutorial for Node.js, Blazor or GitHub. You do not need to be an expert in any of these technologies in order to follow this walkthrough.
First we will build & test our app on local computer before deploying it to Azure.

Host data in Azure Cosmos DB

Log into the Azure portal and click on the "+ Create a resource"  button.
Untitled2.png
Enter 'cosmos' into the filter input field then select "Azure Cosmos DB".
Untitled3.png

Click on the blue Create button.

Untitled4.png
On the "Create Azure Cosmos DB Account" page, enter the account details then click on "Review + create". This is what it looked like for me:
Untitled5.png
Click Create on the next confirmation page.
Untitled6.png
Once you click on Create, it will take a while before the database is provisioned. When provisioning is done, you will see the following under Notifications.
Untitled7.png
Click on “Go to resource” then click on Keys on the left-side navigation menu.
Untitled8.png
To access Cosmos DB from our application, we will need to obtain the first two values (URL and PRIMARY KEY):
Untitled7.png

Azure Functions CRUD REST API

Let us create our solution which will contain two projects:
  1. Azure Functions Rest API
  2. Client-side Blazor application
We will sbe tart with building the Azuree Functions REST API. Choose a suitable working directory on your computer then execute the following commands from a terminal window:

mkdir az-static-web-apps
cd az-static-web-apps
mkdir api
cd api
code .

The above commands create a solution directory named az-static-web-apps, create a sub-directory named api then open VS Code in the az-static-web-apps/api workspace.

Make sure you install the following 'Azure Functions' extension for VS Code:

Untitled10.png
In the left-side navigation menu in VS Code, click on the Azure icon.
Untitled11.png
In the FUNCTIONS popup menu, click on the "Create New Project..." icon.
Untitled12.png
Navigate to the newly created api folder.
Untitled13.png
Since we will be using Node.js to build our Azure Functions API, select JavaScript for the programming language.
Untitled14.png
Next, select HTTP trigger.
Untitled15.png
Provide function name products-get:
Untitled16.png
To keep things simple, we will use avoid authentication. Therefore, select Anonymous.
Untitled17.png
Hit F5 to run your function locally. CTRL + Click on the following link:


Stop the application by clicking on the Disconnect tool.

Edit function.json. Delete “post”. Also, just below the methods methods section, add:
"route": "products"
From within a terminal window inside the api folder, run the following commands to install the @azure/cosmos package:
npm install @azure/cosmos -save
Create a data folder under api and add to it a file named data-context.js. This new file will contain a module that is responsible for creating a Cosmos Database & a Container, if they do not already exist.

Add following code to data/data-context.js:

/*
// This script ensures that the database is setup and populated correctly
*/
async function create(client, databaseId, containerId, partitionKey) {

/**
* Create the database if it does not exist
*/
let { database } = await client.databases.createIfNotExists({
id: databaseId
});

console.log(`Created database:\n${database.id}\n`);

/**
* Create the container if it does not exist
*/
let { container } = await client
.database(databaseId)
.containers.createIfNotExists(
{ id: containerId, partitionKey },
{ offerThroughput: 400 }
);

console.log(`Created container:\n${container.id}\n`);
}

module.exports = { create };

Also, in the data folder, create another file named seed-data.js, which will be used to seed the database container with some sample product data consisting of name, description and quantity.

Add following code to data/seed-data.js:

const data = {
products: [
{
  name: 'Strawberries',
  description: '16oz package of fresh organic strawberries',
  quantity: '1',
},
{
  name: 'Sliced bread',
  description: 'Loaf of fresh sliced wheat bread',
  quantity: 11,
},
{
name: 'Apples',
description: 'Bag of 7 fresh McIntosh apples',
quantity: 12,
},
{
  name: 'Banana',
  description: 'Fresh Baby Nino Bananas',
  quantity: 13,
},
{
  name: 'Potatoes',
  description: 'Russet Potatoes',
  quantity: 14,
},
{
  name: 'Baking Powder',
  description: 'Baker\'s Supply HouseOrganic Baking Powder',
  quantity: 15,
},
{
  name: 'Sugar',
  description: 'Rogers Sugar Cubes',
  quantity: 16,
},
{
  name: 'Milk',
  description: 'Dairyland 1% Skim Milk',
  quantity: 17,
},
{
  name: 'Peppermint Tea',
  description: 'Tetley Pure Peppermint Tea',
  quantity: 18,
},
{
  name: 'Feta Cheese',
  description: 'Shepherd Gourmet Dairy Greek Sheep Feta Cheese',
  quantity: 19,
},
],
};

const getSeedProductData = () => {
  return data.products;
};

module.exports = { getSeedProductData };

All the functions that will used to read, insert, update and delete data will be placed in a file named product-data.js in a shared folder. Create a folder under api named shared.

Create a file named product-data.js in the shared folder and add to it the following code that seeds and reads data:

const CosmosClient = require("@azure/cosmos").CosmosClient;
const DbContext = require("../data/data-context");
const SeedData = require("../data/seed-data");

const { ENDPOINT, KEY, DATABASE, CONTAINER, PARTITION_KEY } = process.env;
const client = new CosmosClient({ endpoint: ENDPOINT, key: KEY });
const database = client.database(DATABASE);
const container = database.container(CONTAINER);

const seedProducts = async () => {
let partition_key = JSON.parse(PARTITION_KEY);
await DbContext.create(client, DATABASE, CONTAINER, partition_key);

let iterator = container.items.readAll();
let { resources } = await iterator.fetchAll();

if (resources.length > 0) {
  return { "message": `The database is already seeded with   ${resources.length} products.` };
} else {
  const products = SeedData.getSeedProductData();

products.forEach(async function (item) {
    const { resource: createdItem } = await      container.items.create(item);
    console.log(item);
  })

return { "message": `The database has been seeded with ${products.length} products.` };
}
};

const getProducts = async () => {
  let iterator = container.items.readAll();
  let { resources } = await iterator.fetchAll();
  return resources;
};

module.exports = {
  seedProducts,
  getProducts
};

Edit api/local.settings.json and add to it the following cosmos database settings in the Values section:

"ENDPOINT": "https://cosmos-node.documents.azure.com:443/",
"PARTITION_KEY": "{ \"kind\": \"Hash\", \"paths\": [\"/name\"] }",
"KEY": "IioHwrKY5D7vcDmsSL05861234567890oQe0oqKdVh4Hrdk1234567890a7bmDFNUU7x1234567890kqxhIHEQ==",
"DATABASE": "Catalog",
"CONTAINER": "Products"


IMPORTANT NOTE: The values for ENDPOINT and KEY depend on your environment and matches the keys values for your Cosmos database instance.  Also add this new section:

"Host": {
  "CORS": "*"
}


Let us add an Azure Function API endpoint that is responsible for seeding data. Click on the "Create Function..." icon as shown below.
Untitled20.png
Choose "HTTP trigger".
Untitled21.png
Provide the function name products-seed.
Untitled22.png
Once again we will choose Anonymous.
Untitled23.png
Edit api/products-seed/function.json, delete method get. Add the following route after methods[..]:
"route": "products/seed"
Replace api/products-seed/index.js with the following code:

const data = require('../shared/product-data');

module.exports = async function (context, req) {
  try {
    const result = await data.seedProducts();
    context.res.status(200).json(result);
  } catch (error) {
    context.res.status(500).send(error);
  }
};

The above code calls the seedProducts() function in shared/product-data.js.

Replace api/products-get/index.js with the following code:

const data = require('../shared/product-data');

module.exports = async function (context, req) {
  try {
    const products = await data.getProducts();
    context.res.status(200).json(products);
  } catch (error) {
    context.res.status(500).send(error);
  }
};

The above code reads all products from the Cosmos database.

Run your Azure Functions in VS Code by hitting CTRL F5 on your keyboard.

Let us seed some real data. Start Postman. Choose POST and enter the below endpoint:
Untitled24.png
If all goes well, the response will look like this:
Untitled25.png
Next, let's try a get request to read all the products:
Untitled26.png
You should see that all products are returned:
Untitled27.png
To complete the APIs, let us add these CRUD endpoints:
products-delete, products-get-one, products-post and products-put
Add these additional CRUD functions to shared/product-data.js:

const addProduct = async (productToAdd) => {
  // remove 'id' property from the JSON object
  delete productToAdd.id;

let { product } = await container.items.create(productToAdd);
  return product;
};

const updateProduct = async (id, product) => {
  return await container.item(id, product.name).replace(product);
};

const readProduct = async (id) => {
  // query to return all items
  const querySpec = {
    query: "SELECT * FROM c WHERE c.id='" + id + "'"
  };

// read all items in the Items container
  const { resources: items } = await container.items
    .query(querySpec)
    .fetchAll();

if (items.length > 0)
    return items[0];
  else
    return {};
};

const deleteProduct = async (id, name) => {
  return await container.item(id, name).delete();
};

Remember to add these to the module.exports at the bottom of shared/product-data.js:

addProduct,
updateProduct,
deleteProduct,
readProduct

To complete the APIs needed, create four more Azure Functions named products-delete, products-get-one, products-post and products-put. Below is the code for each.

1) products-delete

function.json

"methods": ["delete"],
"route": "products/{id}"

index.js

const data = require('../shared/product-data');

module.exports = async function (context, req) {
  const id = req.params.id;
  const product = await data.readProduct(id);
  const name = product.name;

try {
    const { result } = await data.deleteProduct(id, name);
    context.res.status(200).json(result);
  } catch (error) {
    context.res.status(500).send(error);
  }
};

2) products-get-one

function.json

"methods": ["get"],
"route": "products/{id}"

index.js

const data = require('../shared/product-data');

module.exports = async function (context, req) {
  const id = req.params.id;
  try {
    const product = await data.readProduct(id);
    context.res.status(200).json(product);
  } catch (error) {
    context.res.status(500).send(error);
  }
};

3) products-post

function.json

"methods": ["post"],
"route": "products"

index.js

const data = require('../shared/product-data');

module.exports = async function (context, req) {
  const product = req.body;

try {
    const newProduct = await data.addProduct(product);
    context.res.status(201).json(newProduct);
  } catch (error) {
    context.res.status(500).send(error);
  }
};

4) products-put

function.json

"methods": ["put"],
"route": "products/{id}"

index.js

const data = require('../shared/product-data');

module.exports = async function (context, req) {

const id = req.params.id;
  const product = req.body;

try {
    const { updatedProduct } = await data.updateProduct(id, product);
    context.res.status(200).json(updatedProduct);
  } catch (error) {
    context.res.status(500).send(error);
  }
};

Blazor Client

Close VS Code and open a terminal window in the az-static-web-apps directory. Create a Blazor application in a blazor-app sub-directory with the following command:
dotnet new blazorwasm -o blazor-app
We need a solution file in the az-static-web-apps directory for the GitHub Actions workflow. Therefore, execute these commands to create a solution file and add to it the Blazor project:

dotnet new sln
dotnet sln add blazor-app/blazor-app.csproj


You can run the new Blazor application to see what the default template looks like by executing the below commands and then going to https://localhost:5001.

cd blazor-app
dotnet run

Open VS Code in the az-static-web-apps workspace directory.

Create a Models folder under blazor-app and add to it these new files:

Constants.cs
Product.cs

Content of Constants.cs:

namespace blazor_app.Models {
  public class Constants {
    public static string BaseURL {
      get {
        return "http://localhost:7071/";
        //return "/";
      }
    }
  }
}

Content of Product.cs:

namespace blazor_app.Models {
  public class Product {
    public string Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }

//public int Quantity { get; set; }
  }
}

Add the following to _imports.razor so that the above classes are visible in the .razor pages.

@using blazor_app.Models
Delete Pages/Counter.razor & Pages/FetchData.razor.

Copy Index.razor to Add.razor.
Copy Index.razor to Delete.razor.
Copy Index.razor to Edit.razor.

Content of Index.razor

@page "/"
@inject HttpClient httpClient
<h1>Products</h1>
@if (products == null)
{
  <NavLink class="btn btn-primary" href="/add">Add</NavLink>
}
  else
{
  <NavLink class="btn btn-primary" href="/add">Add</NavLink>
  <table class='table table-hover'>
  <thead>
  <tr>
  <th>Id</th>
  <th>Name</th>
  <th>Description</th>
  @*<th>Quantity</th>*@

<th>Edit</th>
  <th>Delete</th>
  </tr>
  </thead>
  <tbody>
  @foreach (var item in products)
  {
    <tr>
    <td>@item.Id</td>
    <td>@item.Name</td>
    <td>@item.Description</td>
    @*<td>@item.Quantity</td>*@

<td><a type="button" class="btn btn-success"       href="/edit/@item.Id">Edit</a></td>
    <td><a type="button" class="btn btn-danger"       href="/delete/@item.Id">Delete</a></td>
    </tr>
  }
  </tbody>
  </table>
}
@code {
  Product[] products;
  string baseUrl = Constants.BaseURL;
  protected override async Task OnInitializedAsync()
  {
    products = await httpClient.GetFromJsonAsync<Product[]>($"{baseUrl}api/products");
  }
}

Content of Add.razor

@page "/Add"
@inject HttpClient httpClient
@inject NavigationManager NavigationManager
<h1>Add a New Speaker</h1>

<EditForm Model="@product" OnValidSubmit="@HandleAdd" class="form-group">
<DataAnnotationsValidator />
<ValidationSummary />

First Name
<InputText placeholder="Name" id="name" @bind-Value="@product.Name" class="form-control"/>
<br />
Last Name
<InputText placeholder="Description" id="description" @bind-Value="@product.Description" class="form-control"/>
<br />
<button type="submit" class="btn btn-primary">Submit</button>
</EditForm>
<NavLink class="btn btn-success" href="/">Back</NavLink>

@code {
  private Product product = new Product();

private async void HandleAdd() {
    string baseUrl = Constants.BaseURL;
    string endpoint = $"{baseUrl}api/products";

await httpClient.PostAsJsonAsync(endpoint, product);

NavigationManager.NavigateTo("/");
  }
}

Content of Edit.razor

@page "/edit/{id}"
@inject HttpClient httpClient
@inject NavigationManager NavigationManager

<h1>Edit Product</h1>

<EditForm Model="@product" OnValidSubmit="@edit" class="form-group">
<DataAnnotationsValidator />
<ValidationSummary />

First Name
<InputText placeholder="Name" id="firstName" @bind-Value="@product.Name" class="form-control" />
<br />
Last Name
<InputText placeholder="Last Name" id="lastName" @bind-Value="@product.Description" class="form-control" />
<br />
<button type="submit" class="btn btn-primary">Submit</button>
</EditForm>
<NavLink class="btn btn-success" href="/">Back</NavLink>

@code {
    [Parameter]
    public string id { get; set; }
    Product product = new Product();
    string baseUrl = Constants.BaseURL;

protected override async Task OnInitializedAsync()
    {
        product = await httpClient.GetFromJsonAsync<Product>($"{baseUrl}api/products/{id}");
    }

private async void edit()
    {
        await httpClient.PutAsJsonAsync($"{baseUrl}api/Products/{id}", product);
        NavigationManager.NavigateTo("/");
    }

Content of Delete.razor

@page "/delete/{id}"
@inject HttpClient httpClient
@inject NavigationManager NavigationManager

<h1>Delete</h1>
@if (product != null)
{
  <p>Are your sure you want to delete</p>
  <p>@product.Name <br /> @product.Description</p>
  <button type="button" @onclick="@del" class="btn btn-danger">Confirm Delete</button>
  <NavLink class="btn btn-success" href="/">Back</NavLink>
}

@code {
  [Parameter]
  public string id { get; set; }
  Product product;
  string baseUrl = Constants.BaseURL;

protected override async Task OnInitializedAsync()
  {
      product = await httpClient.GetFromJsonAsync<Product>($"{baseUrl}api/products/{id}");
  }

private async void del()
  {
      await httpClient.DeleteAsync($"{baseUrl}api/products/{id}");
      NavigationManager.NavigateTo("/");
  }
}

Replace Shared/MainLayout.razor with the following:

@inherits LayoutComponentBase

<div class="main">
  <div class="content px-4">
    @Body
  </div>
</div>

Replace Shared/NavMenu.razor with the following:

@code {
  private bool collapseNavMenu = true;

private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

private void ToggleNavMenu()
  {
    collapseNavMenu = !collapseNavMenu;
  }
}

Let us test our solution to make sure that our Blazor app knows how to talk to the backend Node. js Azure Functions API.

In the api directory terminal window, run the following command to run the Azure Functions project:

func start
To start the Blazor application, in the blazor-app directory, run
dotnet run
Point your browser to http://localhost:5000 and you should see this:
Untitled28.png
Feel free to test out the Add, Edit and Delete functionalities. Meantime, bear in mind that no validations have been added to this application as it is purposely made simple for learning purposes.

Routing of URLs may not work all the time. You need to supply a routes.json file located in your Blazor app’s wwwroot directory to provide the global rewrite rule so that URLs will always work. The wwwroot/routes.json file should look like this:

{
  "routes": [
    {
      "route": "/*",
      "serve": "/index.html",
      "statusCode": 200
    }
  ]
}

GitHub repo

Stop both the Azure Functions & Blazor applications.

Now that our application works, we are ready to do the most exciting part of this walk-through, which is to deploy our solution to Azure Static Web Apps through GitHub.

Edit the Models/Constants.cs file in blazor-app.

Change:
return "http://localhost:7071/";
To:
return "/";
This is necessary because the API and Blazor app will reside in the same web application. The Blazor app can access the API service through a simple relative address.

Let us upload our code into a GitHub repo. We need a suitable .gitignore file. The api directory already contains a .gitignore file. We do, however, need a .gitignore file in the blazor-app directory. Therefore, inside a terminal window in the blazor-app directory, execute the following command to create a suitable .gitignore file for our Blazor app.

Since the main repo directory will be parent directory of both api and blazor-app, delete the .git directory in the api folder.

dotnet new gitignore

Create a repository in GitHub and push your code into it.

Azure Static Web Apps

Let us use the new Azure service named “Azure Static Web Apps” announced by Microsoft at Build 2020.

Back in the Azure Portal, click on "+ Create a resource".
Untitled2.png
Enter 'static web app' in the filter field then choose 'Static Web App (preview)'.
Untitled29.png
Click on the blue Create button on the next page.
Untitled30.png
Enter details for your app and subsequently link into your GitHub account. This was my experience:
Untitled31.png
After you click on "Review + create", you will see the following confirmation page.
Untitled32.png
Click on Create. Once the Azure Static Web App is successfully provisioned, you will notice this message under Notifications.
Untitled33.png
Click on "Go to resource" to get to your application overview page.
Untitled8.png

When you click on the link beside "Workflow file", you will be led to a .yml workflow file in the GitHub repository.

When you click on Actions tab, you will notice that the workflow fails because it is missing values for app_location and app_artifact_location in the .yml file.

Before we set these values, we need to publish our Blazor application so that we only push release artifacts to Azure.

Edit the .github/workflows/??????.yml file and add the following two "Setup SDK and Build App" tasks to the .yml file before - name: Build And Deploy:

- name: Setup .NET SDK
  uses: actions/setup-dotnet@v1
  with:
    dotnet-version: 3.1.302
- name: Build App
  run: dotnet publish -c Release -o published

NOTE: Tab the above tasks until you eliminate validation errors in the online GitHub editor.

Next set the values for app_location and app_artifact_location to:app_locationpublished/wwwrootapp_artifact_location     published/wwwroot Commit your changes then click on the Actions tab. It is suggested that you make a pull on your source code in VS Code so that the latest changes involving the .yml file are updated.

Click on the workflow that was automatically triggered:

Untitled1.png
Click on 'Build and Deploy job' on the left-side.
Untitled2.png
After a short while, you should find that the job has completed successfully and that the application is successfully deployed to Azure.

Remember to do a pull on your code from the GitHub repository because there is a new and updated .yml created in Github.

Testing your deployed web app

Back in Azure, click on your web application’s endpoint:
Untitled3.png
There is a problem because products do not get loaded on the home page. This is because we did not add the environment variables that are required by the API app. Click on Configuration on the left-side navigation.
Untitled4.png
Use the following blade to add the missing environment variables. Copy these values from the api/local.settings.json file. Note that this file does not get pushed to your GitHub repo because it contains confidential information.
Untitled5.png

After adding the following environment variables, click on the Save button at the top:ENDPOINThttps://blazor-with-static-web-apps.documents.azure.com:443/ PARTITION_KEY{ \"kind\": \"Hash\", \"paths\": [\"/name\"] } KEYcAPYGRKQEvmh1234567890irFKXOS9PkjXcrInMpnFD12344567890nWkcT6D3n1234567890v3FMyZ8at6rfaw== DATABASECatalog CONTAINERProducts

If you run the application again you will find that it is working OK:

Untitled6.png
I hope you found this walkthrough useful.

References:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK