<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ahsan Khan</title>
    <description>The latest articles on DEV Community by Ahsan Khan (@mhd-ahsan-khan).</description>
    <link>https://dev.to/mhd-ahsan-khan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3788825%2Fed86f5b0-cc03-4486-81e8-1fbd83f2b11e.jpg</url>
      <title>DEV Community: Ahsan Khan</title>
      <link>https://dev.to/mhd-ahsan-khan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mhd-ahsan-khan"/>
    <language>en</language>
    <item>
      <title>Building Your First Web API with ASP.NET Core Part 4: Putting It All Together &amp; Beyond</title>
      <dc:creator>Ahsan Khan</dc:creator>
      <pubDate>Tue, 10 Mar 2026 00:50:52 +0000</pubDate>
      <link>https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-4-putting-it-all-together-beyond-2dim</link>
      <guid>https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-4-putting-it-all-together-beyond-2dim</guid>
      <description>&lt;p&gt;&lt;em&gt;This is the final part of a 4-part series. In &lt;a href="https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-3-implementing-post-put-delete-3595"&gt;Part 3&lt;/a&gt;, we completed the write operations   POST, PUT, and DELETE. Now we take a step back, look at the big picture, and talk about where to go from here.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What We Built
&lt;/h2&gt;

&lt;p&gt;Over the course of this series, we went from zero to a fully functional RESTful API. Let's take stock of everything that's now in place.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ContosoPizza/
├── Controllers/
│   └── PizzaController.cs     ← handles all HTTP requests
├── Models/
│   └── Pizza.cs               ← our data shape
├── Services/
│   └── PizzaService.cs        ← in-memory data layer
├── Program.cs                 ← app startup &amp;amp; pipeline
├── ContosoPizza.http          ← request testing file
└── ContosoPizza.csproj        ← project config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Full API Surface
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Route&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;th&gt;Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/pizza&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns all pizzas&lt;/td&gt;
&lt;td&gt;&lt;code&gt;200 OK&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/pizza/{id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns one pizza by ID&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;200&lt;/code&gt; / &lt;code&gt;404&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/pizza&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adds a new pizza&lt;/td&gt;
&lt;td&gt;&lt;code&gt;201 Created&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/pizza/{id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replaces an existing pizza&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;204&lt;/code&gt; / &lt;code&gt;400&lt;/code&gt; / &lt;code&gt;404&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/pizza/{id}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes a pizza&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;204&lt;/code&gt; / &lt;code&gt;404&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Five endpoints. Full CRUD coverage. Clean status codes on every path   including the unhappy ones.&lt;/p&gt;




&lt;h2&gt;
  
  
  How the Pieces Fit Together
&lt;/h2&gt;

&lt;p&gt;It's worth zooming out and seeing how data flows through the app from a single request.&lt;/p&gt;

&lt;p&gt;Take a &lt;code&gt;POST /pizza&lt;/code&gt; request as an example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The request arrives at the ASP.NET Core HTTP pipeline&lt;/li&gt;
&lt;li&gt;The router matches &lt;code&gt;/pizza&lt;/code&gt; to &lt;code&gt;PizzaController&lt;/code&gt;, and the &lt;code&gt;POST&lt;/code&gt; verb to the &lt;code&gt;Create&lt;/code&gt; method&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;[ApiController]&lt;/code&gt; deserializes the JSON body into a &lt;code&gt;Pizza&lt;/code&gt; object automatically&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Create&lt;/code&gt; passes that object to &lt;code&gt;PizzaService.Add()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The service assigns an ID and appends it to the in-memory list&lt;/li&gt;
&lt;li&gt;The controller returns &lt;code&gt;CreatedAtAction(...)&lt;/code&gt;, which ASP.NET Core converts into a &lt;code&gt;201 Created&lt;/code&gt; response with a &lt;code&gt;Location&lt;/code&gt; header&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every request follows this same arc: &lt;strong&gt;route → controller action → service → response&lt;/strong&gt;. The controller stays thin, the service handles the logic, and the model defines the shape. That separation is what makes the code easy to extend later.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Limitations of In-Memory Storage
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;PizzaService&lt;/code&gt; uses a static &lt;code&gt;List&amp;lt;Pizza&amp;gt;&lt;/code&gt;   which was perfect for learning, but has real constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data resets on every restart.&lt;/strong&gt; Stop the server and all your pizzas are gone, back to the two defaults.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No concurrency safety.&lt;/strong&gt; If two requests hit &lt;code&gt;Add()&lt;/code&gt; at exactly the same time, you could get race conditions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No persistence.&lt;/strong&gt; Nothing is written to disk, a file, or a database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a real application, you'd swap the in-memory list for a proper database. The good news is that ASP.NET Core is designed for exactly this transition.&lt;/p&gt;




&lt;h2&gt;
  
  
  Moving to a Real Database
&lt;/h2&gt;

&lt;p&gt;When you're ready to add persistence, &lt;strong&gt;Entity Framework Core (EF Core)&lt;/strong&gt; is the natural next step. It's Microsoft's official ORM for .NET and integrates tightly with ASP.NET Core.&lt;/p&gt;

&lt;p&gt;The migration path is straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install EF Core:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.EntityFrameworkCore.SqlServer
&lt;span class="c"&gt;# or for SQLite (great for local dev):&lt;/span&gt;
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create a &lt;code&gt;DbContext&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.EntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PizzaContext&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DbContext&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PizzaContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbContextOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PizzaContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DbSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Pizzas&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Register it in &lt;code&gt;Program.cs&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PizzaContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Data Source=contosopizza.db"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Inject it into your controller instead of calling the static service:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PizzaController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;PizzaContext&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PizzaController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PizzaContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pizzas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your routes, attributes, and HTTP behavior stay exactly the same   only the data layer changes. That's the benefit of keeping the controller and the service layer separate from the start.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Else Should You Add?
&lt;/h2&gt;

&lt;p&gt;A working CRUD API is a solid foundation. Here are the most common things you'd layer on next in a real project:&lt;/p&gt;

&lt;h3&gt;
  
  
  Input Validation
&lt;/h3&gt;

&lt;p&gt;Right now, someone could POST a pizza with no name at all and it would go straight into the list. Data annotations let you enforce rules at the model level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.ComponentModel.DataAnnotations&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pizza&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Required&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;StringLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MinimumLength&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsGlutenFree&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;[ApiController]&lt;/code&gt; is on your controller, invalid requests are automatically rejected with a &lt;code&gt;400 Bad Request&lt;/code&gt;   no extra code needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Swagger / OpenAPI
&lt;/h3&gt;

&lt;p&gt;ASP.NET Core projects come with Swashbuckle pre-installed. It generates an interactive API documentation page automatically from your controller code. Just navigate to &lt;code&gt;/swagger&lt;/code&gt; while your app is running and you'll see every endpoint, its expected inputs, and possible responses   all explorable in the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Authentication &amp;amp; Authorization
&lt;/h3&gt;

&lt;p&gt;When you're ready to lock down your endpoints, ASP.NET Core has built-in support for &lt;strong&gt;JWT Bearer tokens&lt;/strong&gt;. Add the NuGet package, register the middleware, and protect specific endpoints with &lt;code&gt;[Authorize]&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Authorize&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Async All the Way
&lt;/h3&gt;

&lt;p&gt;Our current service methods are synchronous. In production, especially with a real database, you'd want everything to be &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; to avoid blocking threads under load:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pizzas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Series Recap
&lt;/h2&gt;

&lt;p&gt;Here's everything we covered across all four parts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1&lt;/strong&gt;   What REST is, why ASP.NET Core, scaffolding the project, running it for the first time, and testing with &lt;code&gt;.http&lt;/code&gt; files and HTTP REPL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 2&lt;/strong&gt;   How controllers, &lt;code&gt;ControllerBase&lt;/code&gt;, &lt;code&gt;[ApiController]&lt;/code&gt;, and &lt;code&gt;[Route]&lt;/code&gt; work; building the &lt;code&gt;Pizza&lt;/code&gt; model and &lt;code&gt;PizzaService&lt;/code&gt;; implementing GET endpoints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 3&lt;/strong&gt;   Implementing POST, PUT, and DELETE; understanding &lt;code&gt;IActionResult&lt;/code&gt; vs &lt;code&gt;ActionResult&amp;lt;T&amp;gt;&lt;/code&gt;; correct status codes on every path; end-to-end testing of all five operations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 4 (this one)&lt;/strong&gt;   The full picture, in-memory storage limitations, migrating to EF Core, and the natural next steps for a production-ready API.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Building a Web API with ASP.NET Core is remarkably approachable once you understand the underlying structure. Controllers handle routing and HTTP concerns. Services handle logic. Models define the data. Each layer has a clear job.&lt;/p&gt;

&lt;p&gt;The patterns you've learned here   attribute routing, &lt;code&gt;ActionResult&lt;/code&gt; types, proper HTTP status codes, thin controllers, separated service layers   are the same ones used in large-scale production APIs. You're not learning toy concepts; you're learning the real thing.&lt;/p&gt;

&lt;p&gt;From here, the best thing you can do is keep building. Add a second resource. Connect a real database. Explore authentication. Every layer you add will reinforce what you already know.&lt;/p&gt;

&lt;p&gt;Good luck   and thanks for following along through the whole series. 🍕&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this series helpful? Drop a reaction or share it with someone learning .NET. Questions are always welcome in the comments!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; &lt;code&gt;dotnet&lt;/code&gt; &lt;code&gt;csharp&lt;/code&gt; &lt;code&gt;webapi&lt;/code&gt; &lt;code&gt;aspnetcore&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>api</category>
      <category>tutorial</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Building Your First Web API with ASP.NET Core Part 3: Implementing POST, PUT &amp; DELETE</title>
      <dc:creator>Ahsan Khan</dc:creator>
      <pubDate>Tue, 10 Mar 2026 00:48:02 +0000</pubDate>
      <link>https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-3-implementing-post-put-delete-3595</link>
      <guid>https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-3-implementing-post-put-delete-3595</guid>
      <description>&lt;p&gt;&lt;em&gt;This is Part 3 of a 4-part series. In &lt;a href="https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-2-controllers-models-your-first-endpoint-2157"&gt;Part 2&lt;/a&gt;, we built our &lt;code&gt;Pizza&lt;/code&gt; model, an in-memory data service, and wired up two GET endpoints. Now it's time to handle the write side of our API.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Where We Left Off
&lt;/h2&gt;

&lt;p&gt;By the end of Part 2, our &lt;code&gt;PizzaController&lt;/code&gt; could respond to two GET requests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /pizza&lt;/code&gt;   returns all pizzas&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /pizza/{id}&lt;/code&gt;   returns a specific pizza or a 404&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That covers the &lt;strong&gt;Read&lt;/strong&gt; in CRUD. This part finishes the job with &lt;strong&gt;Create&lt;/strong&gt;, &lt;strong&gt;Update&lt;/strong&gt;, and &lt;strong&gt;Delete&lt;/strong&gt;   using &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; respectively.&lt;/p&gt;

&lt;p&gt;Here's the full HTTP verb-to-CRUD mapping we're working with:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;HTTP Verb&lt;/th&gt;
&lt;th&gt;CRUD Operation&lt;/th&gt;
&lt;th&gt;ASP.NET Core Attribute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[HttpGet]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[HttpPost]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[HttpPut]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[HttpDelete]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;IActionResult&lt;/code&gt; vs &lt;code&gt;ActionResult&amp;lt;T&amp;gt;&lt;/code&gt;   A Quick Clarification
&lt;/h2&gt;

&lt;p&gt;In Part 2, our GET methods returned &lt;code&gt;ActionResult&amp;lt;T&amp;gt;&lt;/code&gt;   a typed wrapper that tells ASP.NET Core exactly what shape the response body will have.&lt;/p&gt;

&lt;p&gt;For write operations, we'll use &lt;code&gt;IActionResult&lt;/code&gt; instead. Why? Because write endpoints often return &lt;strong&gt;no body at all&lt;/strong&gt;   just a status code. When you update a pizza successfully, you don't need to send the pizza back; a &lt;code&gt;204 No Content&lt;/code&gt; is enough. Since the return type isn't known until runtime, &lt;code&gt;IActionResult&lt;/code&gt; gives us the flexibility to return different result types from the same method.&lt;/p&gt;




&lt;h2&gt;
  
  
  POST   Adding a New Pizza
&lt;/h2&gt;

&lt;p&gt;Replace the &lt;code&gt;// POST action&lt;/code&gt; comment in &lt;code&gt;PizzaController.cs&lt;/code&gt; with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;CreatedAtAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's unpack what's happening here.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;[HttpPost]&lt;/code&gt; attribute maps this method to &lt;code&gt;POST /pizza&lt;/code&gt;. When the client sends a request with a JSON body, ASP.NET Core automatically deserializes it into a &lt;code&gt;Pizza&lt;/code&gt; object   no manual parsing needed. This works because the controller has &lt;code&gt;[ApiController]&lt;/code&gt; on it, which tells the framework to look for the object in the request body by default.&lt;/p&gt;

&lt;p&gt;The return value, &lt;code&gt;CreatedAtAction&lt;/code&gt;, does two things at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sends back a &lt;code&gt;201 Created&lt;/code&gt; status code&lt;/li&gt;
&lt;li&gt;Adds a &lt;code&gt;Location&lt;/code&gt; header to the response pointing to the newly created resource (e.g., &lt;code&gt;/pizza/3&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That &lt;code&gt;nameof(Get)&lt;/code&gt; refers to our single-item GET method from Part 2. It's used to build the URL in the &lt;code&gt;Location&lt;/code&gt; header without hardcoding any strings.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;HTTP Status&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CreatedAtAction(...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;201 Created&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pizza was added successfully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implicit &lt;code&gt;BadRequest&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Request body failed model validation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  PUT   Updating an Existing Pizza
&lt;/h2&gt;

&lt;p&gt;Replace the &lt;code&gt;// PUT action&lt;/code&gt; comment with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpPut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Pizza&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;existingPizza&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existingPizza&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NoContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few deliberate design decisions here worth understanding.&lt;/p&gt;

&lt;p&gt;The route is &lt;code&gt;PUT /pizza/{id}&lt;/code&gt;, and the method takes both an &lt;code&gt;id&lt;/code&gt; from the URL and a &lt;code&gt;Pizza&lt;/code&gt; object from the request body. The first thing we do is check that these two IDs agree   if someone sends &lt;code&gt;PUT /pizza/1&lt;/code&gt; with a body that has &lt;code&gt;"id": 5&lt;/code&gt;, that's a conflicting request and we return &lt;code&gt;400 Bad Request&lt;/code&gt; immediately.&lt;/p&gt;

&lt;p&gt;Next, we check whether the pizza actually exists before trying to update it. If it doesn't, &lt;code&gt;404 Not Found&lt;/code&gt; is the correct response   not a silent no-op.&lt;/p&gt;

&lt;p&gt;On success, we return &lt;code&gt;204 No Content&lt;/code&gt;. There's no need to send the updated pizza back in the response body; the client already has it since they just sent it to us.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;HTTP Status&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NoContent()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;204 No Content&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Update was applied successfully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BadRequest()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL id and body id don't match&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NotFound()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;404 Not Found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No pizza with that ID exists&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  DELETE   Removing a Pizza
&lt;/h2&gt;

&lt;p&gt;Replace the &lt;code&gt;// DELETE action&lt;/code&gt; comment with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NoContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the most straightforward of the three. The route is &lt;code&gt;DELETE /pizza/{id}&lt;/code&gt;   no request body needed, just the ID in the URL.&lt;/p&gt;

&lt;p&gt;We verify the pizza exists first. If not, &lt;code&gt;404&lt;/code&gt;. If it does, we delete it and return &lt;code&gt;204 No Content&lt;/code&gt; to confirm the operation succeeded without sending anything back.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;HTTP Status&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NoContent()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;204 No Content&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pizza was deleted successfully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NotFound()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;404 Not Found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No pizza with that ID exists&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Complete PizzaController
&lt;/h2&gt;

&lt;p&gt;Here's the full controller at this point   all five actions in one place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PizzaController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PizzaController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpPost&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;CreatedAtAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpPut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Pizza&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;BadRequest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;existingPizza&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existingPizza&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NoContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NoContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean, readable, and covers all five operations in under 50 lines of code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Build &amp;amp; Run
&lt;/h2&gt;

&lt;p&gt;Save the file, then verify there are no errors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Testing the Write Endpoints
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;ContosoPizza.http&lt;/code&gt; and add the following test requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;### Add a new pizza
POST {{ContosoPizza_HostAddress}}/pizza/
Content-Type: application/json

{
  "name": "Hawaii",
  "isGlutenFree": false
}

###

### Update it (fix the name)
PUT {{ContosoPizza_HostAddress}}/pizza/3
Content-Type: application/json

{
  "id": 3,
  "name": "Hawaiian",
  "isGlutenFree": false
}

###

### Confirm the update
GET {{ContosoPizza_HostAddress}}/pizza/3
Accept: application/json

###

### Delete it
DELETE {{ContosoPizza_HostAddress}}/pizza/3

###

### Confirm it's gone
GET {{ContosoPizza_HostAddress}}/pizza/
Accept: application/json

###
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run them in order and here's what you should see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;POST&lt;/strong&gt; → &lt;code&gt;201 Created&lt;/code&gt; with the new pizza in the body and a &lt;code&gt;Location&lt;/code&gt; header&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PUT&lt;/strong&gt; → &lt;code&gt;204 No Content&lt;/code&gt; (no body, just confirmation)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GET &lt;code&gt;/pizza/3&lt;/code&gt;&lt;/strong&gt; → &lt;code&gt;200 OK&lt;/code&gt; showing the updated name "Hawaiian"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DELETE&lt;/strong&gt; → &lt;code&gt;204 No Content&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GET &lt;code&gt;/pizza/&lt;/code&gt;&lt;/strong&gt; → &lt;code&gt;200 OK&lt;/code&gt; with only the original two pizzas   ID 3 is gone&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Note:&lt;/strong&gt; Since we're using in-memory storage, IDs don't persist across server restarts. Every time you run &lt;code&gt;dotnet run&lt;/code&gt;, the list resets to the two seed pizzas.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What's Coming in &lt;a href="https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-4-putting-it-all-together-beyond-2dim"&gt;Part 4&lt;/a&gt;,?
&lt;/h2&gt;

&lt;p&gt;Our API is now fully functional. In the final part, we'll wrap everything up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A recap of how all the pieces fit together&lt;/li&gt;
&lt;li&gt;A look at what you'd change moving from in-memory to a real database&lt;/li&gt;
&lt;li&gt;Next steps for taking your API further (authentication, validation, Swagger UI)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Only one part left   follow along so you don't miss the wrap-up. Questions? Drop them in the comments!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; &lt;code&gt;dotnet&lt;/code&gt; &lt;code&gt;csharp&lt;/code&gt; &lt;code&gt;webapi&lt;/code&gt; &lt;code&gt;aspnetcore&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>beginners</category>
      <category>dotnet</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building Your First Web API with ASP.NET Core Part 2: Controllers, Models &amp; Your First Endpoint</title>
      <dc:creator>Ahsan Khan</dc:creator>
      <pubDate>Tue, 10 Mar 2026 00:42:24 +0000</pubDate>
      <link>https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-2-controllers-models-your-first-endpoint-2157</link>
      <guid>https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-2-controllers-models-your-first-endpoint-2157</guid>
      <description>&lt;p&gt;&lt;em&gt;This is Part 2 of a 4-part series. In &lt;a href="https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-1-getting-started-with-rest-3gc7"&gt;Part 1&lt;/a&gt;, we set up the project and got our first response from the default weather endpoint. Now it's time to build something that actually belongs to us.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Where We Left Off
&lt;/h2&gt;

&lt;p&gt;At the end of Part 1, you had a running ASP.NET Core project with a scaffolded &lt;code&gt;WeatherForecastController&lt;/code&gt;. We hit it in the browser and got back JSON   great. But that controller isn't ours, and it doesn't do anything useful for a pizza inventory system.&lt;/p&gt;

&lt;p&gt;In this part, we'll:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand how ASP.NET Core controllers work under the hood&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;Pizza&lt;/code&gt; model to represent our data&lt;/li&gt;
&lt;li&gt;Build an in-memory data service&lt;/li&gt;
&lt;li&gt;Wire up a &lt;code&gt;PizzaController&lt;/code&gt; with working GET endpoints&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Controllers Actually Work
&lt;/h2&gt;

&lt;p&gt;Before writing our own controller, let's decode the sample one that came with the project. Here's the full &lt;code&gt;WeatherForecastController&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WeatherForecastController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;Summaries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"Freezing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Bracing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Chilly"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Cool"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Mild"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Warm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Balmy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hot"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Sweltering"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Scorching"&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecastController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;WeatherForecastController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ILogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecastController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_logger&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GetWeatherForecast"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecast&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Enumerable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;WeatherForecast&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Date&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;TemperatureC&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;55&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Summary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Summaries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Shared&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Summaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a lot happening in a small amount of code. Let's break it down.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Base Class: &lt;code&gt;ControllerBase&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;A controller is just a public class whose public methods are exposed as HTTP endpoints. By convention, controllers live in the &lt;code&gt;Controllers/&lt;/code&gt; directory and their class names end with &lt;code&gt;Controller&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This class inherits from &lt;code&gt;ControllerBase&lt;/code&gt;, which gives it everything needed to handle HTTP requests   parsing request data, returning status codes, sending responses. You don't write any of that plumbing yourself.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;Important:&lt;/strong&gt; You might have seen &lt;code&gt;Controller&lt;/code&gt; used in MVC tutorials. Don't use that here. &lt;code&gt;Controller&lt;/code&gt; extends &lt;code&gt;ControllerBase&lt;/code&gt; with view-rendering support, which is meant for pages   not APIs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Two Key Attributes
&lt;/h3&gt;

&lt;p&gt;Two attributes sit on top of the class definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;[ApiController]&lt;/code&gt;&lt;/strong&gt; turns on a set of smart, opinionated behaviors that make API development easier:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It automatically infers where parameters come from (query string, body, route, etc.)&lt;/li&gt;
&lt;li&gt;It enforces attribute routing   no routes work without explicit &lt;code&gt;[Route]&lt;/code&gt; or HTTP verb attributes&lt;/li&gt;
&lt;li&gt;It handles model validation errors automatically, returning a &lt;code&gt;400 Bad Request&lt;/code&gt; without extra code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;[Route("[controller]")]&lt;/code&gt;&lt;/strong&gt; maps the URL pattern for this controller. The &lt;code&gt;[controller]&lt;/code&gt; token is a placeholder   it gets replaced at runtime with the controller's class name minus the &lt;code&gt;Controller&lt;/code&gt; suffix. So &lt;code&gt;WeatherForecastController&lt;/code&gt; handles requests to &lt;code&gt;/weatherforecast&lt;/code&gt;, and our upcoming &lt;code&gt;PizzaController&lt;/code&gt; will handle &lt;code&gt;/pizza&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Action Method
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"GetWeatherForecast"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WeatherForecast&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;[HttpGet]&lt;/code&gt; attribute marks this method as the handler for HTTP GET requests to this controller's route. When someone hits &lt;code&gt;GET /weatherforecast&lt;/code&gt;, this method runs and its return value gets automatically serialized to JSON.&lt;/p&gt;




&lt;h2&gt;
  
  
  Creating the Pizza Model
&lt;/h2&gt;

&lt;p&gt;Now that we understand the structure, let's build our own. We'll start with the data model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a &lt;code&gt;Models&lt;/code&gt; folder:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;Models
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Inside it, create &lt;code&gt;Pizza.cs&lt;/code&gt; and add the following:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pizza&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;IsGlutenFree&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple and intentional   an ID, a name, and a gluten-free flag. This is our core data shape. The &lt;code&gt;Models/&lt;/code&gt; directory name follows the model-view-controller convention, keeping things organized as the project grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the In-Memory Data Service
&lt;/h2&gt;

&lt;p&gt;In a production app, you'd reach for a database. For now, we'll use a static in-memory list   fast to set up, easy to understand, and perfectly fine for learning the API layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create a &lt;code&gt;Services&lt;/code&gt; folder:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;Services
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Inside it, create &lt;code&gt;PizzaService.cs&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PizzaService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Pizzas&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;nextId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Pizzas&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Pizza&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Classic Italian"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IsGlutenFree&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Pizza&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Veggie"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="n"&gt;IsGlutenFree&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Pizzas&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Pizzas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nextId&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
        &lt;span class="n"&gt;Pizzas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Pizzas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Pizzas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FindIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;Pizzas&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth noting here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The list is &lt;strong&gt;static&lt;/strong&gt;, meaning it's shared across all requests for the lifetime of the app&lt;/li&gt;
&lt;li&gt;It starts with &lt;strong&gt;two seed pizzas&lt;/strong&gt; so we have something to query immediately&lt;/li&gt;
&lt;li&gt;Every time you stop and restart the server, the list resets   that's expected for in-memory storage&lt;/li&gt;
&lt;li&gt;The service exposes &lt;code&gt;GetAll&lt;/code&gt;, &lt;code&gt;Get&lt;/code&gt;, &lt;code&gt;Add&lt;/code&gt;, &lt;code&gt;Update&lt;/code&gt;, and &lt;code&gt;Delete&lt;/code&gt;   exactly the CRUD surface our API will need&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Creating the PizzaController
&lt;/h2&gt;

&lt;p&gt;With our model and service in place, it's time to hook everything together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Inside &lt;code&gt;Controllers/&lt;/code&gt;, create &lt;code&gt;PizzaController.cs&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Microsoft.AspNetCore.Mvc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;ContosoPizza.Controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ApiController&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[controller]"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PizzaController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ControllerBase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;PizzaController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// GET all action&lt;/span&gt;
    &lt;span class="c1"&gt;// GET by Id action&lt;/span&gt;
    &lt;span class="c1"&gt;// POST action&lt;/span&gt;
    &lt;span class="c1"&gt;// PUT action&lt;/span&gt;
    &lt;span class="c1"&gt;// DELETE action&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The comments are placeholders   we'll fill them in as we go. For now, the controller is wired to handle requests to &lt;code&gt;/pizza&lt;/code&gt;, thanks to &lt;code&gt;[Route("[controller]")]&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding the GET Endpoints
&lt;/h2&gt;

&lt;p&gt;Let's implement the two read operations first.&lt;/p&gt;

&lt;h3&gt;
  
  
  GET all pizzas
&lt;/h3&gt;

&lt;p&gt;Replace the &lt;code&gt;// GET all action&lt;/code&gt; comment with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAll&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Responds exclusively to &lt;code&gt;GET /pizza&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Returns an &lt;code&gt;ActionResult&amp;lt;List&amp;lt;Pizza&amp;gt;&amp;gt;&lt;/code&gt;   the wrapper type that lets ASP.NET Core handle serialization and status codes correctly&lt;/li&gt;
&lt;li&gt;Pulls the full list from our service and automatically sends it as JSON&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  GET a single pizza by ID
&lt;/h3&gt;

&lt;p&gt;Replace the &lt;code&gt;// GET by Id action&lt;/code&gt; comment with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;HttpGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{id}"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pizza&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PizzaService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizza&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;NotFound&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;pizza&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles &lt;code&gt;GET /pizza/{id}&lt;/code&gt;   a separate route from the one above&lt;/li&gt;
&lt;li&gt;Returns a &lt;code&gt;404 Not Found&lt;/code&gt; if no matching pizza exists, via the built-in &lt;code&gt;NotFound()&lt;/code&gt; helper&lt;/li&gt;
&lt;li&gt;Returns the pizza object directly (serialized to JSON) if found&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how the status codes map:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;th&gt;HTTP Status&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Returns pizza&lt;/td&gt;
&lt;td&gt;&lt;code&gt;200 OK&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ID matched a pizza in the list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NotFound()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;404 Not Found&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No pizza with that ID exists&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Build &amp;amp; Test
&lt;/h2&gt;

&lt;p&gt;Make sure everything compiles cleanly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test with the .http file
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;ContosoPizza.http&lt;/code&gt; and add these requests below the existing ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET {{ContosoPizza_HostAddress}}/pizza/
Accept: application/json

###

GET {{ContosoPizza_HostAddress}}/pizza/1
Accept: application/json

###

GET {{ContosoPizza_HostAddress}}/pizza/5
Accept: application/json

###
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hit &lt;strong&gt;Send Request&lt;/strong&gt; on each one. You should see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/pizza/&lt;/code&gt; → &lt;code&gt;200 OK&lt;/code&gt; with both pizzas as a JSON array&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/pizza/1&lt;/code&gt; → &lt;code&gt;200 OK&lt;/code&gt; with just "Classic Italian"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/pizza/5&lt;/code&gt; → &lt;code&gt;404 Not Found&lt;/code&gt; (that ID doesn't exist)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is important   your API handles bad input gracefully without any extra work from you.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Coming in &lt;a href="https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-3-implementing-post-put-delete-3595"&gt;Part 3&lt;/a&gt;?
&lt;/h2&gt;

&lt;p&gt;The read side of our API is working. In &lt;strong&gt;Part 3&lt;/strong&gt;, we'll implement the write operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POST /pizza&lt;/code&gt;   add a new pizza&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUT /pizza/{id}&lt;/code&gt;   update an existing one&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DELETE /pizza/{id}&lt;/code&gt;   remove it from the list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll also cover how &lt;code&gt;IActionResult&lt;/code&gt; differs from &lt;code&gt;ActionResult&amp;lt;T&amp;gt;&lt;/code&gt; and why it matters for write operations.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Questions so far? Drop them in the comments. See you in &lt;a href="https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-3-implementing-post-put-delete-3595"&gt;Part 3&lt;/a&gt;,!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; &lt;code&gt;dotnet&lt;/code&gt; &lt;code&gt;csharp&lt;/code&gt; &lt;code&gt;webapi&lt;/code&gt; &lt;code&gt;aspnetcore&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building Your First Web API with ASP.NET Core Part 1: Getting Started with REST</title>
      <dc:creator>Ahsan Khan</dc:creator>
      <pubDate>Tue, 10 Mar 2026 00:38:25 +0000</pubDate>
      <link>https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-1-getting-started-with-rest-3gc7</link>
      <guid>https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-1-getting-started-with-rest-3gc7</guid>
      <description>&lt;p&gt;&lt;em&gt;This is Part 1 of a 4-part series where we build a fully functional RESTful Web API using ASP.NET Core controllers and C#. By the end of the series, you'll have a working API that handles create, read, update, and delete (CRUD) operations.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Are We Building?
&lt;/h2&gt;

&lt;p&gt;Imagine you've just joined the dev team at a pizza company. Your first task? Build the backend API that will power both the company's website and mobile app   specifically, an inventory management system for pizzas.&lt;/p&gt;

&lt;p&gt;That means your API needs to support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fetching&lt;/strong&gt; the full pizza catalog&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adding&lt;/strong&gt; new pizza types&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Updating&lt;/strong&gt; existing entries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Removing&lt;/strong&gt; items that are discontinued&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a perfect real-world scenario to understand REST and ASP.NET Core controllers, so let's dig in.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is REST, Anyway?
&lt;/h2&gt;

&lt;p&gt;Before writing a single line of code, it helps to understand what REST actually means in practice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;REST (Representational State Transfer)&lt;/strong&gt; is an architectural pattern for designing networked services. It uses standard HTTP verbs   the same ones your browser uses every day   to perform operations on resources.&lt;/p&gt;

&lt;p&gt;Here's the quick mapping:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;HTTP Verb&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetch data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create something new&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace/update existing data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PATCH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partially modify existing data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A RESTful API is defined by three things: a &lt;strong&gt;base URI&lt;/strong&gt; (like &lt;code&gt;https://api.example.com&lt;/code&gt;), these &lt;strong&gt;HTTP methods&lt;/strong&gt;, and a &lt;strong&gt;data format&lt;/strong&gt;   usually JSON, sometimes XML.&lt;/p&gt;

&lt;p&gt;Routing ties it all together. For example, requests to &lt;code&gt;/pizza&lt;/code&gt; might go to a &lt;code&gt;PizzaController&lt;/code&gt;, while &lt;code&gt;/order&lt;/code&gt; routes to an &lt;code&gt;OrderController&lt;/code&gt;. Each resource gets its own logical home in your code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why ASP.NET Core for Web APIs?
&lt;/h2&gt;

&lt;p&gt;You might be wondering   why ASP.NET Core specifically? A few reasons stand out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON out of the box.&lt;/strong&gt; You don't need to configure anything special to return JSON. ASP.NET Core serializes your C# classes automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security built in.&lt;/strong&gt; HTTPS is enabled by default, and there's native support for JWT-based authentication and flexible, policy-driven authorization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attribute-based routing.&lt;/strong&gt; Instead of managing routing in a central config file, you define routes directly on your controller methods using attributes. Clean and co-located.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code reuse across .NET.&lt;/strong&gt; The same models and business logic you write for your API can be shared with mobile, desktop, or other .NET services   no duplication needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before following along, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic familiarity with C# (beginner level is fine)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dotnet.microsoft.com/download" rel="noopener noreferrer"&gt;.NET 8 SDK&lt;/a&gt; installed locally&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit" rel="noopener noreferrer"&gt;C# Dev Kit extension&lt;/a&gt; for VS Code&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client" rel="noopener noreferrer"&gt;REST Client extension&lt;/a&gt; for VS Code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To confirm your .NET version, run this in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet &lt;span class="nt"&gt;--list-sdks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see something like &lt;code&gt;8.0.100&lt;/code&gt; in the output. If not, grab the latest .NET 8 SDK from the link above.&lt;/p&gt;




&lt;h2&gt;
  
  
  Creating the Project
&lt;/h2&gt;

&lt;p&gt;Open VS Code, then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;File &amp;gt; Open Folder&lt;/strong&gt; and create a new folder called &lt;code&gt;ContosoPizza&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open the integrated terminal via &lt;strong&gt;View &amp;gt; Terminal&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Run the following command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet new webapi &lt;span class="nt"&gt;-controllers&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; net8.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This scaffolds a new ASP.NET Core Web API project with controller support. You'll end up with a structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── Controllers/
├── obj/
├── Properties/
├── appsettings.Development.json
├── appsettings.json
├── ContosoPizza.csproj
├── ContosoPizza.http
├── Program.cs
└── WeatherForecast.cs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what matters most right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Controllers/&lt;/code&gt;&lt;/strong&gt;   Where your API endpoint logic lives&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Program.cs&lt;/code&gt;&lt;/strong&gt;   App startup configuration and HTTP pipeline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ContosoPizza.http&lt;/code&gt;&lt;/strong&gt;   A handy file for testing your API right inside VS Code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ContosoPizza.csproj&lt;/code&gt;&lt;/strong&gt;   Project metadata and dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 VS Code might prompt you to add debugging assets. Click &lt;strong&gt;Yes&lt;/strong&gt;   it's worth it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Running It for the First Time
&lt;/h2&gt;

&lt;p&gt;Start the dev server with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see output similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Now listening on: https://localhost:7294
Now listening on: http://localhost:5118
Application started. Press Ctrl+C to shut down.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note your HTTPS port number   you'll need it throughout this series.&lt;/p&gt;

&lt;p&gt;Now open your browser and navigate to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://localhost:{PORT}/weatherforecast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a JSON response with randomized weather data   that's the sample endpoint ASP.NET scaffolded for you. It proves everything is wired up correctly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Testing with the .http File
&lt;/h2&gt;

&lt;p&gt;The project includes a &lt;code&gt;ContosoPizza.http&lt;/code&gt; file that works with the REST Client extension you installed. Open it up   it should already have a &lt;code&gt;GET&lt;/code&gt; request pointed at the &lt;code&gt;/weatherforecast&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;Send Request&lt;/strong&gt; above the GET line, and you'll see the full HTTP response in a side panel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json; charset=utf-8&lt;/span&gt;
&lt;span class="s"&gt;...&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2024-01-18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"temperatureC"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"temperatureF"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Warm"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a great workflow   no need to leave VS Code to test your endpoints.&lt;/p&gt;




&lt;h2&gt;
  
  
  Optional: Explore with HTTP REPL
&lt;/h2&gt;

&lt;p&gt;If you prefer the terminal, ASP.NET Core has a dedicated interactive tool for API exploration called &lt;strong&gt;HTTP REPL&lt;/strong&gt;. Install it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet tool &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; Microsoft.dotnet-httprepl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then connect to your running API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;httprepl https://localhost:&lt;span class="o"&gt;{&lt;/span&gt;PORT&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, you can browse your API like a file system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;                    &lt;span class="c"&gt;# See available endpoints&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;WeatherForecast    &lt;span class="c"&gt;# Navigate to an endpoint&lt;/span&gt;
get                   &lt;span class="c"&gt;# Fire a GET request&lt;/span&gt;
&lt;span class="nb"&gt;exit&lt;/span&gt;                  &lt;span class="c"&gt;# Quit the REPL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's a neat way to explore and verify your API structure as you build it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Coming in &lt;a href="https://dev.to/mhd-ahsan-khan/building-your-first-web-api-with-aspnet-core-part-2-controllers-models-your-first-endpoint-2157"&gt;Part 2&lt;/a&gt;,?
&lt;/h2&gt;

&lt;p&gt;Right now, the project just has a sample weather forecast endpoint. In &lt;strong&gt;Part 2&lt;/strong&gt;, we'll:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand how ASP.NET Core controllers actually work&lt;/li&gt;
&lt;li&gt;Break down the &lt;code&gt;WeatherForecastController&lt;/code&gt; code&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;Pizza&lt;/code&gt; model and an in-memory data service&lt;/li&gt;
&lt;li&gt;Wire up our own &lt;code&gt;PizzaController&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the end of Part 2, you'll be fetching pizza data through your own API endpoint.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow along so you don't miss the next part. If you have questions or run into setup issues, drop them in the comments!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tags:&lt;/strong&gt; &lt;code&gt;dotnet&lt;/code&gt; &lt;code&gt;csharp&lt;/code&gt; &lt;code&gt;webapi&lt;/code&gt; &lt;code&gt;aspnetcore&lt;/code&gt; &lt;code&gt;beginners&lt;/code&gt;&lt;/p&gt;

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