Building interactive console app with Web SDK - c#

I'm going my first steps with .NET Core 3.1 by trying to build an agent/client for the Hyperledger Indy project. They provide a dotnet framework.
The used SDK is Microsoft.NET.Sdk.Web.
Heres my simple application:
Program.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace issuer
{
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("====== Start ======"); // Printed to console
CreateHostBuilder(args).Build().Run();
var input = System.Console.ReadKey(); // Never reached
System.Console.Write(" --- You pressed " + input.Key.ToString());
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
And Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace issuer
{
class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddAriesFramework(builder =>
{
builder.RegisterAgent(options =>
{
options.EndpointUri = "http://localhost:5000";
// ...
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAriesFramework();
}
}
}
When I start it, I have a passive application listening on localhost:5000 for incoming requests from other clients.
What I want to achieve is an interactive console (instead of a web frontend) to actively initiate communication with other clients. I think a good first step would be to get a Console.ReadKey() after everything has been setup.
Is that even possible?

Related

Kestrel Port:0, how to detect autoselected port

I want Kestrel to automatically select a port. My Program.cs looks like this:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
var builder = webBuilder.UseStartup<Startup>();
if (args.Contains("--automatic-port-selection"))
{
builder.UseUrls("http://127.0.0.1:0");
}
});
I looked at https://learn.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-3.1 to understand how I can detect the selected port. But I would actually like to obtain the port when the software starts.
I tried with the following:
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services) { }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
var serverAddressesFeature =
app.ServerFeatures.Get<IServerAddressesFeature>();
if (serverAddressesFeature != null)
{
foreach (var address in serverAddressesFeature.Addresses)
{
int port = int.Parse(address.Split(':').Last());
Console.Out.WriteLine("Port:" + port);
}
Console.Out.Flush();
}
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); }); });
}
}
You can see this basically just a aspnet core application created from the Visual Studio template.
The issue here is that it will always only writes 0. It seems it would only be able to give the correct port number when I read serverAddressesFeature. Addresses` when handling a request. How can I get the used port number when starting up the server?
EDIT
This seems to be relevant: https://github.com/aspnet/Hosting/issues/1390
As far as I know, the reason why you always get the 0 port number is the application doesn't started completely.
When the application started, it will call the startup.cs configure method firstly. This time Kestrel know that its port is 0.
After that it will find the auto free port.
So if you want to get the port which your application is using now. You could write a method which will fired after the application stated completely to log the right port.
More details, you could refer to below codes:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime lifetime , IServiceProvider serviceProvider){
//other codes ....
lifetime.ApplicationStarted.Register(
() => LogAddresses(app.ServerFeatures));
}
static void LogAddresses(IFeatureCollection features)
{
var addressFeature = features.Get<IServerAddressesFeature>();
if (addressFeature != null)
{
foreach (var address in addressFeature.Addresses)
{
int port = int.Parse(address.Split(':').Last());
Console.Out.WriteLine("Port:" + port);
}
}
}
Result:

ASP.NET Core Testing - No method 'public static IHostBuilder CreateHostBuilder(string[] args)

I'm trying to setup my application in tests and use in Startup's Configure method context.Database.EnsureCreated() and expecting Sqlite file appear in Test's bin folder
Here's my code:
using Microsoft.AspNetCore.Mvc.Testing;
using System.Threading.Tasks;
using Xunit;
namespace MyApp.Tests
{
public class UnitTest1 : IClassFixture<CustomWebApplicationFactory<FakeStartup>>
{
private readonly CustomWebApplicationFactory<FakeStartup> _factory;
public UnitTest1(CustomWebApplicationFactory<FakeStartup> factory)
{
_factory = factory;
}
[Fact]
public async Task Test1()
{
// Arrange
var client = _factory.CreateClient();
// Act
var response = await client.GetAsync("https://localhost:5001/");
// Assert
response.EnsureSuccessStatusCode(); // Status Code 200-299
Assert.Equal("text/html; charset=utf-8", response.Content.Headers.ContentType.ToString());
}
}
}
Which is using WebAppFactory:
using MyApp.Tests;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseStartup<FakeStartup>();
}
}
Where FakeStartup is:
using MyApp.Database;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace MyApp.Tests
{
public class FakeStartup
{
public FakeStartup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<Context>(x => x.UseSqlite($"filename={Guid.NewGuid():N}.db"));
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Test API", Version = "v1" });
});
}
}
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env, Context context)
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Test API v1");
c.RoutePrefix = string.Empty;
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseAuthentication();
app.UseCors(x =>
{
x.AllowAnyOrigin();
x.AllowAnyMethod();
x.AllowAnyHeader();
});
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
Here's problem
Message:
System.InvalidOperationException : No method 'public static IHostBuilder CreateHostBuilder(string[] args)' or 'public static IWebHostBuilder CreateWebHostBuilder(string[] args)' found on 'AutoGeneratedProgram'. Alternatively, WebApplicationFactory`1 can be extended and 'CreateHostBuilder' or 'CreateWebHostBuilder' can be overridden to provide your own instance.
Stack Trace:
WebApplicationFactory`1.CreateWebHostBuilder()
WebApplicationFactory`1.EnsureServer()
WebApplicationFactory`1.CreateDefaultClient(DelegatingHandler[] handlers)
WebApplicationFactory`1.CreateDefaultClient(Uri baseAddress, DelegatingHandler[] handlers)
WebApplicationFactory`1.CreateClient(WebApplicationFactoryClientOptions options)
WebApplicationFactory`1.CreateClient()
UnitTest1.Test1() line 20
--- End of stack trace from previous location where exception was thrown
What may be causing this? thanks in advance
Updated with comment from CoreyP:
If you are getting this error and you're on .NET 6.0, you might need to update the Microsoft.AspNetCore.Mvc.Testing package, see this question: Integration test for ASP.NET Core 6 web API throws System.InvalidOperationException
Solution:
Create CustomWebApplicationFactory this way
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override IHostBuilder CreateHostBuilder()
{
var builder = Host.CreateDefaultBuilder()
.ConfigureWebHostDefaults(x =>
{
x.UseStartup<FakeStartup>().UseTestServer();
});
return builder;
}
}
Found here:
https://thecodebuzz.com/no-method-public-static-ihostbuilder-createhostbuilder/
I was getting this error because I had not followed the MS prerequisites closely enough. In my case I had not updated the Project SDK in the test csproj file. It needs to be <Project Sdk="Microsoft.NET.Sdk.Web"> (note the '.Web' on the end).

