Setting up Serilog in .NET 6 for ASP.NET

dotnet add package Serilog.AspNetCore

Select Providers:

https://github.com/serilog/serilog/wiki/Provided-Sinks

Program.cs

builder.Host.UseSerilog((ctx, lc) => lc
    .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
    .WriteTo.MongoDBBson("mongodb://localhost/interfaces","logs"));

Finally, clean up by removing the remaining configuration for the default logger, including the "Logging" section from appsettings.*.json files

References
https://github.com/serilog/serilog-aspnetcore
https://blog.datalust.co/using-serilog-in-net-6/
https://github.com/serilog/serilog-sinks-console
https://github.com/serilog/serilog-sinks-file
https://github.com/serilog/serilog-sinks-mongodb
https://github.com/saleem-mirza/serilog-sinks-sqlite

Set and Unset Local, User and System Wide Environment Variables in Linux

An environment variable can be in three types:
1. Local Environment Variable

a)

$ VAR1='TecMint is best Site for Linux Articles'
$ echo $VAR1
$ unset VAR1
$ echo $VAR1

b)

$ export VAR='TecMint is best Site for Linux Articles'
$ echo $VAR
$ VAR=
$ echo $VAR

c)

$ VAR2='TecMint is best Site for Linux Articles'
$ echo $VAR2
$ env -i bash
$ echo $VAR2

2. User Environment Variable

a) Modify .bashrc file in your home directory to export or set the environment variable you need to add. After that source the file, to make the changes take effect. Then you would see the variable (‘CD’ in my case), taking effect. This variable will be available every time you open a new terminal for this user, but not for remote login sessions.

$ vi .bashrc

Add the following line to .bashrc file at the bottom.

export CD='This is TecMint Home'

Now run the following command to take new changes and test it.

$ source .bashrc 
$ echo $CD

To remove this variable, just remove the following line in .bashrc file and re-source it:

b) To add a variable which will be available for remote login sessions (i.e. when you ssh to the user from remote system), modify .bash_profile file.

$ vi .bash_profile

Add the following line to .bash_profile file at the bottom.

export VAR2='This is TecMint Home'

When on sourcing this file, the variable will be available when you ssh to this user, but not on opening any new local terminal.

$ source .bash_profile 
$ echo $VAR2

Here, VAR2 is not initially available but, on doing ssh to user on localhost, the variable becomes available.

$ ssh tecmint@localhost
$ echo $VAR2

To remove this variable, just remove the line in .bash_profile file which you added, and re-source the file.

NOTE: These variables will be available every time you are logged in to current user but not for other users.

3. System wide Environment Variables

a) To add system wide no-login variable (i.e. one which is available for all users when any of them opens new terminal but not when any user of machine is remotely accessed) add the variable to /etc/bash.bashrc file.

export VAR='This is system-wide variable'

After that, source the file.

$ source /etc/bash.bashrc 

Now this variable will be available for every user when he opens any new terminal.

$ echo $VAR
$ sudo su
$ echo $VAR
$ su -
$ echo $VAR

Here, same variable is available for root user as well as normal user. You can verify this by logging in to other user.

b) If you want any environment variable to be available when any of the user on your machine is remotely logged in, but not on opening any new terminal on local machine, then you need to edit the file – '/etc/profile'.

export VAR1='This is system-wide variable for only remote sessions'

After adding the variable, just re-source the file. Then the variable would be available.

$ source /etc/profile
$ echo $VAR1

To remove this variable, remove the line from /etc/profile file and re-source it.

c) However, if you want to add any environment which you want to be available all throughout the system, on both remote login sessions as well as local sessions( i.e. opening a new terminal window) for all users, just export the variable in /etc/environment file.

export VAR12='I am available everywhere'

After that just source the file and the changes would take effect.

$ source /etc/environment
$ echo $VAR12
$ sudo su
$ echo $VAR12
$ exit
$ ssh localhost
$ echo $VAR12

Here, as we see the environment variable is available for normal user, root user, as well as on remote login session (here, to localhost).

To clear out this variable, just remove the entry in the /etc/environment file and re-source it or login again.

NOTE: Changes take effect when you source the file. But, if not then you might need to log out and log in again.

References
https://www.tecmint.com/set-unset-environment-variables-in-linux/

Calling JavaScript from .NET in Blazor

