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/

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

Use the Angular project template with ASP.NET Core

Create a new app

dotnet new angular -o my-new-app
cd my-new-app

Add pages, images, styles, modules, etc.

The ClientApp directory contains a standard Angular CLI app

Run ng commands

remove package-lock.json then :

cd ClientApp
npm install

Install npm packages

cd ClientApp
npm install --save <package_name>

Run “ng serve” independently

cd ClientApp
npm start

Use npm start to launch the Angular CLI development server, not ng serve, so that the configuration in package.json is respected. To pass additional parameters to the Angular CLI server, add them to the relevant scripts line in your package.json file.

In your Startup class, replace the spa.UseAngularCliServer invocation with the following:

spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");

References
https://docs.microsoft.com/en-us/aspnet/core/client-side/spa/angular?view=aspnetcore-6.0&tabs=visual-studio

Trust the ASP.NET Core HTTPS development certificate on Fedora

sudo dnf install nss-tools

Setup Firefox

echo 'pref("general.config.filename", "firefox.cfg");
pref("general.config.obscure_value", 0);' > ./autoconfig.js

echo '//Enable policies.json
lockPref("browser.policies.perUserDir", false);' > firefox.cfg

echo "{
    \"policies\": {
        \"Certificates\": {
            \"Install\": [
            	\"aspnetcore-localhost-https.crt\"
            ]
        }
    }
}" > policies.json

dotnet dev-certs https -ep localhost.crt --format PEM

sudo mv autoconfig.js /usr/lib64/firefox/
sudo mv firefox.cfg /usr/lib64/firefox/
sudo mv policies.json /usr/lib64/firefox/distribution/
mkdir -p ~/.mozilla/certificates
cp localhost.crt ~/.mozilla/certificates/aspnetcore-localhost-https.crt

Trust Edge/Chrome

certutil -d sql:$HOME/.pki/nssdb -A -t "P,," -n localhost -i ./localhost.crt
certutil -d sql:$HOME/.pki/nssdb -A -t "C,," -n localhost -i ./localhost.crt

Trust dotnet-to-dotnet

sudo cp localhost.crt /etc/pki/tls/certs/aspnetcore-localhost-https.pem
sudo update-ca-trust

Cleanup

rm localhost.crt

References
https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-6.0&tabs=visual-studio#trust-https-certificate-on-linux
https://github.com/dotnet/aspnetcore/issues/32842
https://github.com/dotnet/aspnetcore/issues/32361

Call a Web API from ASP.NET Core Blazor Server

In Program.cs:

builder.Services.AddHttpClient();

Read String using GET method

@page "/"
@inject IHttpClientFactory ClientFactory

<PageTitle>Index</PageTitle>

<button @onclick="Button_Clicked">Call API</button>

@code {

    private async Task Button_Clicked()
    {
    // read string using GET method
        var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5055/hello-world");
        var client = ClientFactory.CreateClient();
        var response = await client.SendAsync(request);

        if (response.IsSuccessStatusCode)
        {
            Console.WriteLine(await response.Content.ReadAsStringAsync());
        }
    }

}

Send and Read JSON using POST method

private async Task Button2_Clicked()
{
// send and read json using POST method
    var url = "http://localhost:5055/hello2";
    var person = new Person() {FirstName = "Mahmood", LastName = "Ramzani"};
    var json = JsonSerializer.Serialize(person);
    var data = new StringContent(json, Encoding.UTF8, "application/json");
    var client = ClientFactory.CreateClient();
    var response = await client.PostAsync(url, data);

    if (response.IsSuccessStatusCode)
    {
        Console.WriteLine(await response.Content.ReadAsStringAsync());
    }
}

 

References
https://docs.microsoft.com/en-us/aspnet/core/blazor/call-web-api?view=aspnetcore-6.0&pivots=server

JSON Parameter in ASP.NET MVC using [FromBody]

To force Web API to read a simple type from the request body, add the [FromBody] attribute to the parameter:

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
[Route("hello2"), HttpPost]
public JsonResult Hello2([FromBody] Person person)
{
    string output = $@"Hello {person.FirstName} {person.LastName}";
    return Json(output);
}

When a parameter has [FromBody], Web API uses the Content-Type header to select a formatter. In this example, the content type is “application/json” and the request body is a raw JSON string (not a JSON object).

References
https://docs.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api

Return JSON Data from ASP.NET MVC Controllers

Send JSON Content welcome note based on user type

using System;  
using System.Collections.Generic;  
using System.Text;  
using System.Web.Mvc;  
using System.Web.Script.Serialization;  
using JsonResultDemo.Models;  
  
namespace JsonResultDemo.Controllers  
{  
    public class JsonDemoController : Controller  
    {  
        #region ActionControllers  
  
        /// <summary>  
        /// Welcome Note Message  
        /// </summary>  
        /// <returns>In a Json Format</returns>  
        public JsonResult WelcomeNote()  
        {  
            bool isAdmin = false;  
            //TODO: Check the user if it is admin or normal user, (true-Admin, false- Normal user)  
            string output = isAdmin ? "Welcome to the Admin User" : "Welcome to the User";  
  
            return Json(output, JsonRequestBehavior.AllowGet);  
        }  
     }  
}

Get the list of users in JSON Format

/// <summary>  
/// Update the user details  
/// </summary>  
/// <param name="usersJson">users list in JSON Format</param>  
/// <returns></returns>  
[HttpPost]  
public JsonResult UpdateUsersDetail(string usersJson)  
{  
    var js = new JavaScriptSerializer();  
    UserModel[] user = js.Deserialize<UserModel[]>(usersJson);  
  
    //TODO: user now contains the details, you can do required operations  
    return Json("User Details are updated");  
}

References
https://www.c-sharpcorner.com/UploadFile/2ed7ae/jsonresult-type-in-mvc/
https://stackoverflow.com/questions/227624/asp-net-mvc-controller-actions-that-return-json-or-partial-html