<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: kis.stupid</title>
    <description>The latest articles on DEV Community by kis.stupid (@kiss_code).</description>
    <link>https://dev.to/kiss_code</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1161408%2F11058758-1374-4cec-a86d-9cfea3358cea.jpg</url>
      <title>DEV Community: kis.stupid</title>
      <link>https://dev.to/kiss_code</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kiss_code"/>
    <language>en</language>
    <item>
      <title>Secure Access to Azure Storage Blobs</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Sun, 28 Apr 2024 16:40:25 +0000</pubDate>
      <link>https://dev.to/kiss_code/secure-access-to-azure-storage-blobs-52oc</link>
      <guid>https://dev.to/kiss_code/secure-access-to-azure-storage-blobs-52oc</guid>
      <description>&lt;p&gt;TLDR: &lt;a href="https://youtu.be/_xCF_BBjqQg"&gt;video&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the midst of building a webshop-like feature for the &lt;a href="https://www.kiss-code.com/"&gt;kiss-code.com&lt;/a&gt; brand website, I stumbled upon the requirement to provide &lt;strong&gt;protected access&lt;/strong&gt; to the &lt;strong&gt;digital products&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Currently, my shop only holds the &lt;a href="https://www.kiss-code.com/products"&gt;lead magnet&lt;/a&gt; which is &lt;strong&gt;free&lt;/strong&gt;. Within 5 minutes after ordering it, you should receive an email containing a download link. This lead magnet lives on an Azure Blob Storage container which allows &lt;strong&gt;public read access&lt;/strong&gt; for anyone to download, at any time.&lt;/p&gt;

&lt;p&gt;This download link getting leaked would result in people bypassing subscribing to my newsletter. So, let's start by changing the access level to private.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WY5eajji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://brandwebsiteblobstore.blob.core.windows.net/images/209c242d2938-blob-access-level.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WY5eajji--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://brandwebsiteblobstore.blob.core.windows.net/images/209c242d2938-blob-access-level.png" alt="Change access level" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Surprise! Changing the access level to &lt;strong&gt;private&lt;/strong&gt; will deny access also to the people who should have access after subscribing. Now, we can generate &lt;strong&gt;Shared Access Signature (SAS) tokens&lt;/strong&gt; to grant limited access for a limited time. We can simply generate one by specifying permissions and the token's time-to-live (expiry) and clicking that "Generate SAS token and URL" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--a2WOzjB1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://brandwebsiteblobstore.blob.core.windows.net/images/355fc01e50eb-generate-sas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--a2WOzjB1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://brandwebsiteblobstore.blob.core.windows.net/images/355fc01e50eb-generate-sas.png" alt="Generate SAS" width="800" height="749"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This results in the following token and URL. Following that URL will download the lead magnet which lives in a &lt;strong&gt;private&lt;/strong&gt; Azure Blob Storage container.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J7gYnZg5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://brandwebsiteblobstore.blob.core.windows.net/images/719444ad70d7-generate-sas-result.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J7gYnZg5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://brandwebsiteblobstore.blob.core.windows.net/images/719444ad70d7-generate-sas-result.png" alt="Generate SAS result" width="800" height="170"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, I'll want to generate these URLs &lt;strong&gt;automatically&lt;/strong&gt; after someone ordered a digital product on my shop. So that this person receives an email containing the download link including the SAS token to &lt;strong&gt;access&lt;/strong&gt; the &lt;strong&gt;purchased product&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'll add the following code to my &lt;a href="https://www.nuget.org/packages/AugusteVN.Azure.BlobStorage/"&gt;NuGet package&lt;/a&gt; to generate an URL including a SAS token that grants &lt;strong&gt;read access&lt;/strong&gt; for a given duration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GenerateSasUrlForBlob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;blobName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;expiresOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;containerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThrowIfNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AccountKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;containerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ContainerName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;ArgumentNullException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ThrowIfNullOrWhiteSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sasBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;BlobSasBuilder&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;BlobName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;blobName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;BlobContainerName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ExpiresOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;expiresOn&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="n"&gt;DateTimeOffset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="n"&gt;sasBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetPermissions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BlobSasPermissions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sasUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sasBuilder&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToSasQueryParameters&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StorageSharedKeyCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AccountName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AccountKey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sasUrl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get an &lt;code&gt;AccountKey&lt;/code&gt; in the Azure portal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xwdVEpV6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://brandwebsiteblobstore.blob.core.windows.net/images/793e8e021dbe-account-key.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xwdVEpV6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://brandwebsiteblobstore.blob.core.windows.net/images/793e8e021dbe-account-key.png" alt="Account key" width="258" height="334"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Find my &lt;a href="https://www.patreon.com/kisstupid/shop/net-8-brand-website-with-markdown-blog-33850"&gt;.NET 8 Brand Website V1&lt;/a&gt; with tons of features add value to and capture your audience!&lt;/p&gt;

&lt;p&gt;If you are interested in more of my work, you can find it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on my website: &lt;a href="https://www.kiss-code.com/products"&gt;kiss-code.com/products&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on my &lt;a href="https://www.patreon.com/kisstupid"&gt;Patreon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on &lt;a href="https://www.youtube.com/@kis.stupid"&gt;YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for taking the time &amp;amp; interest in my work. Kind regards, Auguste @ &lt;a href="https://www.kiss-code.com"&gt;kiss-code.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Sources&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/storage/common/storage-account-sas-create-dotnet#create-an-account-sas"&gt;https://learn.microsoft.com/en-us/azure/storage/common/storage-account-sas-create-dotnet#create-an-account-sas&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.c-sharpcorner.com/article/how-to-generate-sas-token-and-view-url-for-azure-blob-storage-in-blazor-apps-wit/"&gt;https://www.c-sharpcorner.com/article/how-to-generate-sas-token-and-view-url-for-azure-blob-storage-in-blazor-apps-wit/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>azure</category>
      <category>blobstorage</category>
      <category>dotnet</category>
      <category>csharp</category>
    </item>
    <item>
      <title>How I Became Top Rated on UpWork</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Sat, 20 Apr 2024 18:54:46 +0000</pubDate>
      <link>https://dev.to/kiss_code/became-top-rated-on-upwork-21ng</link>
      <guid>https://dev.to/kiss_code/became-top-rated-on-upwork-21ng</guid>
      <description>&lt;p&gt;WOOHOOW!!! The "Top Rated" badge!&lt;/p&gt;

&lt;p&gt;How did I obtain this so quickly? And that, with only 23 hours on 3 jobs?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is written from my POV with &lt;a href="https://www.upwork.com/freelancers/~014f40feab4147fa32"&gt;my free UpWork account&lt;/a&gt; =&amp;gt; very few connects at my disposal.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Research the platform.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I first researched how to get jobs on UpWork by listening to YouTubers like Josh Burns Tech and Cody Codes.&lt;/p&gt;

&lt;p&gt;Their advice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't be proposal number 80, try to be within the first 15.&lt;/li&gt;
&lt;li&gt;The first 2 sentences matter.&lt;/li&gt;
&lt;li&gt;Set filtered job alerts.&lt;/li&gt;
&lt;li&gt;Take profile inspiration from e.g. &lt;a href="https://www.upwork.com/nx/search/talent/?pt=independent&amp;amp;q=.net&amp;amp;revenue=1000000"&gt;top UpWork earners&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Land a job.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I target jobs where I have an edge. This edge can be skill-wise, project-wise, geographical, having things in common e.g. native tongue, ...&lt;/p&gt;

&lt;p&gt;I also increase my odds by having a &lt;strong&gt;portfolio&lt;/strong&gt; with (passion) projects to show &amp;amp; tell about my relevant experience. For example, I applied a job post for a Tinder-like app by leading with my Tinder-like dog adoption app coincidently built with the listed technology.&lt;/p&gt;

&lt;p&gt;And, I put out content (YouTube, tech blog, ...) to &lt;strong&gt;grow my reach&lt;/strong&gt; (potential clients), experience and to build trust and authority. I can confirm that this results in &lt;em&gt;hot-leads&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explain your process&lt;/strong&gt; (how you go about your work). This also helps to gain trust and authority. If you don't have a process yet, borrow one ;).&lt;/p&gt;

