Author Archives: purpleblob

WASM with Rust (and Yew)

In my previous post WASM with Rust (and Leptos) we covered creating a Rust project which generate a binary for use within WASM, using Leptos and using Trunk to build and run it.

There’s more than one framework for creating WASM/WebAssembly projects in Rust, let’s look at another one, this time Yew.

We’ll be using trunk (just as the previous post) to serve but I’ll repeat the step to install here

cargo install trunk

I’m going to assume you’ve also added the target, but I’ll include here for completeness

rustup target add wasm32-unknown-unknown

Getting started

We’re going to use a template to scaffold a basic Yew application, so create yourself a folder for your project then run

cargo generate --git https://github.com/yewstack/yew-trunk-minimal-template

For mine I stuck with the defaults after naming it wasm_app. So the stable Yew version and no logging.

Before we get into the code, let’s add a Trunk.toml (in the folder with the Cargo.toml) with this configuration

[serve]
address = "127.0.0.1"
port = 8081

Let’s see what Yew generated. From the app folder (mine was named wasm_app) run

trunk serve --open

Straight up, Yew gives us a colourful starting point.

In the code

Let’s go through the code, so we know what we need if we’re creating a project without the template, but also to see what’s been added.

If you check out the Cargo.toml it’s filled in a lot of package info. for us, so you might wish to go tweak there, but we have a single dependency

[dependencies]
yew = { version="0.21", features=["csr"] }

The Yew template includes index.scss for our styles and Trunk automatically compiles/transpiles to the .css file of the same name within the dist.

The index.html is lovely and simple, really the only addition from a bare bones index.html is the including the SASS link which tells the compiler to compile using SASS

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Trunk Template</title>
    <link data-trunk rel="sass" href="index.scss" />
  </head>
  <body></body>
</html>

In the src folder we have two files, main.rs and app.rs, within main.rs we have

mod app;

use app::App;

fn main() {
    yew::Renderer::<App>::new().render();
}

Here we are basically telling Yew to render our App. Within the app.rs we have

use yew::prelude::*;

#[function_component(App)]
pub fn app() -> Html {
    html! {
        <main>
            <img class="logo" src="https://yew.rs/img/logo.svg" alt="Yew logo" />
            <h1>{ "Hello World!" }</h1>
            <span class="subtitle">{ "from Yew with " }<i class="heart" /></span>
        </main>
    }
}

Similar to Leptos, we have a macro for our HTML tags etc. but it’s html! here (not view!). Also the component is marked with the function_component annotation, but otherwise it’s very recognisable what’s happening here.

use yew::prelude::*;

#[function_component(App)]
pub fn app() -> Html {
    html! {
        <main>
            <img class="logo" src="https://yew.rs/img/logo.svg" alt="Yew logo" />
            <h1>{ "Hello World!" }</h1>
            <span class="subtitle">{ "from Yew with " }<i class="heart" /></span>
        </main>
    }
}

Let’s add some routing

Create yourself a new file named counter.rs, let’s implement the fairly standard counter.rs component – I should say the Yew web site has an example of the counter page on their Getting Started, so we’ll just take that and make a few tweaks

use yew::prelude::*;

#[function_component(Counter)]
pub fn counter() -> Html {
    let counter = use_state(|| 0);
    let on_add_click = {
        let c = counter.clone();
        move |_| { c.set(*c + 1); }
    };

    let on_subtract_click = {
        let c = counter.clone();
        move |_| { c.set(*c - 1); }
    };

    html! {
        <div>
            <button onclick={on_add_click}>{ "+1" }</button>
            <p>{ *counter }</p>
            <button onclick={on_subtract_click}>{ "-1" }</button>
        </div>
    }
}

If you’ve used React, you’ll see this is very similar to the way we might write our React component.

Ofcourse the syntax differs, but we have a use_state and event handler functions etc. The main difference is the way we’re cloning the value – by convention all those c variables would be named counter as well, but I wanted to make it clear as to what the scope of the counter variable was.

On further reading – it appears the use_XXX syntax are hooks, see Pre-defined Hooks

