<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>ASP.NET on Techformist</title>
    <link>https://techformist.com/tags/asp.net/</link>
    <description>Recent content in ASP.NET on Techformist</description>
    <image>
      <url>https://techformist.com/logo.svg</url>
      <link>https://techformist.com/logo.svg</link>
    </image>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Tue, 01 Oct 2024 06:30:00 +0000</lastBuildDate><atom:link href="https://techformist.com/tags/asp.net/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Identity in .NET API Makes Auth Easy</title>
      <link>https://techformist.com/2024-10-01-identity-dotnet-api-auth-made-easy/</link>
      <pubDate>Tue, 01 Oct 2024 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/2024-10-01-identity-dotnet-api-auth-made-easy/</guid>
      <description>&lt;p&gt;There was a time to be scared of the auth in ASP.NET. Identity really makes it easy.
But before delving any further, let&amp;rsquo;s keep the tradition alive by knowing the ancient Roman history of auth in ASP.NET.&lt;/p&gt;
&lt;h2 id=&#34;how-auth-was-done-earlier&#34;&gt;How Auth was done earlier?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Manual Token Generation: Developers manually created JWT tokens using libraries like System.IdentityModel.Tokens.Jwt and hardcoded key management.&lt;/li&gt;
&lt;li&gt;No Built-in User Management: Handling user registration, login, password hashing, and role management required custom code.&lt;/li&gt;
&lt;li&gt;Manual Claims Management: Claims (roles, permissions) were added to JWT tokens manually, increasing the risk of errors.&lt;/li&gt;
&lt;li&gt;Token Validation: Developers manually validated JWT tokens in each request, including signature, expiration, and claims validation.&lt;/li&gt;
&lt;li&gt;No Built-in Features for Role Management: Handling user roles and claims for authorization were complex&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;rsquo;s not uncommon to see code like this even today (there continue to be valid use cases, of course).&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>There was a time to be scared of the auth in ASP.NET. Identity really makes it easy.
But before delving any further, let&rsquo;s keep the tradition alive by knowing the ancient Roman history of auth in ASP.NET.</p>
<h2 id="how-auth-was-done-earlier">How Auth was done earlier?</h2>
<ul>
<li>Manual Token Generation: Developers manually created JWT tokens using libraries like System.IdentityModel.Tokens.Jwt and hardcoded key management.</li>
<li>No Built-in User Management: Handling user registration, login, password hashing, and role management required custom code.</li>
<li>Manual Claims Management: Claims (roles, permissions) were added to JWT tokens manually, increasing the risk of errors.</li>
<li>Token Validation: Developers manually validated JWT tokens in each request, including signature, expiration, and claims validation.</li>
<li>No Built-in Features for Role Management: Handling user roles and claims for authorization were complex</li>
</ul>
<p>It&rsquo;s not uncommon to see code like this even today (there continue to be valid use cases, of course).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">key</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SymmetricSecurityKey</span><span class="p">(</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="n">GetBytes</span><span class="p">(</span><span class="s">&#34;YourSecretKey&#34;</span><span class="p">));</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">creds</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SigningCredentials</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">SecurityAlgorithms</span><span class="p">.</span><span class="n">HmacSha256</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">claims</span> <span class="p">=</span> <span class="k">new</span><span class="p">[]</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">Claim</span><span class="p">(</span><span class="n">JwtRegisteredClaimNames</span><span class="p">.</span><span class="n">Sub</span><span class="p">,</span> <span class="s">&#34;username&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="k">new</span> <span class="n">Claim</span><span class="p">(</span><span class="n">JwtRegisteredClaimNames</span><span class="p">.</span><span class="n">Jti</span><span class="p">,</span> <span class="n">Guid</span><span class="p">.</span><span class="n">NewGuid</span><span class="p">().</span><span class="n">ToString</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">token</span> <span class="p">=</span> <span class="k">new</span> <span class="n">JwtSecurityToken</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">    <span class="n">issuer</span><span class="p">:</span> <span class="s">&#34;yourdomain.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">audience</span><span class="p">:</span> <span class="s">&#34;yourdomain.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">claims</span><span class="p">:</span> <span class="n">claims</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="n">expires</span><span class="p">:</span> <span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">.</span><span class="n">AddMinutes</span><span class="p">(</span><span class="m">30</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">    <span class="n">signingCredentials</span><span class="p">:</span> <span class="n">creds</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// create token</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">tokenString</span> <span class="p">=</span> <span class="k">new</span> <span class="n">JwtSecurityTokenHandler</span><span class="p">().</span><span class="n">WriteToken</span><span class="p">(</span><span class="n">token</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// time passes..</span>
</span></span><span class="line"><span class="cl"><span class="c1">//</span>
</span></span><span class="line"><span class="cl"><span class="c1">// time now to validate in another part of the code..</span>
</span></span><span class="line"><span class="cl"><span class="c1">// validate token</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">services</span><span class="p">.</span><span class="n">AddAuthentication</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">options</span><span class="p">.</span><span class="n">DefaultAuthenticateScheme</span> <span class="p">=</span> <span class="n">JwtBearerDefaults</span><span class="p">.</span><span class="n">AuthenticationScheme</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="n">options</span><span class="p">.</span><span class="n">DefaultChallengeScheme</span> <span class="p">=</span> <span class="n">JwtBearerDefaults</span><span class="p">.</span><span class="n">AuthenticationScheme</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="cl">    <span class="p">.</span><span class="n">AddJwtBearer</span><span class="p">(</span><span class="n">options</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">options</span><span class="p">.</span><span class="n">TokenValidationParameters</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TokenValidationParameters</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">ValidateIssuer</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">ValidateAudience</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">ValidateLifetime</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">ValidateIssuerSigningKey</span> <span class="p">=</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">ValidIssuer</span> <span class="p">=</span> <span class="s">&#34;yourdomain.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">ValidAudience</span> <span class="p">=</span> <span class="s">&#34;yourdomain.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">            <span class="n">IssuerSigningKey</span> <span class="p">=</span> <span class="k">new</span> <span class="n">SymmetricSecurityKey</span><span class="p">(</span><span class="n">Encoding</span><span class="p">.</span><span class="n">UTF8</span><span class="p">.</span><span class="n">GetBytes</span><span class="p">(</span><span class="s">&#34;YourSecretKey&#34;</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span></code></pre></div><h2 id="roll-in-identity">Roll-in Identity</h2>
<ul>
<li>Built-in User and Role Management
<ul>
<li>Identity provides a complete user management system with built-in support for user registration, login, password hashing, and role management.</li>
<li>No need to manually store user credentials or roles in the database; Identity handles it through its own entity models (e.g., IdentityUser, IdentityRole).</li>
</ul>
</li>
<li>Automatic Token Generation and Validation
<ul>
<li>Identity integrates easily with JWT authentication, automatically handling token generation, validation, and expiration management.</li>
<li>Developers no longer need to manually create or validate tokens using JwtSecurityTokenHandler.</li>
</ul>
</li>
<li>Claims and Role Management: Manage claims and roles through simple, high-level APIs (UserManager, RoleManager), which abstract away the complexity of managing claims in the token payload manually.</li>
<li>Out-of-the-Box (&ldquo;OOB&rdquo;) Security
<ul>
<li>OOB industry-standard security practices such as password hashing (with customizable password policies) and token expiration management - reduce security risks.</li>
<li>Built-in support for multi-factor authentication (MFA), account lockout, and password recovery without custom implementations.</li>
</ul>
</li>
<li>Simplified Middleware Integration
<ul>
<li>Easily integrated into the ASP.NET middleware pipeline - no need to manually configure authentication schemes or token validation parameters.</li>
<li>Built-in Identity middleware automatically manages authentication cookies and tokens.</li>
</ul>
</li>
<li>Flexible Data Store: Work with multiple data stores (SQL Server, MySQL, etc.) by default</li>
<li>Support for External Authentication Providers: Integrating OAuth providers like Google, Facebook, and Microsoft is simplified</li>
</ul>
<h2 id="identity-in-action-a-simple-demo">Identity in Action: A Simple Demo</h2>
<p>Create a new project..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dotnet new webapi -n dotnet-api-auth-demo
</span></span></code></pre></div><p>Add the below packages..</p>
<ul>
<li>Microsoft.EntityFrameworkCore.Sqlite</li>
<li>Microsoft.EntityFrameworkCore.Design</li>
<li>Microsoft.EntityFrameworkCore.Tools</li>
<li>Microsoft.AspNetCore.Identity.EntityFrameworkCore</li>
</ul>
<p>The first three are for Entity Framework Core and for using a SQLite database. The last one is for Identity.</p>
<p>Create the <code>ApplicationDbContext</code> class like as God intended. However, inherit from <code>IdentityDbContext</code> this time.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Identity</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">Microsoft.AspNetCore.Identity.EntityFrameworkCore</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="nn">Microsoft.EntityFrameworkCore</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">class</span> <span class="nc">ApplicationDbContext</span> <span class="p">:</span> <span class="n">IdentityDbContext</span><span class="p">&lt;</span><span class="n">IdentityUser</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">public</span> <span class="n">ApplicationDbContext</span><span class="p">(</span><span class="n">DbContextOptions</span><span class="p">&lt;</span><span class="n">ApplicationDbContext</span><span class="p">&gt;</span> <span class="n">options</span><span class="p">)</span> <span class="p">:</span> <span class="k">base</span><span class="p">(</span><span class="n">options</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>IdentityDbContext</code> will add magic that we are about to experience.</p>
<p>Add three lines of code in <code>Program.cs</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddAuthorization</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddIdentityApiEndpoints</span><span class="p">&lt;</span><span class="n">IdentityUser</span><span class="p">&gt;().</span><span class="n">AddEntityFrameworkStores</span><span class="p">&lt;</span><span class="n">ApplicationDbContext</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl"><span class="c1">// add the above lines before initializing `app`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">Build</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// add the below line after `app` is initialized</span>
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">MapIdentityApi</span><span class="p">&lt;</span><span class="n">IdentityUser</span><span class="p">&gt;();</span>
</span></span></code></pre></div><ul>
<li><code>AddEntityFrameworkStores</code> shows which database to use. In this case, we are using SQLite and using the main <code>ApplicationDbContext</code> itself. We could have used a different auth database as easily.</li>
<li><code>MapIdentityApi</code> automatically adds the necessary routes for Identity.</li>
</ul>
<p>Since we have no other endpoints, add auth to the default <code>weatherforecast</code> endpoint using <code>RequireAuthorization()</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">MapGet</span><span class="p">(</span><span class="s">&#34;/weatherforecast&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kt">var</span> <span class="n">forecast</span> <span class="p">=</span> <span class="n">Enumerable</span><span class="p">.</span><span class="n">Range</span><span class="p">(</span><span class="m">1</span><span class="p">,</span> <span class="m">5</span><span class="p">).</span><span class="n">Select</span><span class="p">(</span><span class="n">index</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="k">new</span> <span class="n">WeatherForecast</span>
</span></span><span class="line"><span class="cl">        <span class="p">(</span>
</span></span><span class="line"><span class="cl">            <span class="n">DateOnly</span><span class="p">.</span><span class="n">FromDateTime</span><span class="p">(</span><span class="n">DateTime</span><span class="p">.</span><span class="n">Now</span><span class="p">.</span><span class="n">AddDays</span><span class="p">(</span><span class="n">index</span><span class="p">)),</span>
</span></span><span class="line"><span class="cl">            <span class="n">Random</span><span class="p">.</span><span class="n">Shared</span><span class="p">.</span><span class="n">Next</span><span class="p">(-</span><span class="m">20</span><span class="p">,</span> <span class="m">55</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="n">summaries</span><span class="p">[</span><span class="n">Random</span><span class="p">.</span><span class="n">Shared</span><span class="p">.</span><span class="n">Next</span><span class="p">(</span><span class="n">summaries</span><span class="p">.</span><span class="n">Length</span><span class="p">)]</span>
</span></span><span class="line"><span class="cl">        <span class="p">))</span>
</span></span><span class="line"><span class="cl">        <span class="p">.</span><span class="n">ToArray</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">forecast</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">})</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="n">WithName</span><span class="p">(</span><span class="s">&#34;GetWeatherForecast&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="n">WithOpenApi</span><span class="p">().</span><span class="n">RequireAuthorization</span><span class="p">();</span>
</span></span></code></pre></div><p>Do a build to ensure there are no errors.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dotnet build
</span></span></code></pre></div><p>Migrate the database.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dotnet ef migrations add InitAuth -o Data/Migrations
</span></span><span class="line"><span class="cl">dotnet ef database update
</span></span></code></pre></div><p>Run the application.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dotnet run
</span></span></code></pre></div><p>Navigate to <code>http://localhost:5001/swagger/index.html</code> to see the Identity endpoints.</p>
<p><img loading="lazy" src="/2024/swagger-dotnet-identity-endpoints.png" type="" alt="swagger-dotnet-identity-endpoints"  /></p>
<p>You can test the auth endpoints using Swagger, or as I prefer - Insomnia/Postman REST clients.</p>
<p>First, do a GET to <code>http://localhost:5001/weatherforecast</code> to see a 401 Unauthorized response.</p>
<p><img loading="lazy" src="/2024/unauthorized-identity-endpoint.png" type="" alt="unauthorized-identity-endpoints"  /></p>
<p>Now, call the <code>register</code> to create a user, and subsequently, the <code>login</code> endpoint to get a token.</p>
<p>POST http://localhost:5001/register</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;email&#34;</span><span class="p">:</span> <span class="s2">&#34;a1@a.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;Pass@123&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>POST http://localhost:5001/login</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;email&#34;</span><span class="p">:</span> <span class="s2">&#34;a1@a.com&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;password&#34;</span><span class="p">:</span> <span class="s2">&#34;Pass@123&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>You should get the token response.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;tokenType&#34;</span><span class="p">:</span> <span class="s2">&#34;Bearer&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;accessToken&#34;</span><span class="p">:</span> <span class="s2">&#34;CfDJ8AcfSlDrZbhNlphsDvuO8gDgQty5M...&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;expiresIn&#34;</span><span class="p">:</span> <span class="mi">3600</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;refreshToken&#34;</span><span class="p">:</span> <span class="s2">&#34;CfDJ8AcfSlDrZbhNlphsDvuO8gD60O...&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Use the access token to call the <code>weatherforecast</code> endpoint and see the glorious response.</p>
<p><img loading="lazy" src="/2024/identity-asp-dotnet-token-api-call.png" type="" alt="identity-asp-dotnet-token-api-call"  /></p>
<p>We have just scratched the surface of Identity in .NET. There is a lot more to explore, including:</p>
<ul>
<li>Account lockout/ recovery</li>
<li>Password policies</li>
<li>Claims and roles management</li>
<li>Customizing Identity</li>
<li>Using external providers</li>
<li>Role-based authorization</li>
<li>Multi-factor authentication</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Check out the full code on <a href="https://github.com/techformist/dotnet-api-auth-demo/">GitHub</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Dotnet 6 is Refreshingly Simple</title>
      <link>https://techformist.com/dotnet6-refreshingly-simple/</link>
      <pubDate>Mon, 07 Feb 2022 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/dotnet6-refreshingly-simple/</guid>
      <description>&lt;p&gt;There are numerous things to love about the new .NET 6, but for me one key thing stands out - .NET now seems more approachable than ever!&lt;/p&gt;
&lt;p&gt;Take a straight-forward example. A new .NET Web API project would look like this -&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;startup.cs&lt;/code&gt; file with generated code&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Program.cs&lt;/code&gt; file with more lines of code&lt;/li&gt;
&lt;li&gt;A lot of other files&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The two file dependencies induced that warm fuzzy feeling in ASP.NET developers for sometime now.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>There are numerous things to love about the new .NET 6, but for me one key thing stands out - .NET now seems more approachable than ever!</p>
<p>Take a straight-forward example. A new .NET Web API project would look like this -</p>
<ol>
<li>A <code>startup.cs</code> file with generated code</li>
<li>A <code>Program.cs</code> file with more lines of code</li>
<li>A lot of other files</li>
</ol>
<p>The two file dependencies induced that warm fuzzy feeling in ASP.NET developers for sometime now.</p>
<p>In addition, you have code that uses a wierd <code>using</code> syntax everywhere with brackets. Sure, it creates better compartmentalized &amp; namespaced code, but did not help calm my nerves when I was evaluating ten different back end technologies to make a quick start (a while ago).</p>
<p>C# 10 and the new ASP.NET 6 will do wonders in the &ldquo;make code look deceptively simple&rdquo; area.</p>
<p>There is no <code>startup.cs</code>. The <code>Program.cs</code> file now looks cool with the new features -</p>
<ul>
<li>top-level statements</li>
<li>global <code>using</code> directives</li>
<li>file scope namespace declarations (bear with me here)</li>
</ul>
<p>A minimal API structure now looks like this (<code>Program.cs</code> file) -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="n">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">Build</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">MapGet</span><span class="p">(</span><span class="s">&#34;/&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="s">&#34;Hello World!&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">Run</span><span class="p">();</span>
</span></span></code></pre></div><p>A full-blown MVC web API application from the new template in .NET 6 looks like this..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">builder</span> <span class="p">=</span> <span class="n">WebApplication</span><span class="p">.</span><span class="n">CreateBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Add services to the container.</span>
</span></span><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddControllers</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">builder</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">AddEndpointsApiExplorer</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="n">Build</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">UseHttpsRedirection</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">UseAuthorization</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">MapControllers</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">app</span><span class="p">.</span><span class="n">Run</span><span class="p">();</span>
</span></span></code></pre></div><p>&ldquo;Whoa..!&rdquo; is right.</p>
<p>I am not proud of the fact that the additional statements &amp; brackets were putting me off a long time. While I love C# for what it can do, I seldom choose C# for personal projects for all the silly reasons - I did not need the complexity, did not like to do lot of typing or &ldquo;auto filling&rdquo;, and did not have a lot of patience for code scrolling tens of pages.</p>
<p>NodeJS simply offered a better alternative, was way simpler to start (especially in Fastify or Express), and came at a cost that did not quite matter to me. And, I am a happy Javascript user - no one will take that away from me.</p>
<p>I am a fan of the new ASP.NET structure - for probably all the wrong reasons for a &ldquo;real&rdquo; developer. I will probably delve more into how a further &ldquo;simplified&rdquo; folder structure can make the ASP.NET world more exciting to work on. The &ldquo;traditional&rdquo; structure has been full of folders and a lot of files. See the <a href="https://github.com/dotnet-architecture/eShopOnWeb/tree/main/src/Web">eShopOnWeb example on Github</a>. The <a href="https://gist.github.com/davidfowl/ff1addd02d239d2d26f4648a06158727">minimal API structure at a glance by Martin Fowler</a> is a good start for a new approach to build Web APIs on .NET 6, but what is even more interesting to me is the <a href="https://timdeschryver.dev/blog/maybe-its-time-to-rethink-our-project-structure-with-dot-net-6">module-based architecture</a> outlined in this post by Tim Deschryver.</p>
<pre tabindex="0"><code>WebApplication
│   appsettings.json
│   Program.cs
│   GlobalImports.cs
│   WebApplication.csproj
│
├───Modules
│   ├───Cart
│   │      CartModule.cs
│   └───Orders
│       │   OrdersModule.cs
│       ├───Endpoints
│       │       GetOrders.cs
│       │       PostOrder.cs
│       ├───Core
│       │       Order.cs
│       │───Ports
│       │       IOrdersRepository.cs
│       │       IPaymentService.cs
│       └───Adapters
│               OrdersRepository.cs
│               PaymentService.cs
</code></pre><p>Awesome time this.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>A quick start on ASP.NET Core Razor Pages</title>
      <link>https://techformist.com/asp-net-razor-pages-quick-start/</link>
      <pubDate>Wed, 05 Feb 2020 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/asp-net-razor-pages-quick-start/</guid>
      <description>&lt;p&gt;Here&amp;rsquo;s a quick introduction to Razor pages in ASP.NET core, and an opinionated way to quickly start building applications using Razor pages. And yes, there is a case for using Razor pages even in 2020.&lt;/p&gt;
&lt;h3 id=&#34;so-aspnet&#34;&gt;So.. ASP.net?&lt;/h3&gt;
&lt;p&gt;Yes, indeed. I have had a love-hate relationship with ASP.NET through years. I am way less productive using ASP.net but cannot ignore the speed that a dotnet web server provides. Take into account the super debugging capabilities/tooling and the sizeable market that keeps providing projects on the platform, we surely have more than a winner in ASP.NET.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Here&rsquo;s a quick introduction to Razor pages in ASP.NET core, and an opinionated way to quickly start building applications using Razor pages. And yes, there is a case for using Razor pages even in 2020.</p>
<h3 id="so-aspnet">So.. ASP.net?</h3>
<p>Yes, indeed. I have had a love-hate relationship with ASP.NET through years. I am way less productive using ASP.net but cannot ignore the speed that a dotnet web server provides. Take into account the super debugging capabilities/tooling and the sizeable market that keeps providing projects on the platform, we surely have more than a winner in ASP.NET.</p>
<p>And oh, it immensely helps that innovations like Blazor are exciting options to make web development arguably more productive and easier.</p>
<h2 id="why-yet-another-introduction">Why yet another introduction?</h2>
<p>There are a million other introductions to ASP.NET platform. Microsoft&rsquo;s own documentation and guides are immensely helpful for beginners at all stages. But, what I love to see is a quick way to create an app (or two) and make new developers comfortable with the ecosystem. At least that&rsquo;s how I learn stuff and I found some of the tutorials I glanced through to be either too detailed, or at a superficially high level.</p>
<p>This introduction is more for developers who use a different language for web development. It is not quite intended for absolute beginners, but anyone should be able to follow along.</p>
<h2 id="firing-off-with-aspnet">Firing off with ASP.net</h2>
<p>There are two main ways to get started on development using ASP.net platform.</p>
<ol>
<li>Use Visual Studio: proven way that has been forever</li>
<li>Use Visual Studio Code or your favourite editor and dotnet core CLI</li>
</ol>
<p>Both ways deserve all the love they can get. I prefer VSCode, but will use Visual Studio in this tutorial because - why not. Going forward I will be basing discussions primarily on Visual Studio 2019 and dotnet Core 3.1.</p>
<p>Installation is somewhat boring and works on expected lines.</p>
<ol>
<li>Download Visual Studio from <a href="https://visualstudio.microsoft.com/">https://visualstudio.microsoft.com/</a></li>
<li>Click, click, click to install</li>
</ol>
<p>Visual Studio comes with dotnet SDK, and you should be all set by now. If you decide to use VSCode, just install the dotnet SDK separately. You should then be able to use all the command line goodness on the best code editor ever(tm).</p>
<p>See the <a href="https://dotnet.microsoft.com/learn/dotnet/hello-world-tutorial/install">install docs</a> if you are stuck. (Stuck during install, really?)</p>
<p>Once everything&rsquo;s ready, just create a new project.</p>
<p><img loading="lazy" src="/misc/dotnet-razor-pages-new-project.jpg" type="" alt="dotnet-razor-pages-new-project"  /></p>
<p>Select &ldquo;ASP.NET Core Web Application&rdquo;. Hit next and name your project. You will get another screen where you can select an &ldquo;Web Application&rdquo; template. This option will instruct Visual Studio to scaffold a few things when the project is created.</p>
<h2 id="the-structure">The Structure</h2>
<p>As any modern web development stack, the files generated can be overwhelming at the beginning. We can break the structure down to basics.</p>
<p>A typical ASP.net project consists of one solution (which is a container for projects), and one or more projects. It will have at least one project (of course), anything else is optional and enables you to build stuff incrementally.</p>
<p><img loading="lazy" src="/misc/project-structure-visual-studio-asp-net-core-razor.jpg" type="" alt="project-structure-visual-studio-asp-net-core-razor"  /></p>
<h3 id="beginning-of-everything-startupcs-and-programcs">Beginning of everything: <code>Startup.cs</code> and <code>Program.cs</code></h3>
<p>These are the two files that bootstrap our app and give it ASP powers. When our app starts, the runtime calls <code>Main</code> method defined in <code>Program.cs</code>, which configures itself and starts running as an ASP app.</p>
<p>If you were so far wondering how our humble app came to acquire ASP powers, <code>IHostBuilder</code> is to blame. That interface provides our function <code>CreateHostBuilder</code> abilities to assume the powers of ASP.net, configure itself and start running - all in a simple one-line statement..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="n">CreateHostBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">).</span><span class="n">Build</span><span class="p">().</span><span class="n">Run</span><span class="p">();</span>
</span></span></code></pre></div><p>More code in <code>Program.cs</code> uses <a href="https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions">C# lambdas</a> and <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1">Dependency Injection</a> (&ldquo;DI&rdquo;) to apply configuration parameters and methods defined in <code>Startup.cs</code> to our application.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="n">IHostBuilder</span> <span class="n">CreateHostBuilder</span><span class="p">(</span><span class="kt">string</span><span class="p">[]</span> <span class="n">args</span><span class="p">)</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Host</span><span class="p">.</span><span class="n">CreateDefaultBuilder</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="p">.</span><span class="n">ConfigureWebHostDefaults</span><span class="p">(</span><span class="n">webBuilder</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">{</span>
</span></span><span class="line"><span class="cl">                <span class="n">webBuilder</span><span class="p">.</span><span class="n">UseStartup</span><span class="p">&lt;</span><span class="n">Startup</span><span class="p">&gt;();</span>
</span></span><span class="line"><span class="cl">            <span class="p">});</span>
</span></span></code></pre></div><p>Further, <code>ConfigureServices</code> in <code>Startup.cs</code> has a specific block that enables Razor pages in your app.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">ConfigureServices</span><span class="p">(</span><span class="n">IServiceCollection</span> <span class="n">services</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">services</span><span class="p">.</span><span class="n">AddRazorPages</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The <code>Configure</code> method in <code>Startup.cs</code> enables a pipeline to include middleware, which in-turn enables various features for your app - all without needing you to code them in.</p>
<p>For e.g. -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="kd">public</span> <span class="k">void</span> <span class="n">Configure</span><span class="p">(</span><span class="n">IApplicationBuilder</span> <span class="n">app</span><span class="p">,</span> <span class="n">IWebHostEnvironment</span> <span class="n">env</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...</span>
</span></span><span class="line"><span class="cl">    <span class="n">app</span><span class="p">.</span><span class="n">UseHttpsRedirection</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// ...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>&hellip; tells runtime to redirect HTTP requests to HTTPS.</p>
<p>We will deal with lambda functions, DI etc. in more detail some other time. For now, we will move on and find out how we can quickly create our app on ASP.net.</p>
<h3 id="before-we-dig-in-settings-and-launchsettingsjson">Before we dig-in: settings and <code>launchSettings.json</code></h3>
<p>We have created a web application that runs as a command line app. The server is just serving content defined in web stuff like HTML/JS/CSS etc and in Razor pages, but does not render UI on its own.</p>
<p>Configuration parameters for our app is defined in <code>launchSettings.json</code>.</p>
<p>You can see the values defined in settings file in a nice user interface - right-click on the project (not the solution) in the right-hand-side bar, and select properties. Any change in one place is reflected in the other - just sayin'.</p>
<p>You may also notice the word &ldquo;development&rdquo; in the configuration file. We can provide parameters for any number of environments including development, and the start-up logic figures out the settings to apply. Further, secrets can be managed quite efficiently and made accessible to those who &ldquo;need to know&rdquo;. This is super helpful as you might imagine.</p>
<p>By default, the app uses IISExpress but there are options to use Kestrel, which is a light-weight (&amp; a more modern) server by Microsoft. You will see these configuration options selected in the Toolbar as well. Typically, I prefer Kestrel since it is fast, and I end up hosting dotnet on Linux (where IIS is not available).</p>
<h3 id="the-client-facing-wwwroot">The &lsquo;client-facing&rsquo; <code>wwwroot</code></h3>
<p>All the client-facing assets are stored in the <code>www</code> folder. This includes CSS, JS (custom and any libraries), static HTML, and anything else that finds its way to the browser.</p>
<p>Right off the bat, you see a sample CSS and JS, bootstrap and JQuery in the scaffolded files. Any and all of this can be changed or replaced. For e.g. you can replace Bootstrap with Bulma and chug along a different path.</p>
<h3 id="pages-folder"><code>Pages</code> folder</h3>
<p>Most of the action takes place here. <code>Pages</code> is where you place razor pages created using C#. The pages in C# get converted to something that the browser can understand by ASP.NET – we will see way more of pages in a few moments. Razor pages have the extension &lsquo;cshtml&rsquo; files</p>
<p>You also see a <code>Shared</code> folder and a bunch of files starting with <code>_</code>. Shared folder is used to contain assets shared across the project, and the <code>_</code> is a convention to create something called a &lsquo;partial&rsquo;. A partial is not a &lsquo;complete&rsquo; page in itself, but comes together with other partials or pages to form complete pages.</p>
<p><code>Layout.cshtml</code> is an example of a partial. In this file we have the header (which forms the header in the pages using this layout), a container (which will contain data), and the footer. The file also has references to script and HTML.</p>
<p>Of course, you can use different layouts, or decide not to use layouts at all – but building pages like this will make the entire process easy to build and to maintain.</p>
<p>In other notes, take a look at -</p>
<ul>
<li><code>_ViewImports.cshtml</code>: enables tag helpers globally</li>
<li><code>_ViewStart.cshtml</code>: base/master page</li>
</ul>
<p>Error, Index and Privacy pages are the actual Razor pages. You will create something on these lines to for adding more pages to your app.</p>
<p>Expand <code>Index.cshtml</code> page to find a file called <code>Index.cshtml.cs</code>. This file has the C# code to go along with the mark-up page. You can refer to the variables, methods, etc. from this c# page in the mark-up <code>cshtml</code> page. This arrangement is akin to mark-up + code arrangement in any Vue/React/Svelte application. In Blazor, you can have mark-up + code in same or different files - but that is for a different day.</p>
<p>This structure is different from a typical MVC model that touts a distinct controller, model and view layer. These functions are present in the Razor page app, but not fashioned in MVC style.</p>
<h2 id="more-about-the-internals-and-workings-of-an-aspnet-app">More about the internals and workings of an ASP.net app</h2>
<p>We are now masters of the structure of ASP.net core razor page project, and it is time to move on and see how everything comes together.</p>
<p>If you&rsquo;re serious about your trade, you will skip this section altogether. The terms here just help you speak the same language - you will probably figure this out by yourself while building the actual app.</p>
<h3 id="routing">Routing</h3>
<p>The <code>Pages</code> folder takes care of routing in our application. The files in the folder represent the URLs served by your application.</p>
<p>Let&rsquo;s see how..</p>
<ul>
<li><code>Privacy.cshtml</code> is a file in the <code>Pages</code> folder</li>
<li><code>baseurl.com/privacy</code> will request the page named &lsquo;privacy.cshtml&rsquo; in the folder. ASP.net serves the HTML generated from the razor page.</li>
<li>Any sub-folder will also get included in the path. For e.g. a <code>blog/Hello.cshtml</code> is rendered on <code>baseurl.com/blog/Hello</code></li>
<li>If the requested page does not exist, a &lsquo;page cannot be found&rsquo; error is displayed by default. You can configure the application to redirect requests to any other page (e.g. <code>index</code>) if the page cannot be located</li>
</ul>
<p>You can override the default routing using the <code>@Page</code> tag.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cs" data-lang="cs"><span class="line"><span class="cl"><span class="n">@page</span> <span class="s">&#34;/privacy1&#34;</span>
</span></span></code></pre></div><p>.. will enable the link to <code>baseurl.com/privacy1</code> and the previously used <code>/privacy</code> page disappears into oblivion.</p>
<h2 id="start-tinkering">Start Tinkering</h2>
<p>Run your app by hitting <code>Ctrl + F5</code>. Visual Studio opens the browser (or allows you to configure browser) and serves the application. You can see the starter page and navigate through the links provided in the scaffolded application.</p>
<p>You will see that any changes will require you to build/restart app. We can avoid that by executing our app in watch mode..</p>
<pre tabindex="0"><code>cd &lt;project-folder&gt;
dotnet run watch
</code></pre>]]></content:encoded>
    </item>
    
  </channel>
</rss>