&lt;p&gt;Show / tell your potential leads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how you can help them.&lt;/li&gt;
&lt;li&gt;how you tackled a similar problem.&lt;/li&gt;
&lt;li&gt;a (pro-active) task breakdown / estimates.&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Build a work-relationship.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I obtained this "Top Rated" badge by bringing clients to the platform to then &lt;strong&gt;deliver&lt;/strong&gt; project milestones resulting in customer satisfaction, 5-star reviews and a high job success score. Don't be afraid to ask for / &lt;em&gt;negotiate&lt;/em&gt; a positive review.&lt;/p&gt;

&lt;p&gt;The majority of jobs I completed, didn't come from UpWork but I dealt with my clients through the platform since it's trustworthy with useful tools for both parties.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go the extra mile&lt;/strong&gt;(stone).&lt;/p&gt;

&lt;p&gt;It can be a great privilege to serve your clients from the comfort of your own home and/or at your own pace. This allows me to be the most efficient and productive. The trade-off may be that you need to fill multiple "roles", you are not just the programmer. This means, being available at odds hours and being &lt;strong&gt;pro-active&lt;/strong&gt; on all fronts of the project. It may not all be "billable" work.&lt;/p&gt;

&lt;p&gt;The result is what matters for your client and for your 5-star reviews. Not the amount of hours you put into it. It's your job to estimate the work appropriately and to deliver on it. &lt;strong&gt;Estimate &amp;amp; communicate&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;Estimate and time track the work even if it has a fixed price.&lt;br&gt;
Scope creep and underestimations are part of the game.&lt;/p&gt;

&lt;p&gt;Communicate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;work progress&lt;/li&gt;
&lt;li&gt;hiccups &amp;amp; how you'll solve them&lt;/li&gt;
&lt;li&gt;(re-)align expectations&lt;/li&gt;
&lt;li&gt;scope creep&lt;/li&gt;
&lt;li&gt;when you'll attack the tasks at hand&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't (accidently) keep your client in the dark until after you're done.&lt;/p&gt;

&lt;p&gt;A win for the client can also mean a big win for (future) you.&lt;br&gt;
Being pro-active, solution oriented will build your relationship and reputation which will open-up future opportunities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;UpWork has opened my eyes to what it takes to be a freelancer and to what is possible. It's a great stepping-stone into the (remote) freelance world.&lt;br&gt;
That said, I'm not putting all my bets on it since the majority of my gigs didn't come from UpWork. There are good reasons for that:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Saturation&lt;/strong&gt;: in a matter of hours, there can be 50 to 80 proposals on jobs that fit my active skillset. This may mean that my skillset is not &lt;strong&gt;niche&lt;/strong&gt; enough.&lt;/p&gt;

&lt;p&gt;With my free account, I can hardly do 2 proposals each month due to the &lt;strong&gt;high cost&lt;/strong&gt; to apply. I could go premium or buy connects with the earnings. I'll have to come up with a better strategy. Until then, my wonderful "Top Rated" badge won't do much.&lt;/p&gt;




&lt;p&gt;If you are interested in more of my work, you can find it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on my website: &lt;a href="https://www.kiss-code.com/products"&gt;kiss-code.com/products&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on my &lt;a href="https://www.patreon.com/kisstupid"&gt;Patreon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on &lt;a href="https://www.youtube.com/@kis.stupid"&gt;YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for taking the time &amp;amp; interest in my work.&lt;br&gt;
Kind regards, Auguste @ &lt;a href="https://www.kiss-code.com"&gt;kiss-code.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>upwork</category>
      <category>freelance</category>
      <category>webdev</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>JavaScript Interop with Blazor WASM</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Sat, 06 Apr 2024 02:22:58 +0000</pubDate>
      <link>https://dev.to/kiss_code/javascript-interop-with-blazor-wasm-eag</link>
      <guid>https://dev.to/kiss_code/javascript-interop-with-blazor-wasm-eag</guid>
      <description>&lt;p&gt;In last article &lt;a href="https://www.kiss-code.com/blog/the-role-of-javascript-in-blazor-wasm-02-04-2024"&gt;The Role of JavaScript in Blazor&lt;/a&gt; I laid out how a Blazor app can leverage JavaScript without being turned into it.&lt;/p&gt;

&lt;p&gt;Now, I'll cover Blazor WASM's JavaScript interopability feature which allows your Blazor app to execute JavaScript. This is commonly used to invoke custom JavaScript methods and browser APIs.&lt;/p&gt;

&lt;p&gt;Did you know that it works in both directions? Your JavaScript code can invoke a C# method of your Blazor app. I'll also cover that later on, in this article or you can &lt;a href="https://youtu.be/8Fl9gX2Mb44?si=LX_plcbQ874ajOsD"&gt;watch the video&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I use JavaScript in Blazor?
&lt;/h3&gt;

&lt;p&gt;The JavaScript ecosystem has an enormous amount of already built functionality and it can do anything from dynamically updating the layout on static web pages to full-fledged, highly-interactive, single-page web apps. There is a lot of knowledge and function to be learned, borrowed, re-used, ...&lt;/p&gt;

&lt;p&gt;That's exactly what I use it for in all of my Blazor WASM projects. I implement the JS interop feature for e.g. automatically starting carousels and other components that would be too time-consuming to rebuild. And, to access the browser APIs like &lt;code&gt;navigator.share&lt;/code&gt; or the local storage and other useful client-side, browser functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Call JavaScript from Blazor
&lt;/h3&gt;

&lt;p&gt;For this demo, I scaffolded a new "Blazor Web App" with "RenderMode.Auto" interactivity set globally. The code will be available on my &lt;a href="https://www.patreon.com/kisstupid"&gt;Patreon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I named this project: "JavaScriptInteropWithBlazor" and it contains a server-side project with the same name and a ".Client" project.&lt;/p&gt;

&lt;p&gt;Take the typical "scroll to top" functionality, let's add that code into the client project's &lt;code&gt;wwwroot/js/custom-js.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Add the custom JavaScript code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scrollToElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elementId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invoked 'scrollToElement'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elementId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;scrollIntoView&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smooth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For demo purposes, I added a log statement simply to verify that it worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Add the script tag&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then, we'll need to make sure this custom code is included in our application. Let's add the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag at the bottom of the &lt;code&gt;App.razor&lt;/code&gt; in the server-side project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;body&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"first_element_id"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Routes&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;rendermode=&lt;/span&gt;&lt;span class="s"&gt;"@InteractiveAuto"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"_framework/blazor.web.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./js/custom-js.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- Here --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We're placing this script tag at the bottom of the page so that the target elements are created before trying to manipulate them with our custom JavaScript code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Create ScrollToTop component&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a new component in the client project's &lt;code&gt;Layout&lt;/code&gt; folder. I'll name it &lt;code&gt;ScrollToTop.razor&lt;/code&gt; and paste in the code below.&lt;/p&gt;

&lt;p&gt;After injecting the &lt;code&gt;IJSRuntime&lt;/code&gt; at the top, this Blazor code can call the JavaScript method &lt;code&gt;scrollToElement&lt;/code&gt; and pass a parameter &lt;code&gt;elementId&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;@inject&lt;/span&gt; &lt;span class="n"&gt;IJSRuntime&lt;/span&gt; &lt;span class="n"&gt;JsRuntime&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="n"&gt;@onclick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@(() =&amp;gt; ScrollToElement("&lt;/span&gt;&lt;span class="n"&gt;first_element_id&lt;/span&gt;&lt;span class="s"&gt;"))"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;Scroll&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;top&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;@code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ScrollToElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;elementId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;   
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;JsRuntime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeVoidAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"scrollToElement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elementId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Add the component to a page&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don't forget to add this component on a Blazor page, I'll put it on the client's &lt;code&gt;MainLayout.razor&lt;/code&gt; somewhere underneath the &lt;code&gt;@Body&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;...
    &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;article&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"content px-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            @Body
        &lt;span class="nt"&gt;&amp;lt;/article&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;ScrollToTop&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; @* Here *@
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, running the Blazor app, clicking the "Scroll to top" button, should log "Invoked 'scrollToElement'" in our browser's console.&lt;/p&gt;