When we clone the counter, we’re not cloning the value, we’re cloning the handle (or type UseStateHandler which implements Clone). All clones point to the same reactive cell, so you are essentially changing the value in that handle.

Before trying this code out we need our router, so the Yew site says add the following dependency to the Cargo.toml file

yew-router = { git = "https://github.com/yewstack/yew.git" }

but I had version issues so instead used

yew-router = { version = "0.18.0" }

Now let’s change the app.rs file to the following

use yew::prelude::*;
use yew_router::prelude::*;
use crate::counter::Counter;

#[derive(Clone, Routable, PartialEq)]
enum Route {
    #[at("/")]
    Home,
    #[at("/counter")]
    Counter,
    #[not_found]
    #[at("/404")]
    NotFound,
}

fn switch(routes: Route) -> Html {
    match routes {
        Route::Home => html! { <h1>{ "Home" }</h1> },
        Route::Counter => { html! { <Counter /> }},
        Route::NotFound => html! { <h1>{ "404" }</h1> },
    }
}

#[function_component(App)]
pub fn app() -> Html {
    html! {
        <BrowserRouter>
            <Switch<Route> render={switch} />
        </BrowserRouter>
    }
}

There’s a fair bit to digest, but hopefully it’s fairly obvious what’s happening thankfully.

We create an enum of the routes with the at annotation mapping to the URL path. Then we use a function (named switch in this case) which maps the enum to the HTML. We’ve embedded HTML into the Home and NotFound routes but the Counter will render our Counter component as if it’s HTML.

The final change is the app functions where we use the BrowserRouter and Switch along with our switch function to render the pages.

Code

Checkout the code on GitHub

WASM with Rust (and Leptos)

I’m going to be going through some of the steps from Leptos Getting Started. Hopefully we’ll be able to add something here.

Prerequisites

We’re going to use Trunk to run our application, so first off we need to make sure we’ve installed it

cargo install trunk

Trunk allows us to build our code, run a server up and run our WASM application, moreover it’s watching for changes and so will rebuild and redeploy things as you go. Web style development with a compiled language. We’ll cover more on trunk later.

Creating our project

Create yourself a folder for your application and run the terminal in that folder.

We need to create our project – although I use RustRover from JetBrains, let’s go “old school” as use cargo to create our project etc.