JavaScript should be added into either /Pages/_Host.cshtml in Server-side Blazor apps, or in wwwroot/index.html for Web Assembly Blazor apps.

BlazorApp.js

var BlazorApp = {
    helloWorld: function () {
        alert("Hello World");
    },
    hello: function (name) {
        alert("Hello " + name);
    },
    sayHi: function (name) {
        return "Hello " + name;
    }
};

add to _Host.cshtml :

<script src="~/scripts/BlazorApp.js"></script>

Index.razor

@page "/"
@inject IJSRuntime JsRuntime;

<div>
    Name : <input @bind-value="name"/>
</div>

<button @onclick="Hello">Hello</button>
<button @onclick="SayHi">Say Hi</button>

<p>@output</p>

@code
{
    private string name;
    private string output;

    protected override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
    // here blazor is ready to call javascript
            Console.WriteLine("Javascript is ready");
            JsRuntime.InvokeVoidAsync("BlazorApp.helloWorld");
        }

        return base.OnAfterRenderAsync(firstRender);
    }

    private async Task Hello()
    {
        await JsRuntime.InvokeVoidAsync("BlazorApp.hello", name);
    }

    private async Task SayHi()
    {
        output = await JsRuntime.InvokeAsync<string>("BlazorApp.sayHi", name);
    }
}

References
https://blazor-university.com/javascript-interop/calling-javascript-from-dotnet/
https://blazor-university.com/javascript-interop/calling-javascript-from-dotnet/updating-the-document-title/

Pre-compressed static files with ASP.NET Core

Install gzipper

npm i gzipper -D

Add module to scripts in your package.json and run compress command npm run compress

"scripts": {
   "gzipper": "gzipper",
   "compress": "gzipper compress ./src"
 }

or Use npx command.

"scripts": {
  "compress": "npx gzipper compress ./src"
}

Serve static files

var mimeTypeProvider = new FileExtensionContentTypeProvider();
 
app.UseStaticFiles(new StaticFileOptions
{
    OnPrepareResponse = context =>
    {
        var headers = context.Context.Response.Headers;
        var contentType = headers["Content-Type"];
 
        if (contentType != "application/x-gzip" && !context.File.Name.EndsWith(".gz"))
        {
            return;
        }
 
        var fileNameToTry = context.File.Name.Substring(0, context.File.Name.Length - 3);
 
        if (mimeTypeProvider.TryGetContentType(fileNameToTry, out var mimeType))
        {
            headers.Add("Content-Encoding", "gzip");
            headers["Content-Type"] = mimeType;
        }
    }
});

References
https://gunnarpeipman.com/aspnet-core-precompressed-files/

Response Caching in ASP.NET Core

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddResponseCaching();

var app = builder.Build();

app.UseHttpsRedirection();

// UseCors must be called before UseResponseCaching
//app.UseCors();

app.UseResponseCaching();
//Near the start
app.UseResponseCompression();

//ensure response compression is added before static files

app.UseStaticFiles();

References
https://docs.microsoft.com/en-us/aspnet/core/performance/caching/middleware?view=aspnetcore-6.0
https://docs.microsoft.com/en-us/aspnet/core/performance/caching/response?view=aspnetcore-6.0
https://stackoverflow.com/questions/46832723/net-core-response-compression-middleware-for-static-files

Integrating Tailwind CSS with Blazor

Creating An npm Project

Make sure that you have Node.js and npm CLI tools installed on your machine. Open the root directory of your Blazor Project.

npm init

Adding Tailwind CSS & Other Packages

npm install -D tailwindcss postcss autoprefixer postcss-cli

Configuring PostCSS

As mentioned earlier, POSTCSS will be responsible for transforming the tailwind.css to your own version. Create a new JS file in the root directory of the project and name it postcss.config.js.

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  }
}

Configuring Tailwind CSS

npx tailwindcss init

tailwind.config.js

module.exports = {
content: ["./**/*.html","./**/*.razor"],
theme: {
extend: {},
},
plugins: [],
}

In the wwwroot\css folder add a new CSS file and name it app.css. ( You could name it anything). This is what POSTCSS will use to generate your site’s CSS resource. Here we will be adding imports from the tailwind CSS library.

@tailwind base;
@tailwind components;
@tailwind utilities;