&lt;h5&gt;
  
  
  Return a value from JavaScript
&lt;/h5&gt;

&lt;p&gt;I can also use &lt;code&gt;InvokeAsync&amp;lt;T&amp;gt;&lt;/code&gt; to invoke a method that returns a value. I added this code to demonstrate that.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;custom-js.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scrollToElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elementId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invoked 'scrollToElement'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elementId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;scrollIntoView&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smooth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- Here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the Blazor component's &lt;code&gt;ScrollToTop&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;isElementNull&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;JsRuntime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"scrollToElement"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;elementId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Element is null: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;isElementNull&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Interop with Browser API
&lt;/h5&gt;

&lt;p&gt;Another major reason for me to use the JavaScript interopability feature is to access the browser's APIs e.g. &lt;code&gt;navigator.share&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;JsRuntime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeVoidAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"navigator.share"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Foo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Foo Bar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://www.kiss-code.com/blog"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding that to the &lt;code&gt;ScrollToElement&lt;/code&gt; method in the Blazor app, clicking the "Scroll To Top" button should now also open the brower's share menu (if supported). This share menu will be operating system specific (Windows, Android, iOS, ...).&lt;/p&gt;

&lt;p&gt;Find more browser APIs &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Import JavaScript into Blazor
&lt;/h3&gt;

&lt;p&gt;What I showed you above is the more common use case in which I included my custom JavaScript code by referencing it in a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag at the bottom of the index page (App.razor).&lt;/p&gt;

&lt;p&gt;A less common use case may be to &lt;strong&gt;encapsulate&lt;/strong&gt; this &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag into a component. Unfortunately, I can't just move the script tag into the component and expect reliable behavior. A better approach is to use the JavaScript interop feature to &lt;strong&gt;import&lt;/strong&gt; the script or a module.&lt;/p&gt;

&lt;p&gt;This can be done similarly as invoking a JavaScript method, as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;jsRuntime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeVoidAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"./js/prism.js"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After which you can use the methods that come with this module, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;jsRuntime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeVoidAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Prism.highlightAll"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Call Blazor from JavaScript
&lt;/h3&gt;

&lt;p&gt;To invoke a C# method in the Blazor app from JavaScript, I added the Blazor method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;JSInvokable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DifferentNameOptional"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LogInBlazor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invoked: "&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LogInBlazor&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added the following JavaScript snippet into the &lt;code&gt;scrollToElement&lt;/code&gt; method in &lt;code&gt;custom-js.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;DotNet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invokeMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JavaScriptInteropWithBlazor.Client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DifferentNameOptional&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should work in a client-side Blazor WASM application not in a server-side one or a pre-rendered one, then it gives the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;There are multiple .NET runtimes present, so a default dispatcher could not be resolved. Use DotNetObject to invoke .NET instance methods.`.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To resolve that error I had to add this object reference and pass it, the update code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;jsObjectReference&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DotNet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createJSObjectReference&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;DotNet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invokeMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JavaScriptInteropWithBlazor.Client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DifferentNameOptional&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jsObjectReference&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is much more you can do in this direction but it can get messy, read more here: &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability/call-dotnet-from-javascript?view=aspnetcore-8.0"&gt;call-dotnet-from-javascript&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I first encountered this use-case in an application leveraging a strong, JavaScript-based library with a lot of custom JavaScript code built on top of it where it really proves it strength. For example, after a user interaction with the calendar, I could then pass the result to Blazor to do the necessary e.g. HTTP call afterwards. I could do this entirely in JavaScript code but I prefer to limit the amount of &lt;em&gt;untyped&lt;/em&gt; JavaScript code in my Blazor apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  JavaScript Interop Server-side
&lt;/h3&gt;

&lt;p&gt;The JavaScript interop feature is &lt;strong&gt;not&lt;/strong&gt; available for Blazor SSR (static, without interactivity) since the Blazor app needs to tap into the client-side. Don't believe ChatGPT when it suggests that, it happened to a freelance client of mine.&lt;/p&gt;

&lt;p&gt;A Blazor app with &lt;code&gt;RenderMode.InteractiveServer&lt;/code&gt; or a WASM hosted (pre-rendered) can use the interop but only once the interactivity kicks in after client-side render. To avoid the server-side project to throw an interop related error, we'll have to (recommended) wrap the interop calls in a conditional of the &lt;code&gt;AfterRender&lt;/code&gt; lifecycle method. Only for interop calls that would happen &lt;code&gt;OnInitialized&lt;/code&gt;, not the ones that happen after a button click or a not-instant user interaction. Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;@inject&lt;/span&gt; &lt;span class="n"&gt;IJSRuntime&lt;/span&gt; &lt;span class="n"&gt;JsRuntime&lt;/span&gt;

&lt;span class="n"&gt;@code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnAfterRender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;firstRender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firstRender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;JsRuntime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeVoidAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"import"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"./js/prism.js"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;I have covered the JS interop feature multiple on my YouTube channel: &lt;a href="https://www.youtube.com/@kis.stupid"&gt;Keep it simple, stupid.&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can grab yourself a copy of my &lt;a href="https://www.patreon.com/kisstupid/shop/net-8-brand-website-with-markdown-blog-33850"&gt;.NET 8 Blazor brand website&lt;/a&gt; which contains multiple uses of the JS interop.&lt;/p&gt;

&lt;p&gt;My free NuGet packages contain great examples of this feature as well: &lt;a href="https://www.kiss-code.com/products"&gt;Get them for FREE&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>blazor</category>
      <category>javascript</category>
      <category>interop</category>
    </item>
    <item>
      <title>The Role of JavaScript in Blazor WASM</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Tue, 02 Apr 2024 18:57:59 +0000</pubDate>
      <link>https://dev.to/kiss_code/the-role-of-javascript-in-blazor-wasm-42ea</link>
      <guid>https://dev.to/kiss_code/the-role-of-javascript-in-blazor-wasm-42ea</guid>
      <description>&lt;p&gt;I have heard the following statement multiple times:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Blazor's (C#) code is transpiled to JavaScript code to run in the browser..."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While this is not correct, that thought is not too far-fetched for multiple reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the past there have been recompilations of C# code to the web.&lt;/li&gt;
&lt;li&gt;The JS interop feature, how else would that work?&lt;/li&gt;
&lt;li&gt;WebAssembly can't manipulate the DOM, only JavaScript can.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will cover the JS interop feature in an upcoming article and I will dedicate another YouTube video to it on my channel: &lt;a href="https://www.youtube.com/@kis.stupid"&gt;Keep it simple, stupid.&lt;/a&gt; but let's gain some &lt;strong&gt;conceptual understanding&lt;/strong&gt; first.&lt;/p&gt;

&lt;h4&gt;
  
  
  JavaScript
&lt;/h4&gt;

&lt;p&gt;Every major web browser runs a "JavaScript engine" also known as a "ECMAScript engine" alongside their &lt;strong&gt;browser engine&lt;/strong&gt;. The latter is also known as a &lt;strong&gt;rendering&lt;/strong&gt; engine or a layout engine and transforms e.g. HTML documents into an interactive visual representation (DOM).&lt;/p&gt;

&lt;p&gt;JavaScript engines started out as &lt;strong&gt;interpreters&lt;/strong&gt; which would execute code line-by-line or statement-by-statement after translating the source code into machine code (intermediate code). Nowadays, JavaScript engines use just-in-time &lt;strong&gt;(JIT) compilation&lt;/strong&gt; which arguably uses the best of both worlds, interpreter vs. compiler.&lt;/p&gt;

&lt;p&gt;Do not confuse the above with the &lt;em&gt;transpiling&lt;/em&gt; of e.g. ECMAScript 6 (ES6) into ES5 code. This is the conversion of JavaScript's ES6+ syntax into its ES5 equivalent to be backwards compatible with older browsers.&lt;/p&gt;

&lt;p&gt;A well-known ECMAScript engine is Google's V8 JavaScript engine which is used in the browser but also for Node.js and Deno runtime systems. So, JavaScript engines are not solely used for browsers.&lt;/p&gt;