Run the following (obviously change wasm_app to something more meaningful for your app name

cargo new wasm_app --bin

cd into the application (as above, mine’s named wasm_app).

cargo add leptos --features=csr

We’ll need to add the WASN target

rustup target add wasm32-unknown-unknown

in the root folder (where your Cargo.toml file is) create an index.html with the following

<!DOCTYPE html>
<html>
  <head></head>
  <body></body>
</html>

Next create a cd into the src folder and edit the main.rs – it should look like the following

use leptos::prelude::*;

fn main() {
    leptos::mount::mount_to_body(|| view! { <p>"Hello, world!"</p> })
}

The mount_to_body, as the name suggests, essentially injects your WASM code into the <body></body> element.

Now, from the root folder (i.e. where index.html is) run

trunk serve --open

If the default port (8080) is already in use we can specify the port using

trunk serve --open --port 8081

If all went well then you see your default browser showing the web page, if not then open a browser window and navigate to http://localhost:8081/

To save having to set the port via the CLI, you can also create a trunk.toml file in the root folder with something like this in it

[serve]
address = "127.0.0.1"
port = 8081

Taking it a bit further

We’ve got ourselves a really simple WASM page.

Let’s move this a little further by creating a component for our application.

Create a file in src named app.rs and we’ll add the following

use leptos::prelude::*;

#[component]
pub fn App() -> impl IntoView {
    view! {
        <p>"Hello, world!"</p>
    }
}

and change the main.rs to this

mod app;

use leptos::mount::mount_to_body;
use crate::app::App;

fn main() {
    mount_to_body(App);
}

If you kept trunk running it will automatically rebuild the code and refresh the browser.

Components

The component (below) returns HTML via the view macro and as you can see, this returns a trait IntoView. As you can see we mark the function as a #[component]

#[component]
pub fn App() -> impl IntoView {
    view! {
        <p>"Hello, world!"</p>
    }
}

Note: you might want to change your text to prove to yourself that the did indeed get updated in the web page when you saved it – assuming trunk was running.

This is a pretty simple starting point, so let’s add some more bits to this…

Let’s change the App function to this

use leptos::prelude::*;
 
#[component]
pub fn App() -> impl IntoView {
    let (count, set_count) = signal(0);

    view! {

        <button on:click=move |_| set_count.set(count.get() + 1)>Up</button>
        <div>{count}</div>
        <button on:click=move |_| set_count.set(count.get() - 1)>Down</button>
    }
}

The signal (which is a reactive variable) may remind you of something like useState in React, we deconstruct the signal (which has the default of 0) into a count (getter) and a set_count (setter). To be honest the set and get functions seem odd if you’re using the properties such as C#, but that’s the way it is is Rust.

The count value is of type ReadSignal and set_count is of type WriteSignal.

We can also set the value of count using the following within the closure. Ultimately this should be a more performant way of doing things. The example above might be preferred for readability (although that’s debatable) – it does however look more inline with the way we get values etc. I’ll leave others to debate the pros and cons, for me the line below is effecient.

*set_count.write() += 1

Routing

Let’s rename the app.rs file to counter.rs (also rename the function to Counter) and create a new app.rs file which will acts as the router to our components. We’ll need to add this to the Cargo.toml dependencies

leptos_router = "0.8.5"

and in the app.rs paste the following code

use leptos::prelude::*;
use leptos_router::{
    components::{Route, Router, Routes},
    StaticSegment,
};
use crate::counter::Counter;
use crate::home::Home;

#[component]
pub fn App() -> impl IntoView {
    view! {
        <Router>
            <Routes fallback=|| "Page not found.">
                <Route path=StaticSegment("") view=Home />
                <Route path=StaticSegment("counter") view=Counter />
            </Routes>
        </Router>
    }
}

You’ll need to add the mod to the main.rs file to include the home reference.

I’ve also added a home.rs file with the following

use leptos::prelude::*;

#[component]
pub fn Home() -> impl IntoView {
    view! {
        <p>Welcome to your new app!</p>
    }
}

As you can see, the router routes / to our Home component and the /counter to our Counter component.

Meta data from code

Whilst we have an index.html which you can edit, we might want to supply some of the meta data etc. via the app’s code.

Add the following to Cargo.toml dependencies

leptos_meta = "0.8.5"

Now in main.rs add

use leptos::view;
use leptos_meta::*;

and now change the code to

fn main() {
    mount_to_body(|| {
        provide_meta_context();
        view! { 
            <Title text="Welcome to My App" />
            <Meta name="description" content="This is my app." />
            <App />
        }
    });
}

The provide_meta_context() function allows us to inject metadata such as <title>, <meta> and <script>

Code

Code for this post is available on GitHub.

cargo-watch

Cargo watch allows us to run our Rust application and when changes are detected updates and runs the application again (you know, standard watch functionality).

To install use

cargo install cargo-watch

The to run, use

cargo-watch -x run

Async/await using tokio and Rust

Rust supports async/await in a similar way to C# although these are supplied via runtimes, for example Tokio, async-std and others.

In this post we’ll look at the tokio runtime option.

The first thing we need to do is add tokio to the Cargo.toml, for example

[dependencies]
tokio = { version = "1", features = ["full"] }

Now, let’s create a simple async function

async fn execute() {
   println!("Execution in async function");
}

Notice we do not return a Task like C# or any type in this case, but this is essentially syntactic sugar for

fn execute() -> impl Future<Output = ()> 

Hence, we can see async functions return a Future (similar to a Promise in Javascript etc.).

The Future trait has a poll function which can be checked to see if the async function is ready to return a value or if it’s pending.

To await an async function we use te following syntax

execute().await;

The await will ofcourse cause the current Future to return to the caller but the code after the await will not execute until the Future completes/is ready.

If you come from C# this is much the same, i.e. running a continuation when completed etc.

To use asyc/await on main we need to make a couple of changes, first to make main async but this alone will not work without the runtime, hence main looks like this

#[tokio::main]
async fn main() {
  execute().await;
}

Futures are lazy loaded. Meaning, that the future will not execute until the await is called.

As you can see Futures do not run in a thread, they are just polling futures. However we can use tokio tasks (which looks a lot like std lib threads) the execute the code on a thread

 
let handle = tokio::spawn(asyc move {
   execute().await;
}

handle.await.unwrap();

By default tokio executes on a threadpool but we could change things, as below

#[tokio::main(flavor = "current_thread")]

Which then uses time slicing instead of threads.

Tokio is good for non blocking IO, but tokio uses a single thread for it’s main event loop hence heavy CPU will basically slow down other tasks. Hence we would need to spawn threads as already discussed.

Slight detour

As a slight detour from async/await – tokio can also create “green” threads (lightweight threads from the runtime – not OS threads), for example

async fn execute() {
   time::sleep(time::Duration::from_secs(1)).await;
}

fn main() {
  let runtime = tokio::runtime::Runtime::new().unwrap();
  
  let future = execute();

  runtime.block_on(future);
}

Secure data in plain sight (using .NET)

Often we’ll come across situations where we need to put a password into our application. For example securing a connection string or login details for a services.

This post is more a dissection of the post Encrypting Passwords in a .NET app.config File than anything new, but interestingly through up a bunch of things to look at.

String & SecureString

Let’s start by looking at String storage. Ultimately strings are stored in memory in clear text and the disposal of them is determined by the garbage collection, hence their lifespan is non-deterministic so we could dump the strings using WinDbg or similar tools in clear text.

SecureString is part of the System.Security assembly and supplies a mechanism for encrypting a string in memory and also, when no longer required, can be disposed of.

The following code shows how we might use a SecureString.

public static class Secure
{
   private static byte[] ENTROPY = System.Text.Encoding.Unicode.GetBytes("Salt Is Not A Password");

   public static string Encrypt(SecureString input)
   {
      return Encrypt(ToInsecureString(input));
   }

   public static string Encrypt(string input)
   {
      var encryptedData = System.Security.Cryptography.ProtectedData.Protect(
         System.Text.Encoding.Unicode.GetBytes(input),
         ENTROPY,
         System.Security.Cryptography.DataProtectionScope.CurrentUser);
      return Convert.ToBase64String(encryptedData);
   }

   public static SecureString DecryptToSecureString(string encryptedData)
   {
      var result = DecryptToString(encryptedData);
      return result != null ? ToSecureString(result) : new SecureString();
   }

   public static string DecryptToString(string encryptedData)
   {
      try
      {
         var decryptedData = System.Security.Cryptography.ProtectedData.Unprotect(
            Convert.FromBase64String(encryptedData),
            ENTROPY,
            System.Security.Cryptography.DataProtectionScope.CurrentUser);
         return System.Text.Encoding.Unicode.GetString(decryptedData);
      }
      catch
      {
         return null;
      }
   }

   public static SecureString ToSecureString(string input)
   {
      var secure = new SecureString();
      foreach (var c in input)
      {
         secure.AppendChar(c);
      }
      secure.MakeReadOnly();
      return secure;
   }

   public static string ToInsecureString(SecureString input)
   {
      string returnValue;
      var ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(input);
      try
      {
         returnValue = System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr);
      }
      finally
      {
         System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr);
      }
      return returnValue;
   }
}