Building CSS With PostCSS CLI

Now, we need to build a script that can automate the postcss processing. Add the highlighted lines to your package.json file. What this script/command does is simple – It takes in app.css as the input and generates an app.min.css file in the same path. The newly generated file contains the complete tailwindcss content.

{
  "name": "blazorwithtailwindcss",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "buildcss": "postcss wwwroot/css/app.css -o wwwroot/css/app.min.css"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "autoprefixer": "^10.2.4",
    "postcss": "^8.2.6",
    "postcss-cli": "^8.3.1",
    "tailwindcss": "^2.0.3"
  }
}
npm run buildcss

This would have generated an app.min.css file in your root directory. You can notice that all the TailwindCSS styles are generated here.

Removing Unused CSS (See Configuring Tailwind CSS instead because purge is obsolete in version 3)

Open up your tailwind.config.js and modify it to match the following.

const colors = require('tailwindcss/colors');
module.exports = {
    purge: {
        enabled: true,
        content: [
            './**/*.html',
            './**/*.razor'
        ],
    },
    darkMode: false, 
    variants: {
        extend: {},
    },
    plugins: [],
}

PS, Make sure that you run the npm run buildcss command every time you make changes in any of the related js or CSS files. (In the next section, we will discuss post-build events that could automate running the npm command every time you build or rebuild the project.)

Configuring Post Build Events

Open your solution file and paste in the following highlighted snippet. This ensures that the dotnet build system runs the npm command once the project is built.

<Project Sdk="Microsoft.NET.Sdk.Web">
  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <Exec Command="npm run buildcss" />
  </Target>
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
</Project>

Adding The Stylesheet Reference

Finally, add the referend of the generated CSS file to your Blazor application. Since we have Server Project, you would probably want to add the following snippet to the _Host.cshml file. If you are on a WebAssembly project, Index.html is where you need to add the following.

<link href="~/css/app.min.css" rel="stylesheet" />

Optimizing for Production

npm install -D cssnano

If you’re using Tailwind CLI, you can minify your CSS by adding the --minify flag:

npx tailwindcss -o build.css --minify

If you’ve installed Tailwind as a PostCSS plugin, add cssnano to the end of your plugin list:

postcss.config.js

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
    cssnano: {preset: 'default'},
  }
}

References
https://codewithmukesh.com/blog/integrating-tailwind-css-with-blazor/
https://tailwindcss.com/docs/installation/using-postcss
https://tailwindcss.com/docs/optimizing-for-production

Response Compression in ASP.NET Core

The following code shows how to enable the Response Compression Middleware for default MIME types and compression providers (Brotli and Gzip):

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddResponseCompression();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseResponseCompression();
    }
}

Customize

public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression(options =>
    {
        options.Providers.Add<BrotliCompressionProvider>();
        options.Providers.Add<GzipCompressionProvider>();
        options.Providers.Add<CustomCompressionProvider>();
        options.MimeTypes = 
            ResponseCompressionDefaults.MimeTypes.Concat(
                new[] { "image/svg+xml" });
    });
}

References
https://docs.microsoft.com/en-us/aspnet/core/performance/response-compression?view=aspnetcore-6.0

Run a Wine installed application from command line

If you are using wine installed from the repositories then wine related files is stored in a folder called ~/.wine

Windows programs are stored within this folder – for example :

~/.wine/drive_c/program_files/Internet Explorer

You can start a windows program in a similar way as from a command line in windows but prefix it with wine

wine "c:\program files\internet explorer\iexplore.exe"

References
https://askubuntu.com/questions/65487/invoke-a-wine-installed-application-from-command-line

Configuring IP Networking with ifcfg Files

Configuring an Interface with Static Network Settings Using ifcfg Files
For example, to configure an interface with static network settings using ifcfg files, for an interface with the name enp1s0, create a file with the name ifcfg-enp1s0 in the /etc/sysconfig/network-scripts/ directory, that contains:

DEVICE=enp1s0
BOOTPROTO=none
ONBOOT=yes
PREFIX=24
IPADDR=10.0.1.27

Configuring an Interface with Dynamic Network Settings Using ifcfg Files

DEVICE=em1
BOOTPROTO=dhcp
ONBOOT=yes

References
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/networking_guide/sec-configuring_ip_networking_with_ifcg_files