&lt;p&gt;ECMAScript engines can execute code (Angular, React, Vue, ...) &lt;strong&gt;within the same (security) sandbox&lt;/strong&gt; as regular JavaScript code.&lt;/p&gt;

&lt;p&gt;Ever since (2017) some of these engines support &lt;strong&gt;WebAssembly&lt;/strong&gt; and can now execute the assembly code in that same security sandbox.&lt;/p&gt;

&lt;h4&gt;
  
  
  Browser APIs
&lt;/h4&gt;

&lt;p&gt;The major browser also provide a large number of Web APIs for your web app to interact with browser features. Commonly used web browser APIs are the "Fetch API", "Geolocation API", "File API", "Share API", "Local Storage", ...&lt;/p&gt;

&lt;p&gt;The browser APIs are mostly called from JavaScript code but are not limited to JavaScript. That said, to use these in a Blazor app, we can use the &lt;strong&gt;JavaScript Interop&lt;/strong&gt; feature (which was supposed to be the subject of this article).&lt;/p&gt;

&lt;h4&gt;
  
  
  WebAssembly
&lt;/h4&gt;

&lt;p&gt;WASM brings high-performing, assembly-like programming to the web as portable, &lt;strong&gt;binary-code&lt;/strong&gt; to be executed client-side. WASM implementations typically either use ahead-of-time (AOT) or just-in-time (JIT) compilation.&lt;/p&gt;

&lt;p&gt;WASM executes apps / app modules within a &lt;strong&gt;sandboxed environment&lt;/strong&gt; separated from the host runtime. Interactions between the apps and its hosting environment can only happen through set APIs.&lt;/p&gt;

&lt;p&gt;More than &lt;strong&gt;40 programming languages&lt;/strong&gt; can be compiled to WebAssembly &lt;strong&gt;executables&lt;/strong&gt;. Most WASM implementations aimed at the web and some are general-purpose implementations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"If WASM+WASI existed in 2008, we wouldn't have needed to create Docker. That's how important it is. WebAssembly on the server is the future of computing."&lt;/em&gt; - Solomon Hykes, a co-founder of Docker.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Lastly, interesting to note is that it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;supports multithreading and &lt;strong&gt;garbage collection&lt;/strong&gt; ;&lt;/li&gt;
&lt;li&gt;cannot directly manipulate the browser's DOM, requires JavaScript;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By reading all of the above, we can make assumptions on how Blazor and JavaScript work together but let's dig in.&lt;/p&gt;

&lt;h4&gt;
  
  
  Blazor WebAssembly
&lt;/h4&gt;

&lt;p&gt;Blazor WASM allows for running highly-interactive, single-page C# web applications in the browser on a WASM-based &lt;strong&gt;.NET runtime&lt;/strong&gt;. The runtime and other dependencies needs to be &lt;strong&gt;downloaded&lt;/strong&gt; and initialized to run a standalone Blazor WASM app. All this is handled by the Blazor script: &lt;code&gt;blazor.web.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This WASM-based .NET runtime has a rather large file size to download which does not result in a great user experience. Blazor optimizes this by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;caching&lt;/strong&gt; these dependencies for future runs;&lt;/li&gt;
&lt;li&gt;strips away unused code (IL trimmer);&lt;/li&gt;
&lt;li&gt;using the just-in-time (JIT) interpreter at execution;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, ahead-of-time (AOT) compilation is &lt;strong&gt;disabled&lt;/strong&gt;. Enabling this comes down to the need to optimize for package size, performance or supported features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;With AOT disabled, the Blazor app runs in the browser by using a &lt;em&gt;.NET intermediate language (IL) interpreter&lt;/em&gt; implemented in WebAssembly with &lt;em&gt;partial&lt;/em&gt; just-in-time (JIT) runtime support.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;With AOT compilation enabled, the Blazor app's C# code &lt;em&gt;precompiles&lt;/em&gt; into binary-code (WebAssembly) before browser execution.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  What have(n't) we learned so far?
&lt;/h4&gt;

&lt;p&gt;The C# code compiled as assembly code can be executed in the same (security) &lt;strong&gt;sandboxed environment&lt;/strong&gt; as regular JavaScript code but (still) &lt;strong&gt;requires JavaScript&lt;/strong&gt; to manipulate the browser's DOM.&lt;/p&gt;

&lt;h5&gt;
  
  
  How does Blazor WASM manipulate the DOM?
&lt;/h5&gt;

&lt;p&gt;We learned that the Blazor script: &lt;code&gt;blazor.web.js&lt;/code&gt; is responsible for downloading and initializing the (WASM-based) .NET runtime. It's not clear whether this is also responsible for DOM interactions.&lt;/p&gt;

&lt;p&gt;Blazor maintains &lt;strong&gt;render-trees&lt;/strong&gt; (representations) of the DOM in-memory to render its components. On component update or a re-render, an old and new render-trees are compared to one another, the difference is communicated to JavaScript (using interop?) functionality to update the DOM. This sound similar like React.js' shadow DOM or a virtual DOM.&lt;/p&gt;

&lt;h5&gt;
  
  
  Why would we need the JS interop?
&lt;/h5&gt;

&lt;p&gt;JavaScript is the front runner for building interactive, client-side web apps and has grown a large, fast-evolving ecosystem around it. I have been an active participant in using JavaScript-heavy projects from hobby to professional.&lt;/p&gt;

&lt;p&gt;The JavaScript ecosystem has an enormous amount of already built functionality and it can do anything from dynamically updating the layout on static web pages to full-fledged, highly-interactive, single-page web apps. So, there is a lot of knowledge and function to be learned, borrowed, re-used, ...&lt;/p&gt;

&lt;p&gt;Thanks to the modern-web framework like React, Angular, Vue, ... and its data-binding functionality, we don't have to concern ourselves too much with manually manipulating the DOM. This allows us to focus on building features, organizing our project structure etc. and to increase our productivity with all its tooling.&lt;/p&gt;

&lt;p&gt;While building awesome web apps with Angular, React and Svelte, I often wondered why I was still using JavaScript since the code didn't resemble Vanilla JavaScript or jQuery anymore. But, I had a strong preference for the structure that Angular and Nest.js offered and soon after for reasons that I described in an earlier blog post, I switched to Blazor WASM &amp;amp; .NET Web API.&lt;/p&gt;

&lt;p&gt;While using Angular, I sometimes used jQuery or other JS libraries and a ton of NPM packages. Among other functionality, I didn't rebuild the already provided Bootstrap jQuery functionality and not all JS libraries worked smoothly with Angular. Just like it is with Blazor, while I can easily rebuild e.g. Bootstrap's dropdown functionality, the accordions and maybe the carousels, I don't have to. Especially that last one can be time-consuming to figure out and rebuild so I rather leverage the already built functionality while not having it scattered across my codebase.&lt;/p&gt;

&lt;p&gt;Long story short, I implement the JS interop feature for e.g. auto-starting carousels and other components that would be too time-consuming to rebuild.&lt;/p&gt;

&lt;p&gt;Another reason I implement the JS interop is to access the browser APIs like &lt;code&gt;navigator.share&lt;/code&gt; or the local storage and more useful client-side, browser functionality.&lt;/p&gt;

&lt;p&gt;I have covered the JS interop feature multiple on my YouTube channel: &lt;a href="https://www.youtube.com/@kis.stupid"&gt;Keep it simple, stupid.&lt;/a&gt; and I will dedicate another upcoming video and article to it.&lt;/p&gt;

&lt;p&gt;Or you can grab yourself a copy of my &lt;a href="https://www.patreon.com/kisstupid/shop/net-8-brand-website-with-markdown-blog-33850"&gt;.NET 8 Blazor brand website&lt;/a&gt; which contains multiple uses of the JS interop.&lt;/p&gt;




&lt;p&gt;Thank you for taking the time &amp;amp; interest in my work.&lt;/p&gt;

&lt;p&gt;Kind regards, Auguste @ kiss-code&lt;/p&gt;




