Host ASP.NET Core on Linux with Apache

dotnet add package Microsoft.AspNetCore.HttpOverrides

Configure a proxy server

Invoke the UseForwardedHeaders method at the top of Startup.Configure before calling other middleware. Configure the middleware to forward the X-Forwarded-For and X-Forwarded-Proto headers:

// using Microsoft.AspNetCore.HttpOverrides;

app.UseForwardedHeaders(new ForwardedHeadersOptions
{
    ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});

app.UseAuthentication();
// using System.Net;

services.Configure<ForwardedHeadersOptions>(options =>
{
    options.KnownProxies.Add(IPAddress.Parse("10.0.0.100"));
});

Forwarded Headers Middleware order

Forwarded Headers Middleware should run before other middleware. This ordering ensures that the middleware relying on forwarded headers information can consume the header values for processing. Forwarded Headers Middleware can run after diagnostics and error handling, but it must be run before calling UseHsts:

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseForwardedHeaders();
    app.UseHsts();
}
else
{
    app.UseDeveloperExceptionPage();
    app.UseForwardedHeaders();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

Alternatively, call UseForwardedHeaders before diagnostics:

using Microsoft.AspNetCore.HttpOverrides;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardedHeaders =
        ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});

var app = builder.Build();

app.UseForwardedHeaders();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

Forwarded Headers Middleware options

using System.Net;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
    options.ForwardLimit = 2;
    options.KnownProxies.Add(IPAddress.Parse("127.0.10.1"));
    options.ForwardedForHeaderName = "X-Forwarded-For-My-Custom-Header-Name";
});

var app = builder.Build();

app.UseForwardedHeaders();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

References
https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-apache?view=aspnetcore-5.0
https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-6.0

Self-hosted gRPC applications

Run your app as a Linux service with systemd

To configure your ASP.NET Core application to run as a Linux service (or daemon in Linux parlance), install the Microsoft.Extensions.Hosting.Systemd package from NuGet. Then add a call to UseSystemd to the CreateHostBuilder method in Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseSystemd() // Enable running as a Systemd service
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });
dotnet publish -c Release -r linux-x64 -o ./publish

/etc/systemd/system/myapp.service

[Unit]
Description=My gRPC Application

[Service]
Type=notify
ExecStart=/usr/sbin/myapp

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl status myapp
sudo systemctl start myapp.service
sudo systemctl enable myapp

References
https://docs.microsoft.com/en-us/dotnet/architecture/grpc-for-wcf-developers/self-hosted

Bind gRPC Service to specific port in ASP.NET Core

This works (server side) with Kestrel:

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
    webBuilder.ConfigureKestrel(options =>
    {
       options.Listen(IPAddress.Loopback, 5000);
       options.Listen(IPAddress.Loopback, 5005, configure => configure.UseHttps());
    });
    webBuilder.UseStartup<Startup>();
});

client side:

var httpHandler = new HttpClientHandler
 {
     ServerCertificateCustomValidationCallback =
     HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
 };  

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
                
using var channel = GrpcChannel.ForAddress("https://localhost:5005", new GrpcChannelOptions { HttpHandler = httpHandler } );
            
var client = new Greeter.GreeterClient(channel);

References
https://stackoverflow.com/questions/63827667/bind-grpc-services-to-specific-port-in-aspnetcore
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel/options?view=aspnetcore-5.0
https://andrewlock.net/5-ways-to-set-the-urls-for-an-aspnetcore-app/

Call insecure gRPC services with .NET Core client

// This switch must be set before creating the GrpcChannel/HttpClient.
AppContext.SetSwitch(
    "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

// The port number(5000) must match the port of the gRPC server.
var channel = GrpcChannel.ForAddress("http://localhost:5000");
var client = new Greet.GreeterClient(channel);

The System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport switch is only required for .NET Core 3.x. It does nothing in .NET 5 and isn’t required.

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(options =>
            {
                // Setup a HTTP/2 endpoint without TLS.
                options.ListenLocalhost(5000, o => o.Protocols = 
                    HttpProtocols.Http2);
            });
            webBuilder.UseStartup<Startup>();
        });

When an HTTP/2 endpoint is configured without TLS, the endpoint’s ListenOptions.Protocols must be set to HttpProtocols.Http2

References
https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-5.0#call-insecure-grpc-services-with-net-core-client
https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-5.0#unable-to-start-aspnet-core-grpc-app-on-macos

Call a gRPC service with an untrusted/invalid certificate in C#