References

Encrypting Passwords in a .NET app.config File
SecureString Class

Lazy loading Blazor WebAssembly assemblies

We can set our WebAssembly application to lazy load assemblies thus reducing the initial download of files.

For example if you have parts of your application that are rarely used, you could ensure these parts exist within their own (or shared) assembly and then set the project up to lazy load them when required. We simply make these changes to the csproj file

<ItemGroup>
  <BlazorWebAssemblyLazyLoad Include="your-assembly.dll" />
</ItemGroup>

We can use the LazyAssemblyLoader service to download/load assemblies as required (don’t forget to register the LazyAssemblyLoader as a singleton), for example

@inject LazyAssemblyLoader AssemblyLoader

@code {
  private async Task OnNavigateAsync(NavigationContext args)
  {
    try 
    { 
       if (args.Path = = "{PATH}") 
       { 
         var assemblies = await AssemblyLoader.LoadAssembliesAsync( 
           new[] { {LIST OF ASSEMBLIES} }); } 
       } 
       catch (Exception ex) 
       { 
         Logger.LogError(" Error: {Message}", ex.Message); 
       } 
    }
  }
}

Classes in Haskell (hint they’re more like interfaces)

A class (also know as typeclass) in Haskell is more analogous to an interfaces in C# and Java.

Let’s look at a simple class which will supply a toString function