Simple routing with web server in netcore console app

I'm having trouble getting routing to work with kestrel.
I can't find any good tutorials on how to implement this inside of a netcore console app.
I want to build a simple web server that will have 2-3 end-points that I can access.
public class WebServer
{
public static void Init()
{
IWebHostBuilder builder = CreateWebHostBuilder(null);
IWebHost host = builder.Build();
host.Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.Build();
return WebHost.CreateDefaultBuilder(args)
.UseUrls("http://*:5000")
.UseConfiguration(config)
.UseStartup<Startup>();
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
// ????
}
public void Configure(IApplicationBuilder app)
{
// ????
}
}
}
File > New Project > Empty ASP.NET Core application.
In order to run it in a console application, make sure you select the name of you project in the "Run" dropdown in Visual Studio.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
namespace WebApplication7
{
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMvc();
}
}
public class MyEndpoint : Controller
{
[Route("")]
public IActionResult Get()
{
return new OkResult();
}
}
}

Windows Authentication in .net Core 2.1 application

I've got a .net Core 2.1 MVC application that hosts an Angular 6 application. I am using Visual Studio 2017 15.7.3. I'm trying to set up Windows Authentication but am having issues. I've followed the documentation and my Program.cs and Startup.cs are below:
Program.cs:
using Microsoft.AspNetCore.Server.HttpSys;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using NLog.Web;
using System;
namespace CoreIV_Admin_Core
{
public class Program
{
public static void Main(string[] args)
{
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
try
{
logger.Debug("init main");
CreateWebHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
//NLog: catch setup errors
logger.Error(ex, "Stopped program because of exception");
throw;
}
finally
{
// Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux)
NLog.LogManager.Shutdown();
}
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
//.UseHttpSys(options =>
//{
// options.Authentication.Schemes = AuthenticationSchemes.NTLM | AuthenticationSchemes.Negotiate;
// options.Authentication.AllowAnonymous = false;
//})
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information);
})
.UseNLog(); // NLog: setup NLog for Dependency injection
}
}
Startup.cs:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Server.HttpSys;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace CoreIV_Admin_Core
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration
{
get;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddAuthentication(HttpSysDefaults.AuthenticationScheme);
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true
});
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseHttpContext();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new
{
controller = "Home",
action = "Index"
});
});
}
}
}
If I uncomment the .UseHttpSys section in program.cs and press play in Visual Studio to debug, it almost immediately stops the debug session and the following error is in the Event log:
"Application 'MACHINE/WEBROOT/APPHOST/myapp' with physical root
'C:\Users\me\myapp' created process with commandline 'C:\Program Files
(x86)\Microsoft Visual
Studio\2017\Professional\Common7\IDE\Extensions\Microsoft\Web
Tools\ProjectSystem\VSIISExeLauncher.exe -argFile
"C:\Users\me\AppData\Local\Temp\tmpFCA6.tmp"' but failed to listen on
the given port '28353'".
If I try with .UseHttpSys commented out, the debugging works but I am unable to be properly authenticated.
Camilo thanks for your comment, it helped point me in the right direction. I removed the UseHttpSys section from program.cs and added .UseIISIntegration(). I also changed services.AddAuthentication(HttpSysDefaults.AuthenticationScheme) to services.AddAuthentication(IISDefaults.AuthenticationScheme) in my startup.cs. I then ticked Enable Windows Authentication in the Debug section of the project properties and chose "IIS Express" for the Launch and it all worked. I'm new to .net core with windows authentication so I don't know if I got it all set up exactly correct but it works and hopefully, this post helps point others in the right direction.