&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/JavaScript%5C_engine"&gt;https://en.wikipedia.org/wiki/JavaScript\_engine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/WebAssembly"&gt;https://en.wikipedia.org/wiki/WebAssembly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/webassembly"&gt;https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/webassembly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/hosting-models"&gt;https://learn.microsoft.com/en-us/aspnet/core/blazor/hosting-models&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability"&gt;https://learn.microsoft.com/en-us/aspnet/core/blazor/javascript-interoperability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webassembly.org/docs/security/"&gt;https://webassembly.org/docs/security/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.reddit.com/r/Blazor/comments/idikdk/what%5C_is%5C_the%5C_flow%5C_for%5C_c%5C_code%5C_to%5C_manipulate%5C_the%5C_dom/"&gt;https://www.reddit.com/r/Blazor/comments/idikdk/what\_is\_the\_flow\_for\_c\_code\_to\_manipulate\_the\_dom/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>blazor</category>
      <category>webassembly</category>
      <category>dotnet</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Pitfalls of Hosting Background Workers as Azure App Service</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Sat, 02 Mar 2024 10:06:31 +0000</pubDate>
      <link>https://dev.to/kiss_code/pitfalls-of-hosting-background-workers-as-azure-app-service-59a8</link>
      <guid>https://dev.to/kiss_code/pitfalls-of-hosting-background-workers-as-azure-app-service-59a8</guid>
      <description>&lt;p&gt;I hope you had a better day than me...&lt;/p&gt;

