Search  
Always will be ready notify the world about expectations as easy as possible: job change page
Jul 17

Quickly convert a console app into a single-point web app in C#

Quickly convert a console app into a single-point web app in C#
Author:
Source:
Views:
1390

I found myself in need of quickly converting my C# console application into a web application, primarily, I was asked to expose our console app’s logic through an API with very little time to manage it.

This article will cover on how I eventually exposed our console app on port 5005 of the localhost, our internal network, for bonus, I’ll throw in the containerised Docker version of the app, too!

Let’s start with a simple console app:

mkdir ConsoleToWeb
cd ConsoleToWeb
dotnet new console
dotnet run

Create console app

The source of this app will look something like this:

using System;

namespace ConsoleToWeb
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!"); // => This needs to be converted into a web app
        }
    }
}

To convert this into a ASP.NET Web App, we need to do 3 things:

  • Convert the console project into a web project (csproj)
  • Introduce a Generic Host to host our Web App
  • Rewrite the Main method to run our WebHost

Converting a console csproj into a web csproj

Our original console csproj looks something like the following:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

</Project>

For a web project, we need to target a different SDK; Microsoft.NET.Sdk.Web

Also, the OutputType does not need to be an Exe.

Modify the csproj file to look like this:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
 
</Project>

Introduce a Generic Host to host our web app

The IHost class is a class designed for the new app hosting system of .NET. It handles core functionality like Dependency Injection, Configuration, Logging, …

We will use the HostBuilder to create a vanilla Web Host that will have 1 Run method which will call the code from our Console app.

Add the following code to your Program.cs file:

public static IHostBuilder CreateHostBuilder(string[] args) =>
  Host.CreateDefaultBuilder(args)
      .ConfigureWebHost(
          webHost => webHost
              .UseKestrel(kestrelOptions => { kestrelOptions.ListenAnyIP(5005); })
              .Configure(app => app
                  .Run(
                      async context =>
                      {
                          await context.Response.WriteAsync("Hello World!");
                      }
                  )));

Don’t forget about the necessary using statements:

using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;

Rewrite the Main method to run our WebHost

We’re almost there, we just need to tie it all up in the Main method of the Program.cs:

static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

Let’s examine the full working example:

using System;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;

namespace ConsoleToWeb
{
    class Program
    {
        static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHost(
                    webHost => webHost
                        .UseKestrel(kestrelOptions => { kestrelOptions.ListenAnyIP(5005); })
                        .Configure(app => app
                            .Run(
                                async context =>
                                {
                                    await context.Response.WriteAsync("Hello World!");
                                }
                            )));
    }
}

That is it! Now you can run the app using dotnet run and see the result in your browser at http://localhost:5005

ConsoleToWeb

Web localhost:5005

Dockerize it!

Since we’re on a roll here, let’s quickly Dockerize this app so that we can package and ship it.

Add a Dockerfile to the root of the ConsoleToWeb directory.

touch Dockerfile

The contents of the Dockerfile should be something like:

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build-env
WORKDIR /app

COPY *.csproj ./
RUN dotnet restore

COPY . ./
RUN dotnet publish -c Release -o out

FROM mcr.microsoft.com/dotnet/aspnet:5.0
WORKDIR /app
COPY --from=build-env /app/out .
EXPOSE 5005
ENTRYPOINT ["dotnet", "ConsoleToWeb.dll"]

Now that is done, we can build our image locally and tag it:

docker build . -t consoletoweb

docker build consoletoweb

After the Docker image is built, we can run the image in a container using docker run -p 5005:5005 consoletoweb

Run the Docker image

And the effect should be the same on http://localhost:5005

Web localhost:5005

Quickly adding services using FuncR

Still going strong now, let’s introduce a service, add some small implementation and have that service injected into our Web App using Dependency Injection!

Introduce the IFooService:

interface IFooService
{
    string Foo(int numberOfFoos);
}

