finish lecture 3

- edit lecture 1
main
borb 4 weeks ago
parent 2eef6c4bc1
commit 62f9e20918

@ -13,13 +13,13 @@
/* buutti.css */ /* buutti.css */
/* @theme buutti */div#\:\$p>svg>foreignObject>section .columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns12{display:grid;grid-template-columns:1fr 2fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns21{display:grid;grid-template-columns:2fr 1fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns32{display:grid;grid-template-columns:3fr 2fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns23{display:grid;grid-template-columns:2fr 3fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns111{display:grid;grid-template-columns:1fr 1fr 1fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .centered{display:flex;flex-direction:column;justify-content:center;text-align:center}div#\:\$p>svg>foreignObject>section .tableborderless td,div#\:\$p>svg>foreignObject>section th{border:none!important;border-collapse:collapse}div#\:\$p>svg>foreignObject>section.extra{background-color:#5d275d;background-image:linear-gradient(to bottom,#401a40,#1d0c1d);color:white}div#\:\$p>svg>foreignObject>section.extra a{color:rgb(145,255,209)}div#\:\$p>svg>foreignObject>section.exercise{background-color:#29366f;background-image:linear-gradient(to bottom,#20636a,#173742);color:white}div#\:\$p>svg>foreignObject>section.exercise a{color:rgb(211,173,255)} /* @theme buutti */div#\:\$p>svg>foreignObject>section .columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns12{display:grid;grid-template-columns:1fr 2fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns21{display:grid;grid-template-columns:2fr 1fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns32{display:grid;grid-template-columns:3fr 2fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns23{display:grid;grid-template-columns:2fr 3fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .columns111{display:grid;grid-template-columns:1fr 1fr 1fr;gap:calc(var(--marpit-root-font-size, 1rem) * 1)}div#\:\$p>svg>foreignObject>section .centered{display:flex;flex-direction:column;justify-content:center;text-align:center}div#\:\$p>svg>foreignObject>section .tableborderless td,div#\:\$p>svg>foreignObject>section th{border:none!important;border-collapse:collapse}div#\:\$p>svg>foreignObject>section.extra{background-color:#5d275d;background-image:linear-gradient(to bottom,#401a40,#1d0c1d);color:white}div#\:\$p>svg>foreignObject>section.extra a{color:rgb(145,255,209)}div#\:\$p>svg>foreignObject>section.exercise{background-color:#29366f;background-image:linear-gradient(to bottom,#20636a,#173742);color:white}div#\:\$p>svg>foreignObject>section.exercise a{color:rgb(211,173,255)}
/* @theme zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6 */div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]{columns:initial!important;display:block!important;padding:0!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:before,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:before{display:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]{all:initial;display:flex;flex-direction:row;height:100%;overflow:hidden;width:100%}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container][data-marpit-advanced-background-direction=vertical]{flex-direction:column}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split]>div[data-marpit-advanced-background-container]{width:var(--marpit-advanced-background-split,50%)}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split=right]>div[data-marpit-advanced-background-container]{margin-left:calc(100% - var(--marpit-advanced-background-split, 50%))}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure{all:initial;background-position:center;background-repeat:no-repeat;background-size:cover;flex:auto;margin:0}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure>figcaption{position:absolute;border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;white-space:nowrap;width:1px}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content],div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo]{background:transparent!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo],div#\:\$p>svg[data-marpit-svg]>foreignObject[data-marpit-advanced-background=pseudo]{pointer-events:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background-split]{width:100%;height:100%}</style></head><body><div class="bespoke-marp-osc"><button data-bespoke-marp-osc="prev" tabindex="-1" title="Previous slide">Previous slide</button><span data-bespoke-marp-osc="page"></span><button data-bespoke-marp-osc="next" tabindex="-1" title="Next slide">Next slide</button><button data-bespoke-marp-osc="fullscreen" tabindex="-1" title="Toggle fullscreen (f)">Toggle fullscreen</button><button data-bespoke-marp-osc="presenter" tabindex="-1" title="Open presenter view (p)">Open presenter view</button></div><div id=":$p"><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="1" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="1" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> /* @theme a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol */div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]{columns:initial!important;display:block!important;padding:0!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]:before,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:after,div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content]:before{display:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]{all:initial;display:flex;flex-direction:row;height:100%;overflow:hidden;width:100%}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container][data-marpit-advanced-background-direction=vertical]{flex-direction:column}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split]>div[data-marpit-advanced-background-container]{width:var(--marpit-advanced-background-split,50%)}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background][data-marpit-advanced-background-split=right]>div[data-marpit-advanced-background-container]{margin-left:calc(100% - var(--marpit-advanced-background-split, 50%))}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure{all:initial;background-position:center;background-repeat:no-repeat;background-size:cover;flex:auto;margin:0}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=background]>div[data-marpit-advanced-background-container]>figure>figcaption{position:absolute;border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;white-space:nowrap;width:1px}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=content],div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo]{background:transparent!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background=pseudo],div#\:\$p>svg[data-marpit-svg]>foreignObject[data-marpit-advanced-background=pseudo]{pointer-events:none!important}div#\:\$p>svg>foreignObject>section[data-marpit-advanced-background-split]{width:100%;height:100%}</style></head><body><div class="bespoke-marp-osc"><button data-bespoke-marp-osc="prev" tabindex="-1" title="Previous slide">Previous slide</button><span data-bespoke-marp-osc="page"></span><button data-bespoke-marp-osc="next" tabindex="-1" title="Next slide">Next slide</button><button data-bespoke-marp-osc="fullscreen" tabindex="-1" title="Toggle fullscreen (f)">Toggle fullscreen</button><button data-bespoke-marp-osc="presenter" tabindex="-1" title="Open presenter view (p)">Open presenter view</button></div><div id=":$p"><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="1" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="1" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h1 id="introduction-to-aspnet">Introduction to ASP.NET</h1> <h1 id="introduction-to-aspnet">Introduction to ASP.NET</h1>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="2" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="2" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h2 id="getting-started-with-aspnet">Getting started with ASP.NET</h2> <h2 id="getting-started-with-aspnet">Getting started with ASP.NET</h2>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="3" data-marpit-fragments="8" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="3" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="3" data-marpit-fragments="8" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="3" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="aspnet">ASP.NET</h3> <h3 id="aspnet">ASP.NET</h3>
<ul> <ul>
<li data-marpit-fragment="1">ASP.NET is a server-side framework developed by Microsoft</li> <li data-marpit-fragment="1">ASP.NET is a server-side framework developed by Microsoft</li>
@ -35,7 +35,7 @@
</li> </li>
</ul> </ul>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="4" data-marpit-fragments="7" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="4" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="4" data-marpit-fragments="7" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="4" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="aspnet-core">ASP.NET Core</h3> <h3 id="aspnet-core">ASP.NET Core</h3>
<ul> <ul>
<li data-marpit-fragment="1"><em><strong>ASP.NET Core</strong></em> is a complete redesign &amp; rewrite of ASP.NET <li data-marpit-fragment="1"><em><strong>ASP.NET Core</strong></em> is a complete redesign &amp; rewrite of ASP.NET
@ -50,7 +50,7 @@
<li data-marpit-fragment="7">We will be focusing on Web APIs and won't be covering the frontend development tools of ASP.NET</li> <li data-marpit-fragment="7">We will be focusing on Web APIs and won't be covering the frontend development tools of ASP.NET</li>
</ul> </ul>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="5" data-marpit-fragments="10" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="5" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="5" data-marpit-fragments="10" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="5" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h4 id="why-use-aspnet-core">Why use ASP.NET Core?</h4> <h4 id="why-use-aspnet-core">Why use ASP.NET Core?</h4>
<ul> <ul>
<li data-marpit-fragment="1">As a .NET application, supports <em><strong>NuGet packages</strong></em> that can be added to your projects modularly</li> <li data-marpit-fragment="1">As a .NET application, supports <em><strong>NuGet packages</strong></em> that can be added to your projects modularly</li>
@ -77,7 +77,7 @@
</li> </li>
</ul> </ul>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="6" data-marpit-fragments="4" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="6" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="6" data-marpit-fragments="4" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="6" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="swagger--swagger-ui">Swagger &amp; Swagger UI</h3> <h3 id="swagger--swagger-ui">Swagger &amp; Swagger UI</h3>
<div class='columns32' markdown='1'> <div class='columns32' markdown='1'>
<div markdown='1'> <div markdown='1'>
@ -93,7 +93,7 @@
</div> </div>
</div> </div>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="7" data-marpit-fragments="2" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="7" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="7" data-marpit-fragments="2" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="7" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="exercise-1-creating-an-aspnet-core-web-application">Exercise 1: Creating an ASP.NET Core Web Application</h3> <h3 id="exercise-1-creating-an-aspnet-core-web-application">Exercise 1: Creating an ASP.NET Core Web Application</h3>
<ol> <ol>
@ -109,7 +109,7 @@
</div> </div>
</div> </div>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="8" data-marpit-fragments="3" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="8" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="8" data-marpit-fragments="3" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="8" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<ol start="3"> <ol start="3">
<li data-marpit-fragment="1">Give a <em>Project name</em> and set a <em>Location</em> for the repository, and check <em>Place solution and project in the same directory</em>. Click <em>Next</em> in the bottom right corner.</li> <li data-marpit-fragment="1">Give a <em>Project name</em> and set a <em>Location</em> for the repository, and check <em>Place solution and project in the same directory</em>. Click <em>Next</em> in the bottom right corner.</li>
<li data-marpit-fragment="2">Select <em>.NET 9.0</em> under <em>Framework</em>. <em>Authentication type</em> should be <em>None</em> for now. Uncheck <em>Configure for HTTPS</em>. Click <em>Create</em> in the bottom right corner.</li> <li data-marpit-fragment="2">Select <em>.NET 9.0</em> under <em>Framework</em>. <em>Authentication type</em> should be <em>None</em> for now. Uncheck <em>Configure for HTTPS</em>. Click <em>Create</em> in the bottom right corner.</li>
@ -126,7 +126,7 @@
</div> </div>
</div> </div>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="9" data-marpit-fragments="1" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="9" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="9" data-marpit-fragments="1" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="9" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<ol start="5"> <ol start="5">
<li data-marpit-fragment="1">Add <a href="https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-8.0&amp;tabs=visual-studio">Swagger</a> to your project. Go to <em>View &gt; Other Windows &gt; Package Manager Console</em> and run the following command:<pre is="marp-pre" data-auto-scaling="downscale-only"><code>Install-Package Swashbuckle.AspNetCore -Version 6.6.2 <li data-marpit-fragment="1">Add <a href="https://learn.microsoft.com/en-us/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-8.0&amp;tabs=visual-studio">Swagger</a> to your project. Go to <em>View &gt; Other Windows &gt; Package Manager Console</em> and run the following command:<pre is="marp-pre" data-auto-scaling="downscale-only"><code>Install-Package Swashbuckle.AspNetCore -Version 6.6.2
</code></pre> </code></pre>
@ -134,7 +134,7 @@
</ol> </ol>
<p><img src="imgs/2-aspnet-core-basics_4_1.png" alt="" /></p> <p><img src="imgs/2-aspnet-core-basics_4_1.png" alt="" /></p>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="10" data-marpit-fragments="1" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="10" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="10" data-marpit-fragments="1" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="10" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<ol start="6"> <ol start="6">
<li data-marpit-fragment="1">Make sure <code>Program.cs</code> includes the following lines:<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-csharp">builder.Services.AddControllers(); <li data-marpit-fragment="1">Make sure <code>Program.cs</code> includes the following lines:<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-csharp">builder.Services.AddControllers();
@ -153,11 +153,11 @@ builder.Services.AddSwaggerGen(); <span class="hljs-comment">// add th
</li> </li>
</ol> </ol>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="11" data-marpit-fragments="4" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="11" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="11" data-marpit-fragments="4" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="11" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<ol start="7"> <ol start="7">
<li data-marpit-fragment="1">Start debugging from the top (the <img class="emoji" draggable="false" alt="▶" src="https://cdn.jsdelivr.net/gh/jdecked/twemoji@14.1.2/assets/svg/25b6.svg" data-marp-twemoji=""/> button with the text <em>http</em>). <li data-marpit-fragment="1">Start debugging from the top (the <img class="emoji" draggable="false" alt="▶" src="https://cdn.jsdelivr.net/gh/jdecked/twemoji@14.1.2/assets/svg/25b6.svg" data-marp-twemoji=""/> button with the text <em>http</em>).
<ul> <ul>
<li data-marpit-fragment="2">Click OK to trust the sertificates.</li> <li data-marpit-fragment="2">Click <em>Yes</em> twice to trust the certificates.</li>
</ul> </ul>
</li> </li>
</ol> </ol>
@ -171,7 +171,7 @@ builder.Services.AddSwaggerGen(); <span class="hljs-comment">// add th
</li> </li>
</ol> </ol>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="12" data-marpit-fragments="2" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="12" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="12" data-marpit-fragments="2" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="12" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<ol start="9"> <ol start="9">
<li data-marpit-fragment="1">A web page should open, showing SwaggerUI for a weather forecast API. Click it open <img class="emoji" draggable="false" alt="🔽" src="https://cdn.jsdelivr.net/gh/jdecked/twemoji@14.1.2/assets/svg/1f53d.svg" data-marp-twemoji=""/>.</li> <li data-marpit-fragment="1">A web page should open, showing SwaggerUI for a weather forecast API. Click it open <img class="emoji" draggable="false" alt="🔽" src="https://cdn.jsdelivr.net/gh/jdecked/twemoji@14.1.2/assets/svg/1f53d.svg" data-marp-twemoji=""/>.</li>
<li data-marpit-fragment="2">Click <em>Try it out</em>, and 11. <em>Execute</em> the <strong>GET</strong> request and see what it returns.</li> <li data-marpit-fragment="2">Click <em>Try it out</em>, and 11. <em>Execute</em> the <strong>GET</strong> request and see what it returns.</li>
@ -186,7 +186,7 @@ builder.Services.AddSwaggerGen(); <span class="hljs-comment">// add th
</div> </div>
</div> </div>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="13" data-marpit-fragments="1" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="13" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="13" data-marpit-fragments="1" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="13" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<ol start="12"> <ol start="12">
<li data-marpit-fragment="1">Close the window. Browse through the source files on Solution Explorer on the right and check where the weather forecasts come from.</li> <li data-marpit-fragment="1">Close the window. Browse through the source files on Solution Explorer on the right and check where the weather forecasts come from.</li>
</ol> </ol>
@ -194,7 +194,7 @@ builder.Services.AddSwaggerGen(); <span class="hljs-comment">// add th
<p><img src="imgs/2-aspnet-core-basics_9.png" alt="" style="width:600px;" /></p> <p><img src="imgs/2-aspnet-core-basics_9.png" alt="" style="width:600px;" /></p>
</div> </div>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="14" data-marpit-fragments="5" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="14" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="14" data-marpit-fragments="5" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="14" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="aspnet-core-web-api-contents">ASP.NET Core Web API contents</h3> <h3 id="aspnet-core-web-api-contents">ASP.NET Core Web API contents</h3>
<ul> <ul>
<li data-marpit-fragment="1">In the previous exercise, we chose an API template for our new project, which have some files and dependencies already added</li> <li data-marpit-fragment="1">In the previous exercise, we chose an API template for our new project, which have some files and dependencies already added</li>
@ -207,10 +207,10 @@ builder.Services.AddSwaggerGen(); <span class="hljs-comment">// add th
<li data-marpit-fragment="5">You can use the API template for the assignments, though</li> <li data-marpit-fragment="5">You can use the API template for the assignments, though</li>
</ul> </ul>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="15" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="15" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="15" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="15" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h2 id="the-heart-of-the-server-programcs">The heart of the server: <code>Program.cs</code></h2> <h2 id="the-heart-of-the-server-programcs">The heart of the server: <code>Program.cs</code></h2>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="16" data-marpit-fragments="5" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="16" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="16" data-marpit-fragments="5" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="16" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="a-default-server-program">A default server program</h3> <h3 id="a-default-server-program">A default server program</h3>
<ul> <ul>
<li data-marpit-fragment="1">The <code>Program.cs</code> file in ASP.NET 7 is where the <em><strong>services</strong></em> for the web application are configured and the <em><strong>middleware</strong></em> is defined</li> <li data-marpit-fragment="1">The <code>Program.cs</code> file in ASP.NET 7 is where the <em><strong>services</strong></em> for the web application are configured and the <em><strong>middleware</strong></em> is defined</li>
@ -225,7 +225,7 @@ builder.Services.AddSwaggerGen(); <span class="hljs-comment">// add th
</li> </li>
</ul> </ul>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="17" data-marpit-fragments="3" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="17" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="17" data-marpit-fragments="3" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="17" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="services">Services</h3> <h3 id="services">Services</h3>
<ul> <ul>
<li data-marpit-fragment="1"> <li data-marpit-fragment="1">
@ -245,7 +245,7 @@ builder.Services.AddSwaggerGen();
</li> </li>
</ul> </ul>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="18" data-marpit-fragments="3" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="18" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="18" data-marpit-fragments="3" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="18" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="middlewares">Middlewares</h3> <h3 id="middlewares">Middlewares</h3>
<ul> <ul>
<li data-marpit-fragment="1">Handling of each HTTP request is defined as a set of <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-9.0">middlewares</a></li> <li data-marpit-fragment="1">Handling of each HTTP request is defined as a set of <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-9.0">middlewares</a></li>
@ -262,10 +262,10 @@ app.Run();
</li> </li>
</ul> </ul>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="19" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="19" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="19" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="19" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h2 id="an-example-controller-weathercontrollercs">An example controller: <code>WeatherController.cs</code></h2> <h2 id="an-example-controller-weathercontrollercs">An example controller: <code>WeatherController.cs</code></h2>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="20" data-marpit-fragments="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="20" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="20" data-marpit-fragments="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="20" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="routing-and-endpoints">Routing and endpoints</h3> <h3 id="routing-and-endpoints">Routing and endpoints</h3>
<ul> <ul>
<li data-marpit-fragment="1"><em><strong>Routing</strong></em> is how web APIs match the requested URI to a corresponding action</li> <li data-marpit-fragment="1"><em><strong>Routing</strong></em> is how web APIs match the requested URI to a corresponding action</li>
@ -298,7 +298,7 @@ app.Run();
</tbody> </tbody>
</table> </table>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="21" data-marpit-fragments="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="21" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="21" data-marpit-fragments="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="21" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="attributes">Attributes</h3> <h3 id="attributes">Attributes</h3>
<ul> <ul>
<li data-marpit-fragment="1"><em><strong>Attributes</strong></em> (<a href="/education/csharp-basics/src/branch/main/15-design-patterns-in-csharp.md#attributes">see C# Basics: Lecture 15</a>) are a way of attaching metadata to entities (classes, methods, properties, etc.)</li> <li data-marpit-fragment="1"><em><strong>Attributes</strong></em> (<a href="/education/csharp-basics/src/branch/main/15-design-patterns-in-csharp.md#attributes">see C# Basics: Lecture 15</a>) are a way of attaching metadata to entities (classes, methods, properties, etc.)</li>
@ -319,7 +319,7 @@ app.Run();
</li> </li>
</ul> </ul>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="22" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="22" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="22" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="22" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="attribute-routing">Attribute Routing</h3> <h3 id="attribute-routing">Attribute Routing</h3>
<table> <table>
<thead> <thead>
@ -353,7 +353,7 @@ app.Run();
</tbody> </tbody>
</table> </table>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="23" data-marpit-fragments="1" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="23" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="23" data-marpit-fragments="1" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="23" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="exercise-2-setting-up-routes">Exercise 2: Setting up Routes</h3> <h3 id="exercise-2-setting-up-routes">Exercise 2: Setting up Routes</h3>
<ol> <ol>
@ -364,7 +364,7 @@ instead of<br />
</ol> </ol>
<p>You can see the route change in the Swagger UI <code>GET</code> method.</p> <p>You can see the route change in the Swagger UI <code>GET</code> method.</p>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="24" data-marpit-fragments="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="24" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="24" data-marpit-fragments="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="24" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="handling-httpget-requests">Handling <code>HttpGet</code> Requests</h3> <h3 id="handling-httpget-requests">Handling <code>HttpGet</code> Requests</h3>
<ul> <ul>
<li data-marpit-fragment="1">We have now established how to call methods with HTTP requests</li> <li data-marpit-fragment="1">We have now established how to call methods with HTTP requests</li>
@ -385,7 +385,7 @@ instead of<br />
<p><img src="imgs/2-aspnet-core-basics_10.png" alt="" style="width:400px;" /></p> <p><img src="imgs/2-aspnet-core-basics_10.png" alt="" style="width:400px;" /></p>
</div> </div>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="25" data-marpit-fragments="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="25" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="25" data-marpit-fragments="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="25" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<ul> <ul>
<li data-marpit-fragment="1">The URI parameters can be made optional with '?'</li> <li data-marpit-fragment="1">The URI parameters can be made optional with '?'</li>
<li data-marpit-fragment="2">A default value must be then set for the method parameter:<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-csharp">[<span class="hljs-meta">Route(<span class="hljs-string">&quot;api&quot;</span>)</span>] <li data-marpit-fragment="2">A default value must be then set for the method parameter:<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-csharp">[<span class="hljs-meta">Route(<span class="hljs-string">&quot;api&quot;</span>)</span>]
@ -405,7 +405,7 @@ instead of<br />
<p><img src="imgs/2-aspnet-core-basics_11.png" alt="" /></p> <p><img src="imgs/2-aspnet-core-basics_11.png" alt="" /></p>
</div> </div>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="26" data-marpit-fragments="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="26" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="26" data-marpit-fragments="2" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="26" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<ul> <ul>
<li data-marpit-fragment="1">Apply constraints for the parameters by setting them after <code>:</code></li> <li data-marpit-fragment="1">Apply constraints for the parameters by setting them after <code>:</code></li>
<li data-marpit-fragment="2">If the URI doesn't fit the constraints, the response will hold a <code>404</code> status code<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-csharp">[<span class="hljs-meta">HttpGet(<span class="hljs-string">&quot;products/{id:int}&quot;</span>)</span>] <span class="hljs-comment">// Required type: int</span> <li data-marpit-fragment="2">If the URI doesn't fit the constraints, the response will hold a <code>404</code> status code<pre is="marp-pre" data-auto-scaling="downscale-only"><code class="language-csharp">[<span class="hljs-meta">HttpGet(<span class="hljs-string">&quot;products/{id:int}&quot;</span>)</span>] <span class="hljs-comment">// Required type: int</span>
@ -417,7 +417,7 @@ instead of<br />
<p><img src="imgs/2-aspnet-core-basics_12.png" alt="" /></p> <p><img src="imgs/2-aspnet-core-basics_12.png" alt="" /></p>
</div> </div>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="27" data-marpit-fragments="4" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="27" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="27" data-marpit-fragments="4" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="27" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="exercise-3-returning-your-own-list">Exercise 3: Returning Your Own List</h3> <h3 id="exercise-3-returning-your-own-list">Exercise 3: Returning Your Own List</h3>
<ol> <ol>
@ -433,10 +433,10 @@ instead of<br />
</li> </li>
</ol> </ol>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="28" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="28" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="28" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="28" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h2 id="sending-requests-with-postman">Sending requests with Postman</h2> <h2 id="sending-requests-with-postman">Sending requests with Postman</h2>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="29" data-marpit-fragments="5" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="29" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="29" data-marpit-fragments="5" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="29" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="postman">Postman</h3> <h3 id="postman">Postman</h3>
<ul> <ul>
<li data-marpit-fragment="1">HTTP <code>POST</code> requests cannot be made with the browser's address bar, only <code>GET</code>!</li> <li data-marpit-fragment="1">HTTP <code>POST</code> requests cannot be made with the browser's address bar, only <code>GET</code>!</li>
@ -449,7 +449,7 @@ instead of<br />
<p><img src="imgs/2-aspnet-core-basics_13.png" alt="" style="width:500px;" /></p> <p><img src="imgs/2-aspnet-core-basics_13.png" alt="" style="width:500px;" /></p>
</div> </div>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="30" data-marpit-fragments="4" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="invert" data-marpit-pagination="30" style="--paginate:true;--class:invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="30" data-marpit-fragments="4" data-paginate="true" data-class="invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="invert" data-marpit-pagination="30" style="--paginate:true;--class:invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="benefits-of-using-postman">Benefits of using Postman</h3> <h3 id="benefits-of-using-postman">Benefits of using Postman</h3>
<ul> <ul>
<li data-marpit-fragment="1">When developing APIs, tools like Postman will almost always surface in the development cycle</li> <li data-marpit-fragment="1">When developing APIs, tools like Postman will almost always surface in the development cycle</li>
@ -458,7 +458,7 @@ instead of<br />
<li data-marpit-fragment="4">Supports all the necessary HTTP requests, like <code>GET</code>, <code>POST</code>, <code>PUT</code> and <code>DELETE</code></li> <li data-marpit-fragment="4">Supports all the necessary HTTP requests, like <code>GET</code>, <code>POST</code>, <code>PUT</code> and <code>DELETE</code></li>
</ul> </ul>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="31" data-marpit-fragments="2" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="31" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="31" data-marpit-fragments="2" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="31" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32">
<h3 id="exercise-4-creating-requests-with-postman">Exercise 4. Creating requests with Postman</h3> <h3 id="exercise-4-creating-requests-with-postman">Exercise 4. Creating requests with Postman</h3>
<p>Run the Weather API program, and test both methods with Postman.</p> <p>Run the Weather API program, and test both methods with Postman.</p>
@ -473,7 +473,7 @@ instead of<br />
<p><img src="imgs/2-aspnet-core-basics_14.png" alt="" style="width:800px;" /></p> <p><img src="imgs/2-aspnet-core-basics_14.png" alt="" style="width:800px;" /></p>
</div> </div>
</section> </section>
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="32" data-marpit-fragments="2" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6" lang="en-US" class="exercise invert" data-marpit-pagination="32" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:zk24th0mt1mugosj50ud5w3iz0lt31kd4zssn5haex6;" data-marpit-pagination-total="32"><div class='columns23' markdown='1'> </foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="32" data-marpit-fragments="2" data-paginate="true" data-class="exercise invert" data-heading-divider="5" data-theme="a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol" lang="en-US" class="exercise invert" data-marpit-pagination="32" style="--paginate:true;--class:exercise invert;--heading-divider:5;--theme:a8h8n70bg0ok9c8o8ybti6dlz0pmul6s5qxel7pi6ol;" data-marpit-pagination-total="32"><div class='columns23' markdown='1'>
<div markdown='1'> <div markdown='1'>
<ol start="2"> <ol start="2">
<li data-marpit-fragment="1">Create your request by selecting the method and entering the URL</li> <li data-marpit-fragment="1">Create your request by selecting the method and entering the URL</li>

@ -149,7 +149,7 @@ Use of NuGet packages add modularity and decrease the minimum memory footprint o
<!--_class: "exercise invert" --> <!--_class: "exercise invert" -->
7) Start debugging from the top (the ▶ button with the text *http*). 7) Start debugging from the top (the ▶ button with the text *http*).
* Click OK to trust the sertificates. * Click *Yes* twice to trust the certificates.
![](imgs/2-aspnet-core-basics_5.png) ![](imgs/2-aspnet-core-basics_5.png)