&lt;p&gt;I spent my day looking at this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR - Container backgroundworker_x didn't respond to HTTP pings on port: 8080, failing site start.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is that supposed to be?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A .NET 8 background worker service hosted on an Azure App Service (Linux container).&lt;/li&gt;
&lt;li&gt;Should run only once every 24 hours.&lt;/li&gt;
&lt;li&gt;Has "Always on" (waste).&lt;/li&gt;
&lt;li&gt;Has "Health checks off".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's wrong?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It recycles every 10 minutes due to supposed error.&lt;/li&gt;
&lt;li&gt;It suddenly restarts and interrupts any ongoing work.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What rabbit holes did I look into?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exposing that port, extending amount of time to start, ... (not the problem)&lt;/li&gt;
&lt;li&gt;Investigating health checks. (shouldn't be the problem)&lt;/li&gt;
&lt;li&gt;Investigating alternatives like WebJobs (for Windows container), ...&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I slightly panicked when I wondered if this behavior was also happening on my other App Services that host APIs or Blazor apps. These do idle after a while of inactivity but sudden restarts wouldn't be great.&lt;/p&gt;

&lt;p&gt;Why are those not facing the same issue? Well, these are webapps "initialized successfully and ready to serve requests"&lt;/p&gt;

&lt;p&gt;So my simple, hacky solution is to convert my background worker services into web projects by changing the &lt;code&gt;Sdk="Microsoft.NET.Sdk.Worker"&lt;/code&gt; to &lt;code&gt;Sdk="Microsoft.NET.Sdk.Web"&lt;/code&gt; and slightly modify the "Progam.cs". And, I added a health check endpoint just in case.&lt;/p&gt;

&lt;p&gt;The WebJobs approach might have been better but not available on my Linux App Service.&lt;/p&gt;

&lt;p&gt;Azure Functions but has been quite a struggle so far.&lt;/p&gt;

&lt;p&gt;Do you know better ways to host Background Worker Services and run them (CRON) periodically? Let me know!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>dotnet</category>
      <category>worker</category>
      <category>csharp</category>
    </item>
    <item>
      <title>When HR makes software engineering tests</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Tue, 13 Feb 2024 14:13:57 +0000</pubDate>
      <link>https://dev.to/kiss_code/when-hr-makes-software-engineering-tests-20hd</link>
      <guid>https://dev.to/kiss_code/when-hr-makes-software-engineering-tests-20hd</guid>
      <description>&lt;p&gt;Cultural fit? As a software developer, this should be my only politically loaded, public post on LinkedIn...&lt;/p&gt;

&lt;p&gt;I just declined a "technical" test for a software role due to it becoming my second encounter with TestGorilla, a Dutch HR company that makes "Pre-Employment Testing Software".&lt;/p&gt;

&lt;p&gt;They claim to be bias-free, scientifically validated, objective, reliable and fair.&lt;/p&gt;

&lt;p&gt;I first encountered them on UpWork with a job post to take and assess their software engineering tests. I hope this isn't what they mean by "scientifically validated".&lt;/p&gt;

&lt;p&gt;I gave it a shot, expecting an objective software engineering test. I never got to that part if there was one...&lt;/p&gt;

&lt;p&gt;It started off with a pre-test "reading comprehension". Which was a quiz with only one "correct" answer per statement.&lt;/p&gt;

&lt;p&gt;A few statements stood out with themes of "climate", "gender", ...&lt;/p&gt;

&lt;p&gt;The statements were phrased suggestively and some hypothetical. These stood out:&lt;/p&gt;

&lt;p&gt;"... ice caps melting could potentially rise sea levels by a few meters"&lt;br&gt;
"... gender bias against women by A.I. ..."&lt;br&gt;
But ... only one correct answer per statement. So, more like an "agree or fail" test possibly fishing for political, cultural or ethical stances, disguised as "reading comprehension".&lt;/p&gt;

&lt;p&gt;My objections to this:&lt;/p&gt;

&lt;p&gt;What do I really know about these topics? I accept the world not to be flat but do I have any evidence of that? Nope. I'm just a simple guy with software development as my craft. Why / how should I know or care? Why ask me about potential world issues?&lt;/p&gt;

&lt;p&gt;I understand a thing or two about A.I. but why do you need me to denounce this occurrence? Why this one?&lt;/p&gt;

&lt;p&gt;My passion is to create, to build and to solve problems. It involves some critical-thinking to make things functionally accurate. Being critical means that you may not just accept everything, just because.&lt;/p&gt;

&lt;p&gt;Anyhow, this form of an "agree or fail" test may not result in the promised "bias-free" outcome. It may "expose" some traits. I understand that compliance and agreeableness likely are attractive traits to a manager, team or employer. But, I doubt the critical-thinking quality or genuinity of the passing candidates.&lt;/p&gt;

&lt;p&gt;I demanded TestGorilla to remove my personal information and my test answers as this smells too much like the "Facebook Cambridge Analytica Scandal" which abused (e.g. quiz) data for political advertising.&lt;/p&gt;

&lt;p&gt;And, I find it an unfortunate way of sneaking "climate-alarmism", gender topics, and other wokeisms into the technical professions as a politically loaded quiz.&lt;/p&gt;

&lt;p&gt;What do you think?&lt;/p&gt;

</description>
      <category>skill</category>
      <category>assesment</category>
      <category>softwareengineering</category>
      <category>criticalthinking</category>
    </item>
    <item>
      <title>My secret to advancing software skills</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Sat, 18 Nov 2023 14:44:28 +0000</pubDate>
      <link>https://dev.to/kiss_code/my-secret-to-advancing-software-skills-512h</link>
      <guid>https://dev.to/kiss_code/my-secret-to-advancing-software-skills-512h</guid>
      <description>&lt;p&gt;As mentioned in my previous posts, working together with an &lt;strong&gt;experienced developer&lt;/strong&gt; or on your own &lt;strong&gt;hobby projects&lt;/strong&gt; can rapidly improve your software skills.&lt;/p&gt;

&lt;p&gt;However, this &lt;strong&gt;learning curve&lt;/strong&gt; also flattens after a while. Without proper &lt;strong&gt;guidance&lt;/strong&gt; while developing your hobby projects, you'll only learn from it &lt;em&gt;the hard way&lt;/em&gt;. This isn't bad but it might take you much &lt;strong&gt;longer&lt;/strong&gt; than it needs to.&lt;/p&gt;

&lt;p&gt;Content creators can be a invaluable to your developer journey! Some provide you with isolated how-to's, others update you on the news within the space, ...&lt;/p&gt;

&lt;p&gt;On the &lt;strong&gt;"Keep it simple, stupid."&lt;/strong&gt; YouTube channel, I continuously work on real-world apps while exploring &amp;amp; leveraging the latest .NET features and Azure cloud services.&lt;/p&gt;

&lt;p&gt;I share with you how I implement these, with &lt;strong&gt;real&lt;/strong&gt; use cases and how I architect entire &lt;strong&gt;end-to-end&lt;/strong&gt; solutions from start to finish. I also share the code for you to learn from or for you to adapt to fit your requirements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My most effective method to advance my software skills.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By &lt;strong&gt;repurposing&lt;/strong&gt; someone else's well-crafted &lt;strong&gt;code&lt;/strong&gt; , you can get a deeper understanding of why and how the author solves certain problems. How they &lt;strong&gt;structure&lt;/strong&gt; their projects and more. By immersing yourself into it with the proper curiosity and willingness to learn, you'll adopt great practices and advance your overall knowledge on software development.&lt;/p&gt;

&lt;p&gt;How I would I know? Well, I've been developing daily for over 11 years, started out as a &lt;strong&gt;self-taught&lt;/strong&gt; programmer, learned a thing or two in college, learned more on the job. In some jobs, I was on my own, in others I had the privilege to work with peer developers. Besides that, I am always working on hobby projects and learning from those slowly but surely.&lt;/p&gt;

&lt;p&gt;Yet, the impact of immersing myself into other's &lt;strong&gt;inspirational&lt;/strong&gt; work have taken me way beyond what I could learn up until that point. Not only is that work the result of decades of experience of the author but their invested time and attention to detail, makes it a work of art and an &lt;strong&gt;invaluable&lt;/strong&gt; resource to learn from.&lt;/p&gt;

&lt;p&gt;That's exactly what I aim to &lt;strong&gt;offer&lt;/strong&gt; with my content! I put a lot of time, effort and thought into making valuable resources to either learn from, act as an alternative solution / perspective or to be molded into someone's work.&lt;/p&gt;

&lt;p&gt;I'm pleased to see that it is doing exactly that for a variety of developers:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5q9ZrsXH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://brandwebsiteblobstore.blob.core.windows.net/campaigns/brand-website-testimonials.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5q9ZrsXH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://brandwebsiteblobstore.blob.core.windows.net/campaigns/brand-website-testimonials.png" alt="Testimonials" width="800" height="133"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you are interested in my work, you can find it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on my website: &lt;a href="https://www.kiss-code.com/products"&gt;kiss-code.com/products&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on my &lt;a href="https://www.patreon.com/kisstupid"&gt;Patreon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on &lt;a href="https://www.youtube.com/@kis.stupid"&gt;YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Subscribe to my &lt;a href="https://www.kiss-code.com"&gt;newsletter&lt;/a&gt; to stay up-to-date &amp;amp; receive discounts.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>softwareengineering</category>
      <category>jobs</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>2 growth hacks to boost your software skills</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Tue, 14 Nov 2023 14:56:23 +0000</pubDate>
      <link>https://dev.to/kiss_code/2-growth-hacks-to-boost-your-software-skills-183k</link>
      <guid>https://dev.to/kiss_code/2-growth-hacks-to-boost-your-software-skills-183k</guid>
      <description>&lt;p&gt;In my experience, the two most impactful actions you can take to boost your developer journey, are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;building hobby projects;&lt;/li&gt;
&lt;li&gt;learning from a relevant peer developer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you can find the time to build hobby projects, it's highly recommended! This way you can trial-n-error, refactor, restructure your project to your hearts content, learn what works and what doesn't. And, it presents you with the perfect opportunity to try-out new features and libraries.&lt;/p&gt;

&lt;p&gt;This way, you'll be one step ahead when assigned to architect / build sustainable projects on the work floor. And you'll have those projects to show off &amp;amp; talk about when applying for jobs. It also great to gain trust about your expertise with your employer. And just maybe they'll let you skip that tricky coding test ;).&lt;/p&gt;

&lt;p&gt;The second luxury is a &lt;strong&gt;relevant&lt;/strong&gt; peer developer, likely someone who does the above. A strategical programmer, who can teach you strong basics but also the &lt;em&gt;latest and greatest&lt;/em&gt;. Someone who doesn't shy away from opportunities to innovate.&lt;/p&gt;

&lt;p&gt;If you don't have these luxuries, tech YouTubers, content- and course creators can be a great substitute. One day, you will be that peer developer if you aren't already.&lt;/p&gt;

&lt;p&gt;What else do you think can fast-track growth as a developer? Let me know! &lt;/p&gt;

&lt;h2&gt;
  
  
  Which hobby projects to build?
&lt;/h2&gt;

&lt;p&gt;This can vary depending on which type of software developer you are but these project types are highly recommended to have built:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your portfolio website;&lt;/li&gt;
&lt;li&gt;your digital resume;&lt;/li&gt;
&lt;li&gt;your (tech) blog.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the best types of projects to share and showcase your work, experience and knowledge. To build these, you either build everything yourself and gain a better understanding of &lt;strong&gt;full-stack&lt;/strong&gt; web development. Or you learn about the technology, tools and topics to help you build the project. To name a few: a headless CMS, JAM-stack, a full CMS like WordPress, ...&lt;/p&gt;

&lt;p&gt;Once you've built one or all of the above, you might be ready to build more &lt;strong&gt;challenging&lt;/strong&gt; projects. Projects that solve real world problems, these can range from solutions that help out your local dog shelter to improving processes of small businesses in your area up to solutions to increase the revenue of companies. &lt;/p&gt;

&lt;p&gt;Think about gathering data from external APIs, which data storage to use, which cloud components and which protocols to communicate over. Try to integrate third-party libraries, technology to boost your app's capabilities and to impress others ;). Diving into topics like "Clean Architecture" or "Clean Code" can also greatly benefit you down the road.&lt;/p&gt;

&lt;p&gt;Did I forget something important? Let me know!&lt;/p&gt;




&lt;p&gt;I have built multiple portfolio websites with different technologies through the past. I have also built a digital, printable resume and I recently built my own &lt;strong&gt;brand website&lt;/strong&gt; with tech blog.&lt;/p&gt;

&lt;p&gt;If you want a head start, you can grab yourself &lt;a href="https://www.patreon.com/kisstupid/shop/net-8-brand-website-with-markdown-blog-33850"&gt;a copy here&lt;/a&gt;. To repurpose that to your own brand, portfolio or blog website.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>softwaredevelopment</category>
      <category>career</category>
      <category>jobs</category>
    </item>
    <item>
      <title>One of the great killers of software projects</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Tue, 07 Nov 2023 22:14:06 +0000</pubDate>
      <link>https://dev.to/kiss_code/one-of-the-great-killers-of-software-projects-2pe6</link>
      <guid>https://dev.to/kiss_code/one-of-the-great-killers-of-software-projects-2pe6</guid>
      <description>&lt;p&gt;One of the great killers of software projects, its potential and the commitment of (future) assigned developers, is &lt;em&gt;going fast and making a mess&lt;/em&gt; in the process. The video at the bottom, describes &lt;strong&gt;tactical vs. strategical&lt;/strong&gt; programming in a nutshell.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can a developer do to combat this?
&lt;/h2&gt;

&lt;p&gt;A first, would be to educate oneself on topics revolving clean code and clean architecture. More specifically, in the principles and patterns resulting in such practices. Although there are many books and video's to learn from, working with a peer developer who applies the above, can be invaluable.&lt;/p&gt;

&lt;p&gt;Think critically about the code you write, &lt;em&gt;"Think twice, code once."&lt;/em&gt;. Improvements can range from the usage of logical operators to leveraging external solutions, productivity-boosting tooling, ...&lt;/p&gt;

&lt;p&gt;You can often write it with less lines of code and improve its readability for your teammates. Aim to leave a codebase in better shape than you found it. Take it one step or one ticket at a time, separate yourself from the &lt;em&gt;"code monkeys"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Optimize and fully utilize your tooling for increased productivity while minimizing code smells or bug potential. This encompasses the features of your programming language, frameworks, IDE plugins and beyond automated release cycles. Don't &lt;em&gt;"half-ass"&lt;/em&gt; or refuse to evolve the above since that will only decrease productivity over time. There are plenty of proven systems to adopt, no need to reinvent these.&lt;/p&gt;

&lt;p&gt;I can see how, from a business perspective, tactical programming might appear to be praised in the short term but will lead to technical debt in the long term. Sooner rather than later. As tactical programming has its perks, it's the lesser advisable approach to be used beyond a concept phase.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can an employer or project manager do to combat this?
&lt;/h2&gt;

&lt;p&gt;Hire proven, quality competence for your back-end systems. As quantity in years might improve your odds, it's unfortunately not a guarantee. Recruiting for battle-tested programming language frameworks might improve the odds. Once obtained, it's an idea to pair them up with rookies on a new project, when possible.&lt;/p&gt;

&lt;p&gt;In time, your development team might live by great coding practices, delivering highly maintainable projects which in turn frees up time for bullet-proof technical decision making. Don't allow for bad practices to train your rookies, as described in the video below.&lt;/p&gt;

&lt;p&gt;Entertain your developer's requests for improvements or refactoring. Involve them (more) in crucial technical decision phases. Phases to tackle structural or procedural improvements should have a place in any software project. Not to be shrugged off as &lt;em&gt;"unbillable"&lt;/em&gt;. It pays in the long run by preventing frustrated developers and dissatisfied clients.&lt;/p&gt;

&lt;p&gt;If possible, don't take on existing projects another tactical development team already ran through until they could no more. You're likely to repeat the cycle the video describes. If you do, be very transparent about it to your development team or new hires. It's dirty work.&lt;/p&gt;

&lt;p&gt;You don't have to take my word for it, watch the video, it describes many software projects. &lt;a href="https://youtu.be/7EmboKQH8lM?si=Kw5e_e0t5FbYwHuj&amp;amp;t=1532"&gt;Clean Code - Uncle Bob&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;If you are interested to see my work, you can find it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on my website: &lt;a href="https://www.kiss-code.com/products"&gt;kiss-code.com/products&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on my &lt;a href="https://www.patreon.com/kisstupid"&gt;Patreon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on &lt;a href="https://www.youtube.com/@kis.stupid"&gt;YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Subscribe to my newsletter to stay up-to-date &amp;amp; receive discounts.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>webdev</category>
      <category>softwareengineering</category>
      <category>softwarejob</category>
    </item>
    <item>
      <title>A glimpse of my software development journey</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Tue, 07 Nov 2023 17:43:12 +0000</pubDate>
      <link>https://dev.to/kiss_code/a-glimpse-of-my-software-development-journey-pl6</link>
      <guid>https://dev.to/kiss_code/a-glimpse-of-my-software-development-journey-pl6</guid>
      <description>&lt;p&gt;Hi, I'm &lt;strong&gt;Auguste&lt;/strong&gt; from the "Keep it simple, stupid." YouTube channel. This channel started out as video documentation for my own software hobby projects. To share how I build my projects.&lt;/p&gt;

&lt;p&gt;Before I realized, the channel hit over 340 subscribers organically. Nowadays, I'm taking the 'knowledge sharing' more serious and do so more frequently. To be of as much &lt;strong&gt;value&lt;/strong&gt; as I can.&lt;/p&gt;

&lt;p&gt;Over 11 years ago, I started out on the attic typing over YouTube video's to build my first full-stack web projects. These days, I'm happy to return the favor. I'm passionate about creating and innovating with an insatiable hunger for learning. That's why I'm an early adopter of &lt;strong&gt;cloud&lt;/strong&gt; technologies, obtained an extra degree in &lt;strong&gt;artificial intelligence&lt;/strong&gt;, ...&lt;/p&gt;

&lt;p&gt;My favorite quote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"An expert is a person who has made all the mistakes that can be made in a very narrow field."&lt;/em&gt; - Niels Bohr&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What is yours?&lt;/p&gt;

&lt;h2&gt;
  
  
  How did I end up in the .NET space?
&lt;/h2&gt;

&lt;p&gt;I made the jump to .NET when Blazor WASM arrived. I jumped from Angular + Nest.JS to Blazor + Web API. I have used React, Svelte, jQuery, Knockout.js, ... as well but ...&lt;/p&gt;

&lt;p&gt;In a professional environment, I prefer structured frameworks to build sustainable codebases. Structure, good practices &amp;amp; standardization help produce predictable codebases resulting in maintainable projects.&lt;/p&gt;

&lt;p&gt;Nest.JS did provide most of the above but soon our go-to ORM package lost maintainers while containing bugs, Webpack glitching every now and then, our projects containing too many configuration files, NPM giving us more work than expected etc. .NET's out-of-the box ORM, authentication &amp;amp; authorization plus LINQ and other tooling, started looking more and more attractive.&lt;/p&gt;

&lt;p&gt;In Nest.JS we could already organize &amp;amp; test our codebases better thanks to the modular setup and dependency injection capabilities. We could separate our concerns better with the supported mono-repo setup. .NET provides all that and more.&lt;/p&gt;

&lt;p&gt;It had long been the wet-dream since Node.JS came out, to re-use code between front-end and back-end. Unfortunately, that has never been easy to do nor to have front-end &amp;amp; back-end projects in one mono-repo.&lt;/p&gt;

&lt;p&gt;In a .NET solution it is very easy to setup a mono-repo with multiple projects. Whether those projects are client-side or server-side, does not matter.&lt;/p&gt;

&lt;p&gt;That said, the re-using of code between front- and back-end is usually limited to constants, helper classes &amp;amp; DTOs.&lt;/p&gt;

&lt;p&gt;Nest.JS and a .NET Web API are structured similarly with similar features &amp;amp; with good practices borrowed from other languages or frameworks. The jump was easier than expected and I never looked back.&lt;/p&gt;

&lt;p&gt;I am glad to have participated in one of the fastest evolving programming ecosystems which is the JavaScript ecosystem. And this experience is still very relevant for my daily web developing activities as I can leverage the powerful JavaScript browser APIs in Blazor.&lt;/p&gt;

&lt;p&gt;In the early days, .NET was too overwhelming for many novice developers. Nowadays, it's way more accessible. And Blazor is an absolute gem to use!&lt;/p&gt;

&lt;h2&gt;
  
  
  How about you?
&lt;/h2&gt;

&lt;p&gt;How about you? I'm interested to hear about your journey in the software space &amp;amp; your experience and your experiences.&lt;/p&gt;




&lt;p&gt;If you are interested in my work, you can find it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on my website: &lt;a href="https://www.kiss-code.com/products"&gt;kiss-code.com/products&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on my &lt;a href="https://www.patreon.com/kisstupid"&gt;Patreon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on &lt;a href="https://www.youtube.com/@kis.stupid"&gt;YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Subscribe to my newsletter to stay up-to-date &amp;amp; receive discounts.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>dotnet</category>
      <category>fullstack</category>
      <category>webdev</category>
    </item>
    <item>
      <title>What are you working on?</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Fri, 03 Nov 2023 12:46:25 +0000</pubDate>
      <link>https://dev.to/kiss_code/how-are-you-what-are-you-working-on-2g8d</link>
      <guid>https://dev.to/kiss_code/how-are-you-what-are-you-working-on-2g8d</guid>
      <description>&lt;p&gt;What are you working on?&lt;/p&gt;

&lt;p&gt;If you can find the time to build hobby projects, it's highly recommended! This way you can trial-n-error, refactor, restructure your project to your heart's content, and learn what works and what doesn't.&lt;/p&gt;

&lt;p&gt;This way, you'll be one step ahead when assigned to architect / build sustainable projects on the work floor. And you'll have those projects to show off &amp;amp; talk about when applying for jobs. It also great to gain trust about your expertise with your employer. And just maybe they'll let you skip that tricky coding test ;).&lt;/p&gt;

&lt;p&gt;So what are you working on? What is your preferred way of learning web / app development or software architecture?&lt;/p&gt;

&lt;p&gt;I learn better by doing, I need to delve into topics to gain the experience of applying those. While building, I attempt to tackle all the questions that I have around the topic. I prefer a strategic approach to programming rather than a tactical approach. "Think twice, code once", but that's a topic for another post.&lt;/p&gt;

&lt;p&gt;On the work floor, I ask a lot of questions to get quick feedback, even if it makes me look less intelligent.&lt;br&gt;
These questions sometimes are self-evident for the team members who have been on the project for some time, but not for the newcomers. I confess that I sometimes blurt things out to see what sticks, to get that instant feedback or to at least make everyone think twice if they hadn't already.&lt;/p&gt;

&lt;p&gt;I'm currently working on the 'EmailCampaigns' module for my &lt;a href="https://www.kiss-code.com"&gt;kiss-code.com&lt;/a&gt; brand website. One of the things that it will do, is to automatically deliver you the content you are interested in, e.g.: &lt;a href="https://www.kiss-code.com/products"&gt;kiss-code.com/products&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While there are great tools out there to do this, I'm stubborn and attempt to build everything myself. It takes a lot of time without guarantees to success. But it's a way for me to completely immerse myself in new fields like e-commerce or marketing.&lt;/p&gt;

&lt;p&gt;When I see existing tools, I think:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"How do they make this work?"&lt;/li&gt;
&lt;li&gt;"Could / should I make a small, customized version of this?"&lt;/li&gt;
&lt;li&gt;"What metrics do I need to collect &amp;amp; how?".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't just do that for fun, I often do need a customized version of it for my current projects. So, I try to build my project as re-usable as possible. Existing of end-to-end modules, packages / libraries, ...&lt;/p&gt;

&lt;p&gt;What about you?&lt;/p&gt;

&lt;p&gt;If you are interested in my work, you can find it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on my website: &lt;a href="https://www.kiss-code.com/products"&gt;kiss-code.com/products&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on my &lt;a href="https://www.patreon.com/kisstupid"&gt;Patreon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on &lt;a href="https://www.youtube.com/@kis.stupid"&gt;YouTube&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Subscribe to my newsletter to stay up-to-date &amp;amp; receive discounts.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>emailmarketing</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Misconception about 'AllowedHosts' in a .NET C# Web API</title>
      <dc:creator>kis.stupid</dc:creator>
      <pubDate>Mon, 09 Oct 2023 18:46:24 +0000</pubDate>
      <link>https://dev.to/kiss_code/misconception-about-allowedhosts-in-a-net-c-web-api-4mnf</link>
      <guid>https://dev.to/kiss_code/misconception-about-allowedhosts-in-a-net-c-web-api-4mnf</guid>
      <description>&lt;p&gt;Did you ever come across the &lt;code&gt;"AllowedHosts": "*"&lt;/code&gt; setting in a &lt;strong&gt;server-side&lt;/strong&gt; .NET  project and wondered what it does?&lt;br&gt;
I have. I did my due diligence on the subject, but once in production, I was proven &lt;strong&gt;wrong&lt;/strong&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  What is 'AllowedHosts'?
&lt;/h4&gt;

&lt;p&gt;It is the configuration for the ASP.NET Core host filtering &lt;strong&gt;middleware&lt;/strong&gt;.&lt;br&gt;
With this setting, you can specify one or more host names from which your project allows incoming HTTP requests. Requests without a matching &lt;strong&gt;host name&lt;/strong&gt; will be refused.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A host name is the domain name of the target server, the request is sent to.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's say you have an API running on &lt;code&gt;https://www.weather-api.com&lt;/code&gt;. The host name would be &lt;code&gt;weather-api.com&lt;/code&gt;. You could specify to only allow requests with that host name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;AllowedHosts:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"weather-api.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or to allow multiple host names &lt;strong&gt;semicolon&lt;/strong&gt; delimited.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;AllowedHosts:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"weather-api.com;v1.weather-api.com;v2.weather-api.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use a wildcard.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;AllowedHosts:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*.weather-api.com"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above will &lt;strong&gt;allow&lt;/strong&gt; requests with a correct host name and &lt;strong&gt;disallow&lt;/strong&gt; requests without matching host names.&lt;br&gt;
A host name is set on a request as an HTTP header: &lt;code&gt;Host: weather-api.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Doesn't this sound similar as &lt;strong&gt;CORS&lt;/strong&gt; to you?&lt;/p&gt;
&lt;h4&gt;
  
  
  What is CORS?
&lt;/h4&gt;

&lt;p&gt;In short, the ASP.NET Core CORS (Cross &lt;strong&gt;Origin&lt;/strong&gt; Resource Sharing) &lt;strong&gt;middleware&lt;/strong&gt; can be configured to allow requests &lt;strong&gt;originating&lt;/strong&gt; from different domains to interact with server-side .NET project.&lt;/p&gt;

&lt;p&gt;By default, without this middleware, a server-side project only allows &lt;code&gt;same-origin&lt;/code&gt; requests. These are requests &lt;strong&gt;originating&lt;/strong&gt; from the same domain as the one, the server-side project is running on.&lt;/p&gt;

&lt;p&gt;So, the CORS middleware can be configured to enable &lt;code&gt;cross-origin&lt;/code&gt; requests from applications deployed on a different domain. The &lt;strong&gt;originating&lt;/strong&gt; domain name is also set on a request as an HTTP header: &lt;code&gt;Origin: https://www.morning-walk.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The origin header is set to the domain, the request originated from&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Allowing requests from the above origin can be configured as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseCors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithOrigins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://www.morning-walk.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AllowAnyMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AllowAnyHeader&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h4&gt;
  
  
  What's the difference?
&lt;/h4&gt;

&lt;p&gt;Requests made by a &lt;strong&gt;browser&lt;/strong&gt; usually sets both the &lt;code&gt;Host&lt;/code&gt; and &lt;code&gt;Origin&lt;/code&gt; headers. Requests made by tools like &lt;strong&gt;Postman&lt;/strong&gt; or by someone running a project &lt;strong&gt;locally&lt;/strong&gt;, will only set the &lt;code&gt;Host&lt;/code&gt; header or none of the above.&lt;/p&gt;

&lt;p&gt;When there is no &lt;code&gt;Origin&lt;/code&gt; header present, the CORS middleware will not intervene. The host filtering middleware could intervene and return a &lt;code&gt;400 Bad Request&lt;/code&gt; when the host name does not match.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Got it! Both middlewares can allow or disallow requests based on their HTTP header. So ... I can protect my API from unwanted origins and unwanted hosts?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not entirely ... Unfortunately the host filtering middleware cannot be used in the same way as the CORS middleware. The host filtering middleware allows &lt;strong&gt;any&lt;/strong&gt; request as long as the &lt;code&gt;Host&lt;/code&gt; header is correctly pointing to its target server.&lt;/p&gt;

&lt;p&gt;Setting &lt;code&gt;"AllowedHosts": "*"&lt;/code&gt; or &lt;code&gt;"AllowedHosts": "weather-api.com"&lt;/code&gt; would not make much of a difference if the goal is to block requests.&lt;/p&gt;

&lt;p&gt;Once in production, inspecting the HTTP headers:&lt;/p&gt;

&lt;p&gt;Request 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host: weather-api.com
Origin: https://www.morning-walk.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Request 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host: weather-api.com
Origin: https://kiss-code.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do you see how you could block requests based on its origin but not based on the host?&lt;/p&gt;

&lt;h4&gt;
  
  
  Misconception
&lt;/h4&gt;

&lt;p&gt;I assumed the host name and origin name would come from the same domain, which is &lt;strong&gt;wrong&lt;/strong&gt;. I approached the host filtering in the same way as the CORS configuration. I assumed the values below and tried to filter hosts based on it.&lt;/p&gt;

&lt;p&gt;Request 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host: morning-walk.com
Origin: https://www.morning-walk.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Request 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host: kiss-code.com
Origin: https://kiss-code.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If you are interested in more of my work, you can find it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on my website: &lt;a href="https://www.kiss-code.com/products"&gt;kiss-code.com/products&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on my &lt;a href="https://www.patreon.com/kisstupid"&gt;Patreon&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;on &lt;a href="https://www.youtube.com/@kis.stupid"&gt;YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Subscribe to my &lt;a href="https://www.kiss-code.com"&gt;newsletter&lt;/a&gt; to stay up-to-date &amp;amp; receive discounts.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>api</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