How to write Web API, Self hosting, Windows Service

I can't make sense of this. I keep getting an exception
Exception thrown: 'System.AggregateException' in mscorlib.dll, with a message: "Internal error in the expression evaluator." and no Inner Exception!
I can't find where the error is coming from other than when the server is being started, this Line: _server.OpenAsync().Wait();
I thought it would be a good idea to use DI and I though my problems were coming from Autofac but as you can see I have remarked it all out and I'm still getting this exception. The pertinent code is in the last method. Please take a look at the code:
using System;
using System.ServiceProcess;
using System.Threading;
using System.Reflection;
using ServicesUtilities;
using System.Web.Http;
using System.Web.Http.SelfHost;
using Autofac;
using Autofac.Integration.WebApi;
namespace SeviceMerge
{
partial class MergeService : ServiceBase, IQuasiServiceBase
{
private HttpSelfHostServer _server;
private bool _runOnStart;
public InjectionService()
{
InitializeComponent();
_runOnStart = Config.Run;
}
protected override void OnStart(string[] args)
{
WebApiListener();
}
protected override void OnStop()
{
_runOnStart = false;
_server.CloseAsync().Wait();
_server.Dispose();
}
void IQuasiServiceBase.OnStart(string[] args, bool isBatchMode)
{
OnStart(args);
}
void IQuasiServiceBase.OnStop()
{
OnStop();
}
bool IQuasiServiceBase.PauseCheck()
{
return false;
}
bool IQuasiServiceBase.StopCheck()
{
return false;
}
private void WebApiListener()
{
var config = new HttpSelfHostConfiguration("http://localhost:26675");
config.Routes.MapHttpRoute(
"Presents",
"api/{controler}/{id}",
new { id = RouteParameter.Optional }
);
_server = new HttpSelfHostServer(config);
_server.OpenAsync().Wait();
}
}
}
Here's my Controller code:
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace AEMtoParmedInject.Controllers
{
public class AemToParmedMergeController : ApiController
{
private ILogger _logger;
private IWorkTheMachine _worker;
public AemToParmedMergeController()
{
_logger = new Logger();
//_worker = worker;
}
[HttpGet]
public HttpResponseMessage Index()
{
return new HttpResponseMessage()
{
Content = new StringContent(
"<html>" +
"<head>" +
"</head>" +
"<body>" +
"<p>By click on the button below you are signaling the AEM to Parmed Merge service to perform it task</p>" +
"<form>" +
"<input type='submit' action='Index' value='Integrate AEM Content'>" +
"</form" +
"</body>" +
"</html>",
Encoding.UTF8,
"text/html"
)
};
}
}
}
Everything in .net core starts with a simple console application and here I give you 5 simple steps that you can use in order to self-host a Web API / Web APP in a worker service and host the final .exe in your windows services, even you can host in Linux Systemd with little changes.
I've created a very simple Worker-Service Web-API template that exists in my GitHub profile. The following steps work 100%, but if you faced any problem, you can ask here or you can clone the template from my GitHub and use it and read the documentation there.
Do the following steps:
Create a .net core console application.
Install packages "Microsoft.AspNetCore.App" and "Microsoft.Extensions.Hosting.WindowsServices" using NuGet.
Create a Worker.cs file which will handle your worker service. put the following codes inside:
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace WorkerServiceWebAppTemplate
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
}
Create a Startup.cs file which will handle your web host and create a simple GET API in the root address and responsible to show a simple message. you can extend it. put the following lines of codes in your Startup.cs file:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
namespace WorkerServiceWebAppTemplate
{
public class Startup
{
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseHsts();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Hello World!");
});
});
}
}
}
Finally to start worker service and host your Web API inside your worker service, and also in order yo allow your published .exe file to be able to be host in windows services, use the following codes in your Program.cs:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace WorkerServiceWebAppTemplate
{
class Program
{
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
private static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices((hostBuilderContext, services) =>
{
services.AddHostedService<Worker>();
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
}

Categories