Now, we could implement this service using a new class, or we could write just the code we need for string Foo(int numberOfFoos) method to work using a function with FuncR.

FuncR logo

FuncR is a small .NET standard library that enables you to register functions against interfaces in C#.

First we need to add FuncR to the project

dotnet add package FuncR

To use FuncR, add a ConfigureServices method to your Program.cs file:

 1: private static void ConfigureServices(IServiceCollection services)
 2: {
 3:     services.AddScopedFunction<IFooService>
 4:         (nameof(IFooService.Foo))
 5:         .Runs<int, string>(numberOfFoos =>
 6:         {
 7:             var foos = Enumerable.Range(1, numberOfFoos).Select(n => "Foo");
 8:             return $"{String.Join(", ", foos)}";
 9:         });
10: }

In this ConfigureServices method, you can register any service agains the DI container of ASP.NET, more concretely, the code above registers a Proxy for IFooService and hooks up the string Foo(int numberOfFoos) method to the function that is provided on lines 7 and 8, cool huh!?

Now we just need to tell the HostBuilder to use this method to configure its services and we’re ready to have that service injected:

using System;
using Microsoft.Extensions.Hosting;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using FuncR;
using System.Linq;

namespace ConsoleToWeb
{
    interface IFooService
    {
        string Foo(int numberOfFoos);
    }

    class Program
    {
        static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        private static void ConfigureServices(IServiceCollection services)
        {
            services.AddScopedFunction<IFooService>
                (nameof(IFooService.Foo))
                .Runs<int, string>(numberOfFoos =>
                {
                    var foos = Enumerable.Range(1, numberOfFoos).Select(n => "Foo");
                    return $"{String.Join(", ", foos)}";
                });
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHost(
                    webHost => webHost
                        .UseKestrel(kestrelOptions => { kestrelOptions.ListenAnyIP(5005); })
                        .ConfigureServices(ConfigureServices)
                        .Configure(app => app
                            .Run(
                                async context =>
                                {
                                    var numberOfFoos = 5;
                                    // Resolve IFooService here
                                    var fooService = context.RequestServices.GetRequiredService<IFooService>();
                                    await context.Response.WriteAsync(fooService.Foo(numberOfFoos));
                                }
                            )));
    }
}

If we now dotnet run and navigate to http://localhost:5005 we finally see some different output:

Web app

That’s a lot of Foo’s.

And naturally, the Docker example also still works after a rebuild and run:

docker build . --no-cache -t consoletoweb
docker run -p 5005:5005 consoletoweb

Wrapping up

In this article we saw how easy and quickly a console app can be converted into a ASP.NET web app, and we got an introduction to FuncR to speed up our service implementations. The full code, and more is available through GitHub:

Source: https://github.com/merken/ConsoleToWeb

Similar
17 декабря 2014 г.
Краткий обзор .NET Core, как это соотносится с .NET Framework и что это все означает для кросс-платформенной разработки и разработки с открытым кодом. Взгляд назад – мотивация для .NET Core Прежде всего, давайте оглянемся назад, чтобы понять, как платформа .NET...
Sep 26
Author: Malinda Wimalasena
This article is mainly about implementing a simple web notification system in a Vue application using basic functionalities provided by SignalR. Here, we will explore how we can configure SignalR in a .NET Core application and how to integrate SignalR...
Jan 25
Author: Dev·edium
A deep dive into Ahead-of-Time (AOT) compilation for optimized performance What is it? Traditionally, .NET languages like C# use Just-in-Time (JIT) compilation. In this process, the source code is initially compiled into Common Intermediate Language (CIL) code, a platform-agnostic code...
Nov 25, 2021
Author: Jay Krishna Reddy
Today, we are going to cover uploading and downloading multiple files using ASP.Net Core 5.0 Web API by a simple process. Note: This same technique works in .Net Core 3.1 and .Net Core 2.1 as well. Begin with creating an...
Send message
Type
Email
Your name
*Message