File diff suppressed because one or more lines are too long

@ -1,151 +1,244 @@
# MVC Pattern & Repositories ---
marp: true
paginate: true
math: mathjax
theme: buutti
title: N. MVP Pattern and Repositories
---
# MVP Pattern and Repositories
<!-- headingDivider: 3 -->
<!-- class: invert -->
# The MVC Pattern ## The MVC Pattern
* For creating APIs, we now know how to * For creating APIs, we now know how to
* set up an ASP.NET Core web application, * set up an ASP.NET Core web application,
* set up routes with attributes to connect request URIs with methods, * set up routes with attributes to connect request URIs with methods,
* respond with HTTP responses * respond with HTTP responses
* The problem is that all this functionality is scattered here and there around the program, meaning we have no structure to what we are doing * The problem is that all this functionality is scattered here and there around the program, meaning we have no structure to what we are doing
* In order to write production level code, your API should follow the __MVC pattern__ * In order to write production level code with ASP.NET, your API should follow the *__MVC pattern__*
# The MVC Pattern (continued) ### What is the MVC Pattern?
* Helps to enforce __separation of concerns__ , and induce code readability and testability * [MVC](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) is a software architectural pattern, an acronym for Model - View - Controller
* Acronym for Model - View - Controller * Traditionally used for desktop graphical user interfaces
* Model * Helps to enforce __*separation of concerns*__
* When an entity in code has only a single job:
* It is easier to read and write
* It is easier to test and debug
* $\Rightarrow$ It's easier to scale the application in complexity
* ASP.NET Core includes an [MVC Framework](https://learn.microsoft.com/en-us/aspnet/core/mvc/overview?view=aspnetcore-9.0) for implementing this design pattern
### Model, view and controller
<div class='columns' style='grid-template-columns: 0.7fr 1fr 1fr' markdown='1'>
<div markdown='1'>
#### Model
* Representation of data in code * Representation of data in code
* Could also include some logic to retrieve and save the data * Can also include some logic to retrieve and save the data
* View </div>
* The data that is shown to the client, e.g. web page <div markdown='1'>
* More often than not different from the Model - client does not need to (and often shouldn't) see all possible data
* Controller #### View
* The data that is shown to the client, e.g. a web page
* More often than not different from the Model - client does not need to (and often *shouldn't*) see all possible data
</div>
<div markdown='1'>
#### Controller
* Communicates with the Model and the View * Communicates with the Model and the View
* How the data (models) will be processed before sending it forwards to the client or to the view * How the data (models) will be processed before sending it forwards to the client or to the view
</div>
</div>
In an API, the pattern is implemented like this: ### MVC implementation
class User * In an ASP.NET Core API, the pattern is implemented like this:
{ <div class='columns111' markdown='1'>
<div markdown='1'>
public int Id {get; set;} #### Model
public string Name {get; set;} `Models/User.cs`
```csharp
namespace MyApi.Models
{
public class User
{
public int Id {get; set;}
public string Name {get; set;}
} }
}
```
Get() </div>
<div markdown='1'>
Post()
... #### View
```csharp
{ {
"id": 1 "id": 1
"name": "Sonja" "name": "Sonja"
} }
```
(Basically just JSON data that ASP.NET can show automatically)
--- </div>
<div markdown='1'>
The "view" in APIs is basically just JSON data
# Exercise 1. Setting up the Project #### Controller
Create a new ASP.NET Core Web API template project. Name it CourseAPI. Delete WeatherForecastController.cs and WeatherForecast.cs. `Controllers/UserController.cs`
```csharp
using Microsoft.AspNetCore.Mvc;
using MyApi.Models;
namespace MyApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetUser(int id)
// ...
}
}
```
Add a new controller CoursesController by right-clicking the Controllers folder and selecting _Add_ > _Controller… > API Controller > Empty_ </div>
</div>
Add a new folder Models. Inside, add a new class file ( _Add > Class…_ ) Course.cs ### The controller
To the Course class, create the properties int Id, string Name and int Credits <div class='columns32' markdown='1'>
<div markdown='1'>
# Exercise 2: Creating GET Endpoints ```csharp
using Microsoft.AspNetCore.Mvc;
using MyApi.Models;
namespace MyApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
var person = new Person
{
Id = 1,
Name = "Sonja"
};
return Ok(person);
}
}
}
```
Inside the Controller class, initialize a list of courses with a couple of test courses </div>
<div markdown='1'>
Create endpoints for GET requests with URIs api/courses and api/courses/{id} which return all courses and a single course with a corresponding ID, respectively * Here's an extended implementation of the earlier controller, showcasing the HTTP `GET` method
* The object that gets sent to the View (the webpage) as a JSON is just hardcoded here, but normally the data comes from a ***database***
# Repositories </div>
</div>
* When following the _separation of concerns_ principle, the controllers or models should NOT be accessing the database directly ### Exercise 1. Setting up the Project
* Instead, the web app should have some kind of repository for reading from and writing to the database <!--_class: "exercise invert" -->
* For now, we can just create a mock repository to a new folder called "Repositories" for our needs, and make it static so that it can be accessed everywhere
* Later on, we will add services to make the repository available within our program via dependency injection
# Repository Example 1) Create a new ASP.NET Core Web API template project. Name it `CourseAPI`. Delete `WeatherForecastController.cs` and `WeatherForecast.cs`.
2) Add a new controller `CoursesController` by right-clicking the `Controllers` folder and selecting *Add > Controller... > API Controller > Empty*
3) Add a new folder `Models`. Inside, add a new class file ( _Add > Class..._ ) named `Course.cs`
* To the Course class, create the properties `int Id`, `string Name` and `int Credits`
public class MockRepo ### Exercise 2. Creating `GET` endpoints
<!--_class: "exercise invert" -->
{ 1) Inside the Controller class, initialize a list of courses with a couple of test courses
2) Create endpoints for `GET` requests with URIs `api/courses` and `api/courses/{id}` which return all courses and a single course with a corresponding ID, respectively
// Replace this later on with a database context ## Repositories
private List<Student> Students { get; set; } ### Accessing data with repositories
// Constructor to initialize student list * When following the **_separation of concerns_** principle, the controllers or models should NOT be accessing the database directly
* Instead, the web app should have some kind of ***repository*** for reading from and writing to the database
* (Not to be confused with a Git repository!)
* For now, we can just create a mock repository to a new folder called `Repositories` for our needs, and make it static so that it can be accessed everywhere
* Later on, we will add services to make the repository available within our program via ***dependency injection***
private MockRepo(){ ### Repository Example
Students = new List<Student>(); <div class='columns32' markdown='1'>
<div markdown='1'>
```csharp
// Repositories/ContactRepository.cs
public class MockRepo
{
// Replace this later on with a database context
private List<Contact> Contacts { get; set; }
// Constructor to initialize contact list
private MockRepo()
{
Contacts = new List<Contact>();
} }
// Replace this later on with dependency injection // Replace this later on with dependency injection
public static MockRepo Instance { get; } =
new MockRepo();
public static MockRepo Instance { get; } = new MockRepo(); public List<Contact> GetContacts() => Contacts;
public List<Student> GetStudents() => Students;
// Update database here later // Update database here later
// Other methods to read/write from database // Other methods to read/write from database
// ... // ...
} }
```
--- </div>
<div markdown='1'>
At this point, implementation of the db is up to you
Later, real database is added
# Repository Example (continued)
In the controller class:
```csharp
// Controllers/ContactsController.cs
// ...
[HttpGet] [HttpGet]
public List<Contact> Get()
public List<Student> Get()
{ {
return MockRepo.Instance.GetContacts();
return MockRepo.Instance.GetStudents();
} }
// ...
```
--- * At this point, implementation of the "database" is up to you
* Later, a real database is added
At this point, implementation of the db is up to you </div>
Later, real database is added </div>
# Exercise 3: Using a Repository ### Exercise 3: Using a Repository
<!--_class: "exercise invert" -->
Continue working on CourseAPI. Continue working on CourseAPI.
Within the solution, create a folder called Repositories 1) Within the solution, create a folder called `Repositories`
2) Add a mock repository to the folder. Move the course list from the controller to the repository to simulate our database for now. Add methods to get all courses in the list and a single course by ID.
3) Modify the controller so that it uses the repository for getting courses.
Add a mock repository to the folder. Move the course list from the controller to the repository to simulate our database for now. Add methods to get all courses in the list and a single course by ID. ## Services
Modify the controller so that it uses the repository for getting courses. ### Repository, as a service
# Services * Services and [dependency injection (DI)](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1) have been introduced earlier in this training
* [C# Basics: Lecture 15: Dependency injection](https://gitea.buutti.com/education/csharp-basics/src/branch/main/15-design-patterns-in-csharp.md#dependency-injection), [Lecture 1: Services](1-aspnet-introduction#services)
* [Services and dependency injection](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1) (DI) have been mentioned during this course * Recap: DI allows for ***loose coupling*** between classes and their dependencies
* Recap: DI allows for loose coupling between classes and their dependencies
* This decreases complexity, makes refactoring easier and increases code testability * This decreases complexity, makes refactoring easier and increases code testability
* DI is used in ASP.NET to distribute services to classes in a controlled way * DI is used in ASP.NET to distribute services to classes in a controlled way
* Let's get started by making a repository service * Let's get started by making a repository service
@ -153,148 +246,138 @@ Modify the controller so that it uses the repository for getting courses.
* Repositories should be accessible within the API from multiple controllers * Repositories should be accessible within the API from multiple controllers
* Logical step is to make it a service! * Logical step is to make it a service!
# Services (continued) ### Service interface
Create a service interface and a class implementing it: <div class='columns' markdown='1'>
<div markdown='1'>
* Create a ***service interface*** and a class implementing it:
![](imgs/4-mvc-pattern-and-repositories_0.png) ![](imgs/4-mvc-pattern-and-repositories_0.png)
--- * ***A rule of thumb:*** Create a separate repository for all ***tightly coupled*** models, not necessarily every model
Repository for each model?
1 repo for all tightly coupled models
Add _all _ methods of the service to the interface: </div>
<div markdown='1'>
* Add **_all_** methods of the service to the interface:
```csharp
public interface IContactRepository public interface IContactRepository
{ {
Contact GetContact(int id); Contact GetContact(int id);
List<Contact> GetContacts(); List<Contact> GetContacts();
void AddContact(Contact contact); void AddContact(Contact contact);
void UpdateContact(int id, Contact contact); void UpdateContact(int id, Contact contact);
void DeleteContact(int id); void DeleteContact(int id);
// UpdateDataBase() later on... // UpdateDataBase() later on...
} }
```
Create the class which implements the interface: </div>
</div>
public class ContactRepository : IContactRepository * Next, we'll create a class that implements this interface.
{ ### Implementing the interface
```csharp
public class ContactRepository : IContactRepository
{
// Replace this with database context in a real life application // Replace this with database context in a real life application
private static List<Contact> Contacts = new List<Contact> private static List<Contact> Contacts = new List<Contact>
{ {
new Contact{Id=0, Name="Johannes Kantola", Email="johkant@example.com"}, new Contact{Id=0, Name="Johannes Kantola", Email="johkant@example.com"},
new Contact{Id=1, Name="Rene Orosz", Email="rene_king@example.com"} new Contact{Id=1, Name="Rene Orosz", Email="rene_king@example.com"}
}; };
public void AddContact(Contact contact) => Contacts.Add(contact); public void AddContact(Contact contact) => Contacts.Add(contact);
public void DeleteContact(int id) => public void DeleteContact(int id) =>
Contacts = Contacts.Where(c => c.Id != id).ToList(); Contacts = Contacts.Where(c => c.Id != id).ToList();
public Contact GetContact(int id) => Contacts.FirstOrDefault(c => c.Id == id); public Contact GetContact(int id) => Contacts.FirstOrDefault(c => c.Id == id);
public List<Contact> GetContacts() => Contacts; public List<Contact> GetContacts() => Contacts;
public void UpdateContact(int id, Contact newContact) => public void UpdateContact(int id, Contact newContact) =>
Contacts = Contacts.Select(c => c.Id != id ? c : newContact).ToList(); Contacts = Contacts.Select(c => c.Id != id ? c : newContact).ToList();
} }
```
The service is now ready to be added to the container in the Program.cs file: ### Adding the service to `Program.cs`
* The service is now ready to be added to the container in the Program.cs file:
```csharp
// ... // ...
builder.Services.AddSingleton<IContactRepository, ContactRepository>(); builder.Services.AddSingleton<IContactRepository, ContactRepository>();
builder.Services.AddControllers().AddNewtonsoftJson(); builder.Services.AddControllers().AddNewtonsoftJson();
// ... // ...
```
Add the service to your controller by creating a constructor and passing it as a parameter: ### Using the service in a controller
* Add the service to your controller by creating a constructor and passing it as a parameter:
```csharp
public class ContactsController : ControllerBase public class ContactsController : ControllerBase
{ {
private readonly IContactRepository _contactRepository; private readonly IContactRepository _contactRepository;
public ContactsController(IContactRepository contactRepository) public ContactsController(IContactRepository contactRepository)
{ {
_contactRepository = contactRepository; _contactRepository = contactRepository;
} }
} }
```
* If you need to add more services to the controller later, you can just add them as a parameter as well - the order of parameters does not matter!
If you need to add more services to the controller later, you can just add them as a parameter as well - the order of parameters does not matter! ### Consuming the service in endpoints
That's it! Your service is now ready to be consumed by the controller (no need to use the Instance anymore!)
* Your service is now ready to be consumed by the controller
* No need to use `MockRepo.Instance.GetContacts()` anymore!
```csharp
[HttpGet("{id}")] [HttpGet("{id}")]
public IActionResult GetContactById(int id) public IActionResult GetContactById(int id)
{ {
Contact contact = _contactRepository.GetContact(id); Contact contact = _contactRepository.GetContact(id);
if (contact == null) if (contact == null)
{ {
return NotFound(); return NotFound();
} }
return Ok(contact); return Ok(contact);
} }
```
# Exercise 4: ### Exercise 4: A real repository
<!--_class: "exercise invert" -->
Continue working on CourseAPI. Continue working on CourseAPI.
Create an interface ICourseRepository with methods for getting a single course or all courses. Rename your mock repository to CourseRepository and make sure the interface is fully implemented. 1) Create an interface `ICourseRepository` with methods for getting a single course or all courses. Rename your mock repository to `CourseRepository` and make sure the interface is fully implemented.
2) Add `CourseRepository` as a service, and refactor the controller to use methods for `ICourseRepository`
Add CourseRepository as a service, and refactor the controller to use methods for ICourseRepository ## Wrapping Things Up
# Wrapping Things Up At this point, the flow of your API should be in line with this chart:
At this point, the flow of your APIs should be in lines with this chart: ![](imgs/api-chart.svg)
Client (e.g. Postman) ### Project hierarchy
![](imgs/4-mvc-pattern-and-repositories_1.png)
* In solution explorer, the project hierarchy would look like this <div class='columns32' markdown='1'>
<div markdown='1'>
* In solution explorer, the project hierarchy would look like this.
* Once you start adding new classes, they should have their own models and controllers * Once you start adding new classes, they should have their own models and controllers
* One repository can store multiple models, and there can be multiple repositories * One repository can store multiple models, and there can be multiple repositories
* Every server/database/API you use should have its own repository! * Every server/database/API you use should have its own repository!
# What the Heck Does This Do? </div>
<div markdown='1'>
![](imgs/4-mvc-pattern-and-repositories_1.png)
</div>
</div>
### What the Heck Does This Do?
* A lot of the functionality in ASP.NET comes from the base class library * A lot of the functionality in ASP.NET comes from the base class library
* At beginning, trying to remember all the methods and what did what can feel overwhelming * When starting out, trying to remember all the methods and what does what can feel overwhelming
* If you ever end up getting confused about what did what, Ctrl + click Type name to go to definition * If you ever end up getting confused what any method does, Ctrl + click *Type name* to go to definition
* There is always a summary about the method * There is always a summary about the method
* Also looking up the method from Microsoft documentation is helpful * Also looking up the method from Microsoft documentation is helpful

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 800 350" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
<g transform="matrix(0.551076,0,0,0.865161,-39.5943,-36.3734)">
<path d="M276.094,143.082C276.094,136.843 268.141,131.777 258.345,131.777L105.722,131.777C95.927,131.777 87.974,136.843 87.974,143.082L87.974,195.84C87.974,202.08 95.927,207.146 105.722,207.146L258.345,207.146C268.141,207.146 276.094,202.08 276.094,195.84L276.094,143.082Z" style="fill:rgb(239,155,47);stroke:white;stroke-width:3.47px;"/>
</g>
<g transform="matrix(0.551076,0,0,0.865161,245.98,-37.4322)">
<path d="M276.094,143.082C276.094,136.843 268.141,131.777 258.345,131.777L105.722,131.777C95.927,131.777 87.974,136.843 87.974,143.082L87.974,195.84C87.974,202.08 95.927,207.146 105.722,207.146L258.345,207.146C268.141,207.146 276.094,202.08 276.094,195.84L276.094,143.082Z" style="fill:rgb(146,142,252);stroke:white;stroke-width:3.47px;"/>
</g>
<g transform="matrix(0.551076,0,0,0.865161,105.485,128.968)">
<path d="M276.094,143.082C276.094,136.843 268.141,131.777 258.345,131.777L105.722,131.777C95.927,131.777 87.974,136.843 87.974,143.082L87.974,195.84C87.974,202.08 95.927,207.146 105.722,207.146L258.345,207.146C268.141,207.146 276.094,202.08 276.094,195.84L276.094,143.082Z" style="fill:rgb(255,163,254);stroke:white;stroke-width:3.47px;"/>
</g>
<g transform="matrix(0.551076,0,0,0.865161,342.559,131.26)">
<path d="M276.094,143.082C276.094,136.843 268.141,131.777 258.345,131.777L105.722,131.777C95.927,131.777 87.974,136.843 87.974,143.082L87.974,195.84C87.974,202.08 95.927,207.146 105.722,207.146L258.345,207.146C268.141,207.146 276.094,202.08 276.094,195.84L276.094,143.082Z" style="fill:rgb(13,255,178);stroke:white;stroke-width:3.47px;"/>
</g>
<g transform="matrix(1.41896,0,0,1.41896,-242.978,-135.17)">
<text x="200.392px" y="170.366px" style="font-family:'Bahnschrift', sans-serif;font-size:10.571px;">Client</text>
<text x="180.455px" y="180.937px" style="font-family:'Bahnschrift', sans-serif;font-size:10.571px;">(e<tspan x="189.503px 191.692px " y="180.937px 180.937px ">.g</tspan>. P<tspan x="208.741px " y="180.937px ">o</tspan>stman)</text>
</g>
<g transform="matrix(1.41896,0,0,1.41896,42.084,-127.238)">
<text x="189.968px" y="170.366px" style="font-family:'Bahnschrift', sans-serif;font-size:10.571px;">Contr<tspan x="216.029px " y="170.366px ">o</tspan>ller</text>
</g>
<g transform="matrix(0.551076,0,0,0.865161,452.338,-36.3734)">
<path d="M276.094,143.082C276.094,136.843 268.141,131.777 258.345,131.777L105.722,131.777C95.927,131.777 87.974,136.843 87.974,143.082L87.974,195.84C87.974,202.08 95.927,207.146 105.722,207.146L258.345,207.146C268.141,207.146 276.094,202.08 276.094,195.84L276.094,143.082Z" style="fill:rgb(198,193,193);stroke:white;stroke-width:3.47px;"/>
</g>
<g transform="matrix(1.41896,0,0,1.41896,248.368,-127.762)">
<text x="188.618px" y="170.366px" style="font-family:'Bahnschrift', sans-serif;font-size:10.571px;">R<tspan x="195.302px " y="170.366px ">e</tspan>pository</text>
</g>
<g transform="matrix(1.41896,0,0,1.41896,-98.4116,39.1623)">
<text x="202.782px" y="170.366px" style="font-family:'Bahnschrift', sans-serif;font-size:10.571px;">Vie<tspan x="217.1px " y="170.366px ">w</tspan></text>
</g>
<g transform="matrix(1.41896,0,0,1.41896,138.663,41.4539)">
<text x="199.816px" y="170.366px" style="font-family:'Bahnschrift', sans-serif;font-size:10.571px;">Model</text>
</g>
<g transform="matrix(1.75521,0,0,1.75521,-170.82,-198.581)">
<text x="185.358px" y="170.366px" style="font-family:'Bahnschrift', sans-serif;font-size:9.116px;fill:white;">H<tspan x="191.901px 196.957px 201.524px " y="170.366px 170.366px 170.366px ">TTP</tspan> request</text>
</g>
<g transform="matrix(1.20127,-1.27973,1.27973,1.20127,-205.622,262.823)">
<text x="181.964px" y="170.366px" style="font-family:'Bahnschrift', sans-serif;font-size:9.116px;fill:white;">H<tspan x="188.507px 193.563px 198.13px " y="170.366px 170.366px 170.366px ">TTP</tspan> response</text>
</g>
<g transform="matrix(0.961908,0,0,1.00052,-41.1251,-34.5693)">
<g transform="matrix(1.0396,-0,-0,0.999484,42.7536,34.5514)">
<path d="M129.553,109.153L112.554,114.82L129.553,120.486L129.553,109.153Z" style="fill:white;"/>
<path d="M126.153,114.82L294.461,114.82" style="fill:none;stroke:white;stroke-width:2.52px;"/>
</g>
</g>
<g transform="matrix(-0.961908,3.87139e-17,-2.10352e-16,-1.00052,448.14,253.133)">
<g transform="matrix(-1.0396,-4.02262e-17,2.18569e-16,-0.999484,465.887,253.003)">
<path d="M277.462,109.411L294.461,103.745L277.462,98.078L277.462,109.411Z" style="fill:white;"/>
<path d="M280.862,103.745L112.554,103.745" style="fill:none;stroke:white;stroke-width:2.52px;"/>
</g>
</g>
<g transform="matrix(0.543012,0,0,1,311.375,-35.4093)">
<g transform="matrix(1.84158,-0,-0,1,-573.422,35.4093)">
<path d="M415.128,108.236L398.129,113.902L415.128,119.569L415.128,108.236Z" style="fill:white;"/>
<path d="M411.728,113.902L500.818,113.902" style="fill:none;stroke:white;stroke-width:2.52px;"/>
</g>
</g>
<g transform="matrix(0.378387,0.212266,-0.48925,0.872143,616.09,-44.4939)">
<g transform="matrix(2.0102,-0.48925,1.12767,0.872143,-1188.29,340.227)">
<path d="M621.09,123.015L603.492,119.64L615.545,132.898L621.09,123.015Z" style="fill:white;"/>
<path d="M615.352,126.293C635.689,137.702 675.049,159.782 675.049,159.782" style="fill:none;stroke:white;stroke-width:2.52px;"/>
</g>
</g>
<g transform="matrix(-0.37005,-0.207589,0.48925,-0.872143,661.119,312.042)">
<g transform="matrix(-2.05549,0.48925,-1.15308,-0.872143,1718.73,-51.3077)">
<path d="M657.451,145.28L675.049,148.655L662.996,135.397L657.451,145.28Z" style="fill:white;"/>
<path d="M663.189,142.002C643.226,130.803 605.069,109.398 605.069,109.398" style="fill:none;stroke:white;stroke-width:2.52px;"/>
</g>
</g>
<g transform="matrix(-0.543012,1.0596e-16,-7.40307e-17,-1,587.573,252.145)">
<g transform="matrix(-1.84158,-1.95134e-16,1.36334e-16,-1,1082.06,252.145)">
<path d="M483.819,108.499L500.818,102.833L483.819,97.167L483.819,108.499Z" style="fill:white;"/>
<path d="M487.219,102.833L398.129,102.833" style="fill:none;stroke:white;stroke-width:2.52px;"/>
</g>
</g>
<g transform="matrix(0.431952,0.547228,-0.784932,0.619582,398.385,-38.1569)">
<g transform="matrix(0.888714,-0.784932,1.12589,0.619582,-311.09,336.346)">
<path d="M365.176,151.614L350.196,141.782L356.281,158.636L365.176,151.614Z" style="fill:white;"/>
<path d="M358.622,152.456C379.487,178.89 431.883,245.268 431.883,245.268" style="fill:none;stroke:white;stroke-width:2.52px;"/>
</g>
</g>
<g transform="matrix(-0.428989,-0.543474,0.784932,-0.619582,397.323,424.608)">
<g transform="matrix(-0.894853,0.784932,-1.13367,-0.619582,836.909,-48.792)">
<path d="M431.005,235.436L445.985,245.268L439.9,228.415L431.005,235.436Z" style="fill:white;"/>
<path d="M437.559,234.594C416.798,208.293 364.859,142.492 364.859,142.492" style="fill:none;stroke:white;stroke-width:2.52px;"/>
</g>
</g>
<g transform="matrix(0.501942,-0.53511,0.729349,0.684142,43.7505,226.319)">
<g transform="matrix(0.932477,0.729349,-0.994095,0.684142,184.186,-186.743)">
<path d="M240.341,226.702L232.844,242.977L248.606,234.455L240.341,226.702Z" style="fill:white;"/>
<path d="M242.147,233.058C266.053,207.573 327.766,141.782 327.766,141.782" style="fill:none;stroke:white;stroke-width:2.52px;"/>
</g>
</g>
<g transform="matrix(0.628168,0,0,0.628168,619.786,46.5239)">
<path d="M276.094,143.082C276.094,136.843 271.028,131.777 264.789,131.777L99.279,131.777C93.039,131.777 87.974,136.843 87.974,143.082L87.974,195.84C87.974,202.08 93.039,207.146 99.279,207.146L264.789,207.146C271.028,207.146 276.094,202.08 276.094,195.84L276.094,143.082Z" style="fill:rgb(245,204,72);"/>
<clipPath id="_clip1">
<path d="M276.094,143.082C276.094,136.843 271.028,131.777 264.789,131.777L99.279,131.777C93.039,131.777 87.974,136.843 87.974,143.082L87.974,195.84C87.974,202.08 93.039,207.146 99.279,207.146L264.789,207.146C271.028,207.146 276.094,202.08 276.094,195.84L276.094,143.082Z"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(2.57489,0,0,2.57489,-366.909,-258.243)">
<text x="192.469px" y="170.366px" style="font-family:'Bahnschrift', sans-serif;font-size:9.892px;">Database</text>
</g>
</g>
<path d="M276.094,143.082C276.094,136.843 271.028,131.777 264.789,131.777L99.279,131.777C93.039,131.777 87.974,136.843 87.974,143.082L87.974,195.84C87.974,202.08 93.039,207.146 99.279,207.146L264.789,207.146C271.028,207.146 276.094,202.08 276.094,195.84L276.094,143.082Z" style="fill:none;stroke:white;stroke-width:4.01px;"/>
</g>
<g transform="matrix(0.686047,0,0,0.686047,57.2353,1.31136)">
<circle cx="213.21" cy="86.775" r="24.476" style="fill:none;"/>
<clipPath id="_clip2">
<circle cx="213.21" cy="86.775" r="24.476"/>
</clipPath>
<g clip-path="url(#_clip2)">
<g transform="matrix(1,0,0,1,12.7839,24.5898)">
<text x="194.307px" y="73.154px" style="font-family:'Bahnschrift', sans-serif;font-size:30.899px;fill:white;">1.</text>
</g>
</g>
<circle cx="213.21" cy="86.775" r="24.476" style="fill:none;stroke:rgb(255,56,142);stroke-width:3.67px;"/>
</g>
<g transform="matrix(0.686047,0,0,0.686047,309.564,11.1024)">
<circle cx="213.21" cy="86.775" r="24.476" style="fill:none;"/>
<clipPath id="_clip3">
<circle cx="213.21" cy="86.775" r="24.476"/>
</clipPath>
<g clip-path="url(#_clip3)">
<g transform="matrix(1,0,0,1,9.92498,24.7029)">
<text x="194.307px" y="73.154px" style="font-family:'Bahnschrift', sans-serif;font-size:30.899px;fill:white;">2.</text>
</g>
</g>
<circle cx="213.21" cy="86.775" r="24.476" style="fill:none;stroke:rgb(255,56,142);stroke-width:3.67px;"/>
</g>
<g transform="matrix(0.686047,0,0,0.686047,505.271,49.8663)">
<circle cx="213.21" cy="86.775" r="24.476" style="fill:none;"/>
<clipPath id="_clip4">
<circle cx="213.21" cy="86.775" r="24.476"/>
</clipPath>
<g clip-path="url(#_clip4)">
<g transform="matrix(1,0,0,1,9.92498,24.7029)">
<text x="194.307px" y="73.154px" style="font-family:'Bahnschrift', sans-serif;font-size:30.899px;fill:white;">3.</text>
</g>
</g>
<circle cx="213.21" cy="86.775" r="24.476" style="fill:none;stroke:rgb(255,56,142);stroke-width:3.67px;"/>
</g>
<g transform="matrix(0.686047,0,0,0.686047,282.921,123.069)">
<circle cx="213.21" cy="86.775" r="24.476" style="fill:none;"/>
<clipPath id="_clip5">
<circle cx="213.21" cy="86.775" r="24.476"/>
</clipPath>
<g clip-path="url(#_clip5)">
<g transform="matrix(1,0,0,1,6.76603,24.5747)">
<text x="194.307px" y="73.154px" style="font-family:'Bahnschrift', sans-serif;font-size:30.899px;fill:white;">4.</text>
</g>
</g>
<circle cx="213.21" cy="86.775" r="24.476" style="fill:none;stroke:rgb(255,56,142);stroke-width:3.67px;"/>
</g>
<g transform="matrix(0.686047,0,0,0.686047,82.8056,117.557)">
<circle cx="213.21" cy="86.775" r="24.476" style="fill:none;"/>
<clipPath id="_clip6">
<circle cx="213.21" cy="86.775" r="24.476"/>
</clipPath>
<g clip-path="url(#_clip6)">
<g transform="matrix(1,0,0,1,9.92498,24.7029)">
<text x="194.307px" y="73.154px" style="font-family:'Bahnschrift', sans-serif;font-size:30.899px;fill:white;">5.</text>
</g>
</g>
<circle cx="213.21" cy="86.775" r="24.476" style="fill:none;stroke:rgb(255,56,142);stroke-width:3.67px;"/>
</g>
<g transform="matrix(1.20603,0,0,0.913424,-72.8921,-0.985609)">
<path d="M582.258,39.602C582.258,30.215 576.487,22.595 569.378,22.595L192.13,22.595C185.021,22.595 179.249,30.215 179.249,39.602L179.249,345.73C179.249,355.116 185.021,362.737 192.13,362.737L569.378,362.737C576.487,362.737 582.258,355.116 582.258,345.73L582.258,39.602Z" style="fill:none;stroke:white;stroke-width:2.35px;stroke-dasharray:4.71,9.42,0,0;"/>
</g>
<g transform="matrix(3.09958,0,0,3.09958,-1133.69,-586.674)">
<text x="541.444px" y="290.513px" style="font-family:'Bahnschrift', sans-serif;font-size:16px;fill:white;">AP<tspan x="561.608px " y="290.513px ">I</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 13 KiB

Loading…
Cancel
Save