class ToString a where 
  toString :: a -> String

This class allows us to use a toString function on different types. So think of this as an interface where data types that support it will now work with the toString function.

Let’s just create a simple data type as an example

data Point = Point { x :: Integer, y :: Integer }

Now it’d be cool if we could use code such as print (toString pt) and have the output a string like this “X:1, Y:2”. All we need to do is create an instance of the class. Before we do this, we’re going to need to also support converting of an Integer (as it’s used in the Point) to a string, so we may as well create an instance of the ToString class for Integer’s first and then use this code in our Point instance

instance ToString Integer where    
    toString i = show i

So now we’ve got a ToString to handle Integer’s we can combine to create an instance for a Point, like this

instance ToString Point where    
    toString pt = "X:" ++ toString (x pt) ++ ", " ++ "Y:" ++ toString (y pt)

Creating audit history for your table(s) in SQL Server

We’re wanting to capture audit history on our table(s) in our database. A good way to do this is via triggers.

Let’s create ourselves an Audit table first, something like this

CREATE TABLE [dbo].[Audit](
  [Id] [int] IDENTITY(1,1) NOT NULL,
  [Operation] [nvarchar](3) NOT NULL,
  [DateTime] [datetime] NOT NULL,
  [Before] [nvarchar](256) NULL,
  [After] [nvarchar](256) NULL,
) ON [PRIMARY]

The Operation will be used to store a value from “UPD”, “DEL” and “INS”. In other words the audit record is an Update, Delete or Insert. The DateTime is the date and time the operation took place, Before is the data before it’s changed, After the data it changed to and a Description field for a bit more information.

I’ve not included this field, but most likely we’ll also want to have a Who field for who made the change.

In my little example I have a table named Scale which is used to store musical scales. Just for completeness let’s take a look at the Scale table

CREATE TABLE [dbo].[Scale](
  [Id] [int] IDENTITY(1,1) NOT NULL,
  [Name] [nvarchar](256) NOT NULL,
  [Intervals] [nvarchar](256) NOT NULL,
  [Description] [nvarchar](256) NULL
) ON [PRIMARY]

Now I’m just going to add triggers to this one table, however you might have such triggers on multiple tables. We will want to have a trigger for deletion, insertion and updates.

For the delete trigger we might use something like this

ALTER TRIGGER [dbo].[ScaleDelete]
ON [dbo].[Scale]
FOR DELETE
AS
  INSERT INTO dbo.Audit(Operation, DateTime, Before)
  SELECT 'DEL', GETDATE(), (select d.Name, d.Description, d.Intervals from deleted d FOR JSON PATH)
  FROM deleted

For a deletion we insert the operation, date/time and a description into the audit table using the deleted data.

For insertions we might have something like this

ALTER TRIGGER [dbo].[ScaleInsert] 
ON [dbo].[Scale]
FOR INSERT
AS
  INSERT INTO dbo.Audit(Operation, DateTime, After)
  SELECT 'INS', GETDATE(), (select i.Name, i.Description, i.Intervals from inserted i FOR JSON PATH)
  FROM inserted;

In this case we take the data from the insert data and passing parts of this data to the audit table (similar to the deletion).

