I first heard about Blazor a couple of years ago, and it sounded very interesting, especially the part about running .NET via WebAssembly on the browser. Blazor seems to have matured enough now and it's officially part of ASP.NET. Recently, my team needed a tool for PBKDF2 as we integrate with Keycloak, so I tried writing it in Blazor. Here are some of my thoughts on it so far.
You can find the full source in my GitHub repo.
C#
It's actually very refreshing to use just one language – C# (probably my favorite language) – for both the frontend and the backend, in a modern SPA-like paradigm, not WebForms with full page PostBacks like the good old days...? Of course, you're still using HTML and CSS (with support for SCSS and scoped CSS) for the "view" template. You can also call JavaScript via interop, which will be inevitable if you want to use built-in browser functionality such as alert()
.
WebAssembly
Originally, I wanted to build it as a WebAssembly project so that I can host as a static site and all you'd need is the browser to run it, but sadly, the KeyDerivation
library doesn't support WebAssembly platform, so I had to use the Blazor Server hosting model.
The server hosting model uses WebSockets, and the server-side runs the logic, so scalability would need to be considered if writing real business applications with it.
JSX..?
I suppose this is more of a Razor template feature than Blazor, but I like that it's similar to React's JSX. For example, being able to use code blocks within the template, such as using the if
statement to wrap around a component. (Never liked how Angular templates handle logic, such as ngIf
and ngFor
directives, where they are added as element attributes.)
Unit Testing
There's no official Microsoft library for unit testing Blazor, but an open-source project called bUnit seems popular for writing unit tests for Blazor components.
Being able to test the difference between snapshots of a render seems useful, but might get too complicated if testing a large change. Component isolation and keeping them small would help.
You can write tests as a razor component, which means you can use razor syntax for the component-under-test. Visual Studio editor's auto-formatting support still needs some work though.
DisplayName Validation Message Bug?
Perhaps there's a bug when using the DisplayName
attribute on the InputText
component. For both ValidationSummary
and ValidationMessage<T>
, setting the DisplayName
attribute seems to have no effect on InputText
. It just displays the property name, not the DisplayName
.
InputNumber
works fine though, as can be seen here:
... validationErrorMessage = string.Format(ParsingErrorMessage, DisplayName ?? FieldIdentifier.FieldName); ...
Maybe it's not supported yet?
It does work if I use the [Display]
attribute on the model itself, but nowadays I feel that's not the right place, since it breaks the layer boundary. If the model is serving as a "View Model", then it'd be okay.
Finding out how the [Required]
attribute's message validation works was taking longer than I'd like, so I stopped at ValidationAttributeAdapterOfTAttribute.cs
. If I can get to spend more time on this and can confirm it, perhaps I'll file a bug report. By the way, did you know that the ASP.NET Core solution has 480 projects? Took a very long time to load it on VS2019...
Dotnet Watch Hot Reload
It sometimes forces a manual reload, but I like the experience. VS2022 will have this feature, and will remain in .NET 6 CLI..
Misc.
RNGCryptoServiceProvider
RNGCryptoServiceProvider
is obsolete in .Net 6 Preview. Be aware that a lot of examples on the internet still use this class for generating the salt.
JavaScript PBKDF2
There's actually a JavaScript library for handling PBKDF2, so yes, I could've just done it all in CodeSandbox:
import "./styles.css"; import { pbkdf2Sync } from "pbkdf2"; export default function App() { let textToHash = "foobar"; let salt = atob("b9Txti27LxJWX9BejPSaAQ=="); let result = pbkdf2Sync(textToHash, salt, 27500, 64, "sha256").toString( "base64" ); return <div className="App">Hash: {result}</div>; }