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.

Companion video: https://youtu.be/DsGNAx_kJ3g

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, December 11, 2020

HttpRepl essentials

HttpRepl is a light-weight utility from Microsoft used to test REST APIs and view their results. It can run on any O/S that .NET Core (or .NET 5 and later) runs on. HttpRepl is an open-source project on GitHub at https://github.com/dotnet/HttpRepl. It is an alternative to Postman.

Companion video: https://youtu.be/RLdnXN4OAPg

To install the HttpRepl, run the following command from within a terminal window:

dotnet tool install -g Microsoft.dotnet-httprepl

To update HttpRepl to the latest version, enter the following command:

dotnet tool update -g Microsoft.dotnet-httprepl

On Windows, this tool gets installed at:

%USERPROFILE%\.dotnet\tools\httprepl.exe

Let us test  HttpRepl with a REST API service located at https://api4all.azurewebsites.net. From within a terminal window, enter the following command:

httprepl https://api4all.azurewebsites.net

HttpRepl

You are advised to associate the HttpRepl utility with a text editor so that you can compose your POST & PUT requests. The below terminal window command will associate Notepad.exe (in Windows) with HttpRepl:

pref set editor.command.default "C:\Windows\system32\notepad.exe"

pref set editor.command.default

Enter dir (or ls) :

HttpRepl allows you to navigate through your API tree. In our current service, all services are under /api. Therefore, change directory to api then check the contents with:

cd api
dir
dir

Now, let’s checkout Students. Therefore, change to the Students location and inspect what is available there with:

cd Students
dir

dir

NOTE: HttpRepl is case-sensitive. This means that “cd students” will generate an error.

This shows us that we can do GET, POST, PUT and DELETE. Entering the id is necessary for DELETE, PUT and retrieving one Student (GET by id).

Let us retrieve all Students data by entering GET:

GET

How about we try POST. Just enter POST and the editor we configured earlier on pops up with a JSON template:
POST

Enter some data then simply save & close your editor. I entered the following then saved & closed the editor. 
POST

This was the output I got:

POST
Enter GET to ensure that data was indeed inserted:
GET

Let’s do a GET by id by entering the following:

GET A00555555

This is the result:
GET by id

How about we do an update. Enter:

PUT A00555555

This opens the editor again. However, the existing data is not displayed. This is what I got:
PUT

I entered data as follows, using the same ID as before, and changing only school to nursing. I then saved the document before closing it:


PUT

This is the output I received:

PUT result

I then did a GET to see all students:

GET all result

Finally, let us delete our student by entering:

DELETE A00555555

You should see the following response:

DELETE by id

Now when we do a GET, there will be no student with ID = A00555555:
GET all

To exit out of HttpRepl, simply type exit.

I hope you found HttpRepl a useful utility.





Use Service Reference in Visual Studio 2019 to consume a REST API with OpenAPI description from a C# console app

In this tutorial I will show you how easy it is to consume a REST API Service using the ‘Service Reference …’ feature in Visual Studio 2019. I will consume a simple service located at https://api4all.azurewebsites.net/

Companion video: https://youtu.be/XqIniGHpcEc

Source Code: https://github.com/medhatelmasry/ConsumeOpenAPI.git

Prerequisites:

  1. You need to have Visual Studio 2019 installed on your Windows computer. In my case, I am using: Microsoft Visual Studio Community 2019 Version 16.8.2
  2. The REST API you are consuming needs to have an OpenAPI programming language-agnostic interface description for REST APIs.
Point your browser to https://api4all.azurewebsites.net/. The landing page is an OpenAPI page that has the following heading:

OpenAPI Endpoint







Copy the link to the swagger.json file in the top-left corner and park it in Notepad.exe for later use.

Let us create a simple “Console App (.NET Core)” application in Visual Studio 2019 to consume the REST API service.
Console App (.NET Core)

Once your console app is created, Add >> Service Reference …

Add >> Service Reference
Choose OpenAPI, then click Next.
OpenAPI

Select URL then paste into the textbox the link to the swagger.json file that you parked in Notepad.exe. Click on Finish.
OpenAPI URL

A "Service reference configuration progress" dialog will appear. Click Close when it is done.
Service reference configuration progress

Looking at "Solution Explorer", you will find a folder named OpenAPI with swagger.json contained inside it.
swagger.json


Build (or compile) your application so that the proxy class that gets created is visible in your application's environment.

If you open your obj folder in File Explorer, you will find that a proxy class named swaggerClient.cs was auto-magically created for you.
swaggerClient.cs

Feel free to open this file in a text editor to have a peek at its contents. Alternatively, you can click on ... in your Service Dependencies dialog and select "View generated code".
View generated code


These are the CRUD methods that are provided  by the swaggerClient object:

Method nameHTTP VerbPurpose
StudentsAllAsyncGETretrieve all students
Students2AsyncGETget student by id
StudentsAsyncPOSTadd a student
Students3AsyncPUTupdate student data
Students4AsyncDELETEdelete student by id

It is obvious that the swaggerClient method names are not very intuitive. The above table will help you figure out the translation.

Add the following instance variable, that represents the BASE_URL, to the Program.cs class:

const string BASE_URL = "https://api4all.azurewebsites.net";

Add the following displayStudents() method to Program.cs:

private static async Task displayStudents() {
  using (var httpClient = new HttpClient()) {
    var client = new swaggerClient(BASE_URL, httpClient);
    var items = await client.StudentsAllAsync();
    foreach (var i in items) {
      Console.WriteLine($"{i.StudentId}\t{i.FirstName}\t{i.LastName}\t{i.School}");
    }
  }
}

Replace your Main() method in Program.cs with the following code:

static async Task Main(string[] args) {
   await displayStudents();
}

OpenAPI output

Below are the other methods that complete all other CRUD operations:

private static async Task getStudentById(string id) {
  using (var httpClient = new HttpClient()) {
    var client = new swaggerClient(BASE_URL, httpClient);
    var item = await client.Students2Async(id);
    Console.WriteLine($"{item.StudentId}\t{item.FirstName}\t{item.LastName}\t{item.School}");
  }
}

private static async Task addStudent() {
  using (var httpClient = new HttpClient()) {
    var client = new swaggerClient(BASE_URL, httpClient);
    var student = new Student() {
        StudentId = "A00777776",
        FirstName = "Joe",
        LastName = "Roy",
        School = "Forestry"
    };

    try {
      var response = await client.StudentsAsync(student);
    } catch (ApiException apiEx) {
      // not really error because server returns HTTP Status Code 201
      Console.WriteLine(apiEx.ToString());
    }
  }
}

private static async Task updateStudentById(string id) {
  using (var httpClient = new HttpClient()) {
    var client = new swaggerClient(BASE_URL, httpClient);
    var student = new Student() {
        StudentId = id,
        FirstName = "Pam",
        LastName = "Day",
        School = "Nursing"
    };
    await client.Students3Async(id, student);
  }
}

private static async Task deleteStudentById(string id) {
    using (var httpClient = new HttpClient()) {
      var client = new swaggerClient(BASE_URL, httpClient);
      var item = await client.Students4Async(id);
    }
}

I hope you appreciate how easy and painless it is to consume an OpenAPI REST service using Visual Studio 2019.