var httpHandler = new HttpClientHandler();
// Return `true` to allow certificates that are untrusted/invalid
httpHandler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = httpHandler });
var client = new Greet.GreeterClient(channel);

References
https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-5.0#call-a-grpc-service-with-an-untrustedinvalid-certificate

gRPC services with C#

Add a .proto file to a C# app

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

C# Tooling support for .proto files

Server:

<PackageReference Include="Grpc.AspNetCore" Version="2.28.0" />

Client:

<PackageReference Include="Google.Protobuf" Version="3.11.4" />
<PackageReference Include="Grpc.Net.Client" Version="2.28.0" />
<PackageReference Include="Grpc.Tools" Version="2.28.1">

Generated C# assets

Server Side:

To ensure only the server assets are generated in a server project, the GrpcServices attribute is set to Server.

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>
public class GreeterService : Greeter.GreeterBase
{
    private readonly ILogger<GreeterService> _logger;
    public GreeterService(ILogger<GreeterService> logger)
    {
        _logger = logger;
    }

    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply
        {
            Message = "Hello " + request.Name
        });
    }
}

Client Side:

static async Task Main(string[] args)
{
    // The port number(5001) must match the port of the gRPC server.
    using var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var client =  new Greeter.GreeterClient(channel);
    var reply = await client.SayHelloAsync(
                      new HelloRequest { Name = "GreeterClient" });
    Console.WriteLine("Greeting: " + reply.Message);
    Console.WriteLine("Press any key to exit...");
    Console.ReadKey();
}

References
https://docs.microsoft.com/en-us/aspnet/core/grpc/basics?view=aspnetcore-5.0

Pass values (parameters) between XAML pages

1 – Using the query string
Navigating page:

page.NavigationService.Navigate(new Uri("/Views/Page.xaml?parameter=test", UriKind.Relative));

Destination page:

string parameter = string.Empty;
if (NavigationContext.QueryString.TryGetValue("parameter", out parameter)) {
    this.label.Text = parameter;
}

2 – Using NavigationEventArgs
Navigating page:

page.NavigationService.Navigate(new Uri("/Views/Page.xaml?parameter=test", UriKind.Relative));

// and ..

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    // NavigationEventArgs returns destination page
    Page destinationPage = e.Content as Page;
    if (destinationPage != null) {

        // Change property of destination page
        destinationPage.PublicProperty = "String or object..";
    }
}

Destination page:

// Just use the value of "PublicProperty"..

3 – Using Manual navigation
Navigating page:

page.NavigationService.Navigate(new Page("passing a string to the constructor"));

Destination page:

public Page(string value) {
    // Use the value in the constructor...
}

References
https://stackoverflow.com/questions/12444816/how-to-pass-values-parameters-between-xaml-pages

Working with WPF Frame using C# and XAML

<Window x:Class="FrameSample.Window1"  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    Title="Window1" Height="300" Width="300">  
    <Grid>          
        <TextBlock>Outside area of frame</TextBlock>  
        <Frame Source="Page1.xaml">              
        </Frame>  
    </Grid>  
</Window>

navigate to a URI in a WPF Frame

<Window x:Class="FrameSample.Window1"  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    Title="Window1" Height="300" Width="300">  
    <Grid>  
        <TextBlock>Outside area of frame</TextBlock>  
        <Frame Name="FrameWithinGrid" >  
        </Frame>  
        <Button Height="23" Margin="114,12,25,0"   
                Name="button1" VerticalAlignment="Top" Click="button1_Click">Navigate to C# Corner  
        </Button>  
    </Grid>  
</Window>
private void button1_Click(object sender, RoutedEventArgs e)  
{  
    FrameWithinGrid.Navigate(new System.Uri("Page1.xaml",  
             UriKind.RelativeOrAbsolute));  
}

navigate to an external website URL and opens the ASPX page within a Frame

FrameWithinGrid.Source = new Uri("http://www.c-sharpcorner.com/Default.aspx", UriKind.Absolute);

this code first creates a NavigationWindow and then sets its Source to a URI

NavigationWindow window = new NavigationWindow();  
Uri source = new Uri("http://www.c-sharpcorner.com/Default.aspx", UriKind.Absolute);  
window.Source = source; window.Show();
private void NavigationViewItemHome_OnClick(object sender, RoutedEventArgs e)
{
    FrameMain.Navigate(new PageItems());
}

References
https://www.c-sharpcorner.com/UploadFile/mahesh/using-xaml-frame-in-wpf857/
https://www.youtube.com/watch?v=YoZcAx_0rNM