For the update trigger we might write something similar to the above, but any time the UPDATE SQL command is called the audit log will be added to, even if there were not changes. It would, therefore, be nice if we only updated the audit table when real changes are detected.

In the case of an update we get both inserted and deleted data. So we can check whether the inserted differs from deleted using the EXCEPT SQL command. Then, only if a difference has been found, will we update the audit table. For fun, we’re also going to turn the deleted and inserted data into JSON and store the whole row to in the Before and After fields of the audit table.

CREATE TRIGGER [dbo].[ScaleUpdate]
ON [dbo].[Scale]
AFTER UPDATE
AS
IF EXISTS (
  SELECT Name, Description, Intervals FROM inserted
  EXCEPT
  SELECT Name, Description, Intervals FROM deleted
)
BEGIN
  INSERT INTO dbo.Audit(Operation, DateTime, Before, After)
  SELECT 'UPD', GETDATE(), 
  (select d.Name, d.Description, d.Intervals from deleted d FOR JSON PATH), 
  (select i.Name, i.Description, i.Intervals from inserted i FOR JSON PATH)
  FROM inserted i
  join deleted d on (i.Id = d.Id)
END

If you’d prefer to create XML output you can replace JSON PATH to XML RAW.

Now to test these we can use

DELETE from Scale where Name = 'Chromatic'

INSERT INTO Scale (Name, Intervals)
VALUES ('Chromatic', '1,1,1,1,1,1,1,1,1,1,1,1')

UPDATE Scale
SET Intervals = '1,1,1,1,1,1,1,1,1,1,1,2'
WHERE Name = 'Chromatic'

Output caching in ASP.NET core

Output caching can be used on your endpoints so that if the same request comes into your endpoint within an “cache expiry” time then the endpoint will not get called and the stored/cached response from a previous call will be returned.

To make that clearer the endpoint’s method will NOT be called, the response is cached and hence returned via the ASP.NET middleware.

This is particularly useful for static or semi-static context.

Out of the box, the OutputCache can handle caching for different query parameters and routes can be easily set up to handle caching.

I’m going to setup the output cache to use Redis

builder.AddRedisOutputCache("cache");

builder.Services.AddOutputCache(options =>
{
    options.AddPolicy("ShortCache", builder => builder.Expire(TimeSpan.FromSeconds(10)));
});

Now from a minimal API endpoint we can apply output caching using CacheOutput as below

app.MapGet("/cached/{id}", (int id) => new
    {
        Message = $"Output Cache {id}",
        Timestamp = DateTime.UtcNow
    })
    .CacheOutput(c => c.VaryByValue(
        ctx => new KeyValuePair<string, string>("id", ctx.Request.RouteValues["id"]?.ToString() ?? string.Empty)));

Each unique id gets its own cached response.

The endpoint is only executed once per id within the cache duration.
– The VaryByValue method lets you define custom cache keys based on route values, headers, or query strings.
– Without this, /cache/1 and /cache/2 might share a cache entry or overwrite each other depending on the default key behavior.

app.MapGet("/cached-query", (int id) => new
    {
        Message = $"Output Cache {id}",
        Timestamp = DateTime.UtcNow
    })
    .CacheOutput();
app.UseOutputCache();

IIS in Docker

With the ability to use Windows based Docker images we can now deploy an IIS Docker image to host our sites (and it’s pretty simple)

Would you want to use Windows and IIS? Possibly not but this post is a primer for deploying ASP.NET framework apps. to Docker, so just shows what’s possible.

Let’s jump start in and look at the Dockerfile

FROM mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019 

COPY wwwroot /inetpub/wwwroot/

This assumes that your HTML etc. reside in a folder named wwwroot off of the folder containing the Dockerfile. You can replace COPY with ADD if preferred to get the capability of extracting compressed files as part of the copy.

Now build this using

docker build -t ns/mysite:0.1.0 .

Change ns/mysite:0.1.0 to your preferred tag name. Next run using

docker run --name mysite -p 80:80 -d ns/mysite:0.1.0

Now from your browser go to http://localhost and access the HTML pages etc.