<?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: Ryan Mitchell</title>
    <description>The latest articles on DEV Community by Ryan Mitchell (@ryanmitchelliqlance).</description>
    <link>https://dev.to/ryanmitchelliqlance</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4119447%2Faaeafd83-7863-471f-8a06-fe651a503a61.png</url>
      <title>DEV Community: Ryan Mitchell</title>
      <link>https://dev.to/ryanmitchelliqlance</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ryanmitchelliqlance"/>
    <language>en</language>
    <item>
      <title>5 Software Testing Mistakes Developers Should Avoid in 2027</title>
      <dc:creator>Ryan Mitchell</dc:creator>
      <pubDate>Tue, 27 Oct 2026 10:57:16 +0000</pubDate>
      <link>https://dev.to/ryanmitchelliqlance/5-software-testing-mistakes-developers-should-avoid-in-2027-2ln8</link>
      <guid>https://dev.to/ryanmitchelliqlance/5-software-testing-mistakes-developers-should-avoid-in-2027-2ln8</guid>
      <description>&lt;p&gt;Software testing is an essential part of building reliable applications. As software systems become more interconnected, development teams must consider security, performance, usability, compatibility, and data integrity throughout the development process. A feature that works correctly in a developer's environment may still fail when it encounters unexpected input, heavy traffic, or a different device configuration.&lt;/p&gt;

&lt;p&gt;In 2027, developers will continue to face challenges associated with rapid release cycles, automated testing, artificial intelligence, and increasingly complex application architectures. Avoiding common testing mistakes can help teams identify defects earlier, reduce rework, and deliver more dependable software.&lt;/p&gt;

&lt;p&gt;Whether a team builds web platforms, enterprise applications, or mobile products, a well-planned testing strategy should be part of the entire software development lifecycle. Here are five common software testing mistakes developers should avoid.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Relying Too Heavily on Manual Testing
&lt;/h2&gt;

&lt;p&gt;Manual testing remains valuable because human testers can evaluate usability, visual consistency, and unexpected user behavior. However, depending entirely on manual testing can create problems when applications grow or updates become more frequent.&lt;/p&gt;

&lt;p&gt;Testers may need to repeat the same steps after every code change. As the number of features increases, regression testing becomes more time-consuming. Repetitive tasks can also introduce human error, allowing defects to go unnoticed.&lt;/p&gt;

&lt;p&gt;For example, imagine an online shopping application that receives frequent updates to its checkout process. A developer changes the discount calculation, but the change accidentally affects shipping costs. If testers focus only on the updated discount feature, the shipping issue may remain undetected.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to avoid this mistake
&lt;/h3&gt;

&lt;p&gt;Teams should identify repetitive, predictable test cases that can be automated. Automated tests are particularly useful for calculations, form validation, authentication, API responses, and regression checks.&lt;/p&gt;

&lt;p&gt;A practical approach includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automating stable, frequently repeated test cases.&lt;/li&gt;
&lt;li&gt;Keeping manual testing for exploratory checks and usability evaluations.&lt;/li&gt;
&lt;li&gt;Running automated tests whenever important code changes are introduced.&lt;/li&gt;
&lt;li&gt;Reviewing failed tests instead of automatically assuming they are irrelevant.&lt;/li&gt;
&lt;li&gt;Maintaining test scripts as application behavior evolves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Automation does not eliminate the need for human judgment. Instead, it allows testers to spend more time investigating complex problems while routine checks run consistently.&lt;/p&gt;

&lt;p&gt;The goal is to develop a balanced testing process in which manual and automated methods support one another.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Testing Only the Expected User Scenarios
&lt;/h2&gt;

&lt;p&gt;A common mistake is testing an application only under ideal conditions. Developers may verify that a form accepts valid information or that a dashboard displays data correctly, but they may overlook empty fields, malformed input, duplicate submissions, and interrupted requests.&lt;/p&gt;

&lt;p&gt;Real users do not always follow the expected path. They may enter unexpected characters, lose their internet connection, click a button several times, or submit incomplete information.&lt;/p&gt;

&lt;p&gt;Consider a registration form that works correctly when every field contains valid information. What happens when a user enters an excessively long name, submits an invalid email address, or refreshes the page during submission? Without appropriate testing, these scenarios can result in confusing messages, duplicated records, or application failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to avoid this mistake
&lt;/h3&gt;

&lt;p&gt;Developers should test both expected and unexpected behavior. This includes boundary-value analysis, negative testing, and exploratory testing.&lt;/p&gt;

&lt;p&gt;Useful checks include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Submitting empty or partially completed forms.&lt;/li&gt;
&lt;li&gt;Testing minimum and maximum permitted values.&lt;/li&gt;
&lt;li&gt;Providing invalid, unexpected, or malformed input.&lt;/li&gt;
&lt;li&gt;Simulating timeouts and interrupted network requests.&lt;/li&gt;
&lt;li&gt;Testing repeated clicks and duplicate submissions.&lt;/li&gt;
&lt;li&gt;Checking how the application handles missing or unavailable data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Boundary-value analysis is particularly useful because defects often occur near the limits of permitted input. If a field accepts values between 1 and 100, testers should examine values around both boundaries rather than checking only a typical value such as 50.&lt;/p&gt;

&lt;p&gt;Developers should also consider how errors are communicated. A useful error message explains what went wrong and, where possible, tells the user how to correct it.&lt;/p&gt;

&lt;p&gt;Thorough scenario testing helps uncover defects that may not appear during a routine demonstration but could affect real users after deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Neglecting Performance and Load Testing
&lt;/h2&gt;

&lt;p&gt;An application may function correctly with a small number of users but become slow or unstable when traffic increases. Without performance testing, teams may discover bottlenecks only after deployment, when they are more expensive and disruptive to resolve.&lt;/p&gt;

&lt;p&gt;Performance problems can result from inefficient database queries, excessive API requests, memory leaks, poorly optimized images, or limited server resources. Third-party services can also introduce delays that developers cannot fully control.&lt;/p&gt;

&lt;p&gt;For example, a reporting dashboard may load quickly when it retrieves a few hundred records. When the same dashboard processes hundreds of thousands of records, database queries may take significantly longer, and users may experience timeouts.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to avoid this mistake
&lt;/h3&gt;

&lt;p&gt;Performance testing should begin before an application reaches production. Teams should establish realistic expectations for response times, throughput, and resource consumption.&lt;/p&gt;

&lt;p&gt;Several testing methods can help:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Load testing:&lt;/strong&gt; Evaluates application behavior under an expected level of user activity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stress testing:&lt;/strong&gt; Pushes the system beyond its expected capacity to identify failure points and understand how it recovers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spike testing:&lt;/strong&gt; Examines how the application responds to sudden increases in traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Endurance testing:&lt;/strong&gt; Evaluates whether performance deteriorates during sustained activity.&lt;/p&gt;

&lt;p&gt;Developers should also monitor CPU utilization, memory consumption, database performance, and API latency. These measurements help identify the source of a bottleneck rather than treating every slowdown as a server-capacity problem.&lt;/p&gt;

&lt;p&gt;Testing environments should resemble production as closely as practical. Differences in hardware, network conditions, configuration, and dataset size can make performance results misleading.&lt;/p&gt;

&lt;p&gt;Teams planning new platforms can learn from the evaluation criteria used by &lt;a href="https://www.iqlance.com/software-development-texas/" rel="noopener noreferrer"&gt;software development companies texas&lt;/a&gt; businesses may consider when assessing technical reliability, scalability, and delivery practices. Regardless of the provider, measurable performance requirements are more useful than vague expectations that an application should simply be fast.&lt;/p&gt;

&lt;p&gt;Performance testing should be repeated when major architectural changes occur, database structures change, or new features significantly increase processing requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Treating Security Testing as a Final Step
&lt;/h2&gt;

&lt;p&gt;Security testing is sometimes postponed until shortly before launch. This approach can allow vulnerabilities to remain in the application architecture, dependencies, authentication processes, and data-handling logic throughout development.&lt;/p&gt;

&lt;p&gt;Security problems are not limited to sophisticated attacks. They may involve weak access controls, exposed credentials, insecure session management, insufficient input validation, or excessive permissions.&lt;/p&gt;

&lt;p&gt;For instance, an application may correctly require users to sign in before accessing a dashboard but fail to verify whether each user is authorized to access a particular record. An attacker could potentially manipulate a record identifier and view information belonging to another account.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to avoid this mistake
&lt;/h3&gt;

&lt;p&gt;Security should be integrated into the development and testing process from the beginning.&lt;/p&gt;

&lt;p&gt;Important practices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reviewing authentication and authorization rules.&lt;/li&gt;
&lt;li&gt;Testing access controls for different user roles.&lt;/li&gt;
&lt;li&gt;Checking input validation and output encoding.&lt;/li&gt;
&lt;li&gt;Scanning dependencies for known vulnerabilities.&lt;/li&gt;
&lt;li&gt;Protecting credentials and sensitive information.&lt;/li&gt;
&lt;li&gt;Examining error messages and application logs for data exposure.&lt;/li&gt;
&lt;li&gt;Testing session expiration and account recovery procedures.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Static application security testing can identify certain coding weaknesses before execution. Dynamic testing examines an application while it is running, helping teams identify vulnerabilities that depend on actual application behavior. Dependency scanning and carefully scoped penetration testing can provide additional coverage.&lt;/p&gt;

&lt;p&gt;Automated security tools are useful, but their results require interpretation. A clean scan does not prove that an application is secure, and an alert does not always indicate an exploitable vulnerability.&lt;/p&gt;

&lt;p&gt;Teams should prioritize findings according to their severity, exploitability, and potential business impact. Security fixes should then be verified through retesting and, where appropriate, regression tests.&lt;/p&gt;

&lt;p&gt;Organizations evaluating texas software development companies should also consider whether their development processes include ongoing security reviews rather than treating security as a one-time launch requirement.&lt;/p&gt;

&lt;p&gt;Security testing is most effective when developers, testers, and security specialists collaborate throughout the software lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Failing to Maintain Tests and Test Environments
&lt;/h2&gt;

&lt;p&gt;Writing automated tests is only the beginning. Tests must remain accurate as application requirements, dependencies, APIs, and user interfaces change.&lt;/p&gt;

&lt;p&gt;Poorly maintained test suites can become unreliable. Some tests may fail because of genuine defects, while others fail because selectors changed, test data became outdated, or an external service responded differently.&lt;/p&gt;

&lt;p&gt;When teams repeatedly encounter unreliable test results, they may begin ignoring failures. This creates a serious risk because genuine defects can become difficult to distinguish from problems in the testing process itself.&lt;/p&gt;

&lt;p&gt;Test environments can introduce similar challenges. A feature may pass on one developer's computer but fail in another environment because of different dependency versions, configuration settings, permissions, or database contents.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to avoid this mistake
&lt;/h3&gt;

&lt;p&gt;Developers should treat test maintenance as part of regular engineering work.&lt;/p&gt;

&lt;p&gt;The following practices can improve reliability:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use version-controlled tests:&lt;/strong&gt; Keep test code alongside the application code so that changes can be reviewed and tracked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create repeatable environments:&lt;/strong&gt; Use documented configurations, dependency versions, and containerization where appropriate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manage test data carefully:&lt;/strong&gt; Prepare predictable datasets and isolate tests that modify shared records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Investigate flaky tests:&lt;/strong&gt; Identify whether failures result from timing issues, unstable dependencies, shared state, or genuine application defects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review outdated test cases:&lt;/strong&gt; Remove obsolete checks and update tests when requirements change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run tests in continuous integration:&lt;/strong&gt; Execute relevant checks automatically when code is submitted or merged.&lt;/p&gt;

&lt;p&gt;A reliable test suite should produce consistent results when the application has not changed. If a test passes and fails unpredictably under identical conditions, the team should investigate the reason instead of accepting the inconsistency.&lt;/p&gt;

&lt;p&gt;It is also important to avoid making every test dependent on a live third-party service. Where practical, mocks, stubs, and controlled test environments can isolate application behavior from external failures. Separate integration tests can then verify that the application communicates correctly with real dependencies.&lt;/p&gt;

&lt;p&gt;For teams working with a texas software development company, or evaluating a &lt;a href="https://www.iqlance.com/software-development-san-antonio/" rel="noopener noreferrer"&gt;custom software development company san antonio&lt;/a&gt;, the ability to demonstrate repeatable testing practices is a useful technical consideration. The same principle applies to any provider building software that must remain reliable as requirements change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building a Better Software Testing Strategy for 2027
&lt;/h2&gt;

&lt;p&gt;Avoiding these five mistakes requires more than purchasing a testing tool or adding a final testing phase before launch. Effective quality assurance depends on clear requirements, suitable test coverage, repeatable environments, and continuous collaboration between development and testing teams.&lt;/p&gt;

&lt;p&gt;A practical testing strategy should include unit tests for individual components, integration tests for interactions between systems, and end-to-end tests for important user workflows. Security, accessibility, compatibility, and performance checks should be added according to the application's risks and requirements.&lt;/p&gt;

&lt;p&gt;Teams should also measure testing effectiveness using meaningful indicators. Defect escape rates, recurring production issues, test execution time, and the percentage of critical workflows covered by reliable tests can reveal opportunities for improvement. A high test count alone does not guarantee software quality.&lt;/p&gt;

&lt;p&gt;For mobile products, compatibility deserves additional attention. Different screen sizes, operating system versions, device capabilities, and network conditions can affect the user experience. Teams assessing the practices of a &lt;a href="https://www.iqlance.com/app-development-chicago/" rel="noopener noreferrer"&gt;mobile app development company in chicago illinois&lt;/a&gt; can consider how systematically it approaches device coverage, regression testing, and release validation.&lt;/p&gt;

&lt;p&gt;Artificial intelligence may also support test generation, code analysis, and defect investigation in 2027. However, AI-generated tests can contain incorrect assumptions or miss important business rules. Developers should review generated tests, validate their expected results, and ensure that sensitive source code or data is handled according to organizational policies.&lt;/p&gt;

&lt;p&gt;Ultimately, testing is most valuable when it helps teams make informed decisions about software quality. Developers who test realistic scenarios, measure performance, address security early, and maintain dependable test suites can identify problems before they affect users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Software testing mistakes can increase maintenance costs, delay releases, and reduce user confidence. Relying exclusively on manual testing, overlooking unexpected scenarios, ignoring performance, postponing security checks, and neglecting test maintenance are five common problems development teams should address.&lt;/p&gt;

&lt;p&gt;In 2027, a disciplined testing process will remain essential for building dependable software. By integrating testing throughout development, automating repetitive checks, evaluating realistic conditions, and continuously improving test coverage, teams can reduce avoidable defects and deliver applications that are easier to maintain and more resilient in production.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>testing</category>
      <category>softwareengineering</category>
      <category>ai</category>
    </item>
    <item>
      <title>Building a REST API with ASP.NET Core: A Step-by-Step Guide</title>
      <dc:creator>Ryan Mitchell</dc:creator>
      <pubDate>Tue, 13 Oct 2026 13:23:51 +0000</pubDate>
      <link>https://dev.to/ryanmitchelliqlance/building-a-rest-api-with-aspnet-core-a-step-by-step-guide-5g0a</link>
      <guid>https://dev.to/ryanmitchelliqlance/building-a-rest-api-with-aspnet-core-a-step-by-step-guide-5g0a</guid>
      <description>&lt;p&gt;REST APIs are an essential part of modern application development. They allow web applications, mobile apps, and other software systems to exchange data through standardized HTTP requests. For example, an e-commerce application might use an API to retrieve products, create orders, update customer information, and process inventory changes.&lt;/p&gt;

&lt;p&gt;ASP.NET Core provides a framework for building these APIs using C# and the .NET platform. It supports routing, dependency injection, request validation, authentication, and several other capabilities that help developers create maintainable web services.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will build a simple REST API for managing products. You will learn how to create a project, define a model, implement CRUD operations, validate incoming data, test endpoints, and identify important security considerations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a REST API?
&lt;/h2&gt;

&lt;p&gt;A REST API exposes resources through URLs and uses HTTP methods to perform operations on those resources. A resource might represent a product, customer, order, employee, or any other entity managed by an application.&lt;/p&gt;

&lt;p&gt;Common HTTP methods include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GET:&lt;/strong&gt; Retrieve a resource or collection of resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST:&lt;/strong&gt; Create a new resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PUT:&lt;/strong&gt; Replace or update an existing resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PATCH:&lt;/strong&gt; Apply a partial update to a resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DELETE:&lt;/strong&gt; Remove a resource.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, a product API might expose the following endpoints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /api/products&lt;/code&gt; retrieves all products.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /api/products/1&lt;/code&gt; retrieves a product by its ID.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST /api/products&lt;/code&gt; creates a product.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUT /api/products/1&lt;/code&gt; updates a product.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DELETE /api/products/1&lt;/code&gt; deletes a product.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A well-designed API uses consistent resource names, appropriate HTTP status codes, clear error responses, and predictable request and response formats.&lt;/p&gt;

&lt;p&gt;Microsoft provides an official overview of &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/web-api" rel="noopener noreferrer"&gt;creating web APIs with ASP.NET Core&lt;/a&gt;, including controller-based APIs and Minimal APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create an ASP.NET Core Web API Project
&lt;/h2&gt;

&lt;p&gt;Before starting, install a supported .NET SDK and an editor such as Visual Studio or Visual Studio Code.&lt;/p&gt;

&lt;p&gt;Open a terminal and run:&lt;br&gt;
&lt;/p&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;-n&lt;/span&gt; ProductApi
&lt;span class="nb"&gt;cd &lt;/span&gt;ProductApi
dotnet run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The template creates a project with the initial configuration required to run a web API. Depending on the SDK and template options, the generated project may use Minimal APIs or include example endpoints.&lt;/p&gt;

&lt;p&gt;For this tutorial, we will use controllers because they provide a clear structure for organizing request-handling logic as an application grows.&lt;/p&gt;

&lt;p&gt;Create a folder named &lt;code&gt;Models&lt;/code&gt; and another named &lt;code&gt;Controllers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can review the &lt;a href="https://dotnet.microsoft.com/en-us/platform/support/policy/aspnet" rel="noopener noreferrer"&gt;official .NET and ASP.NET Core support policy&lt;/a&gt; to choose a supported version and plan upgrades. Using a supported release helps ensure access to security fixes and ongoing maintenance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create the Product Model
&lt;/h2&gt;

&lt;p&gt;A model defines the data structure that the application will work with.&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;Models&lt;/code&gt; folder, create a file named &lt;code&gt;Product.cs&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;System.ComponentModel.DataAnnotations&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;ProductApi.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;Product&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="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&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="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;;&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;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1000000&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;decimal&lt;/span&gt; &lt;span class="n"&gt;Price&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;IsAvailable&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="k"&gt;true&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 model defines four properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Id&lt;/code&gt; identifies each product.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Name&lt;/code&gt; stores the product name and cannot be empty.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Price&lt;/code&gt; stores a positive price within the specified range.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;IsAvailable&lt;/code&gt; indicates whether the product is available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The validation attributes describe basic rules for incoming data. For example, a product name longer than 100 characters should not be accepted.&lt;/p&gt;

&lt;p&gt;For a production application, you would generally use separate request DTOs for creating and updating products. This prevents clients from modifying properties they should not control, such as database-generated identifiers or internal administrative fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Register Controllers in Program.cs
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;Program.cs&lt;/code&gt; and configure the application:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&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="nf"&gt;AddControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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="nf"&gt;AddEndpointsApiExplorer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&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="nf"&gt;AddSwaggerGen&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;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&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;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsDevelopment&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSwagger&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSwaggerUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseHttpsRedirection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseAuthorization&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapControllers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main configuration steps are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AddControllers()&lt;/code&gt; registers controller support.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AddEndpointsApiExplorer()&lt;/code&gt; enables endpoint discovery.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AddSwaggerGen()&lt;/code&gt; configures Swagger document generation when the corresponding package is available.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UseHttpsRedirection()&lt;/code&gt; redirects HTTP requests to HTTPS where configured.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MapControllers()&lt;/code&gt; maps controller routes to application endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the generated project does not include Swagger support, install the required package before using these Swagger configuration methods.&lt;/p&gt;

&lt;p&gt;Swagger UI can provide an interactive interface for exploring and testing endpoints during development. It should be configured carefully in deployed environments, especially if the API exposes sensitive functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Implement CRUD Operations
&lt;/h2&gt;

&lt;p&gt;Create a file named &lt;code&gt;ProductsController.cs&lt;/code&gt; inside the &lt;code&gt;Controllers&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;For this introductory example, we will store products in a list held in memory. This makes the API easy to understand without introducing database configuration immediately.&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;using&lt;/span&gt; &lt;span class="nn"&gt;ProductApi.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;ProductApi.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;"api/[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;ProductsController&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="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Products&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="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Product&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;"Keyboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;49.99m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;IsAvailable&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="k"&gt;private&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;2&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;IEnumerable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&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;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Products&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="s"&gt;"{id:int}"&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;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetById&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;product&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Products&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&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="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="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&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;ActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;product&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;Products&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;product&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;GetById&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;product&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;product&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:int}"&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;Product&lt;/span&gt; &lt;span class="n"&gt;updatedProduct&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;product&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Products&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&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="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="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;product&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="n"&gt;updatedProduct&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="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updatedProduct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAvailable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;updatedProduct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAvailable&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:int}"&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;product&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Products&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&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="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="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;Products&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;product&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;Let's understand the important parts of this controller.&lt;/p&gt;

&lt;h3&gt;
  
  
  Routing
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;[Route("api/[controller]")]&lt;/code&gt; attribute defines the base URL. Because the controller is named &lt;code&gt;ProductsController&lt;/code&gt;, the route becomes &lt;code&gt;/api/products&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The HTTP attributes determine which method handles each request. For example, &lt;code&gt;[HttpGet("{id:int}")]&lt;/code&gt; matches requests such as &lt;code&gt;/api/products/1&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  HTTP status codes
&lt;/h3&gt;

&lt;p&gt;The controller returns different status codes depending on the result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;200 OK&lt;/code&gt; indicates that a retrieval succeeded.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;201 Created&lt;/code&gt; indicates that a new product was created.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;204 No Content&lt;/code&gt; indicates that an update or deletion succeeded without returning a response body.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;404 Not Found&lt;/code&gt; indicates that the requested product does not exist.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;400 Bad Request&lt;/code&gt; indicates that the request is invalid.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;CreatedAtAction()&lt;/code&gt; method returns a creation response and identifies the URL where the new resource can be retrieved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Important limitation of the example
&lt;/h3&gt;

&lt;p&gt;The in-memory list is suitable for learning, not for a production API. Data disappears when the application restarts, and concurrent requests can cause consistency problems. The static ID counter is also not a reliable replacement for database-generated identifiers.&lt;/p&gt;

&lt;p&gt;For a real application, use a database such as SQL Server or PostgreSQL, with an appropriate persistence layer and concurrency handling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Validate Incoming Requests
&lt;/h2&gt;

&lt;p&gt;An API should not assume that every client sends valid data.&lt;/p&gt;

&lt;p&gt;Because the controller uses &lt;code&gt;[ApiController]&lt;/code&gt;, ASP.NET Core automatically returns a &lt;code&gt;400 Bad Request&lt;/code&gt; response when model validation fails. Microsoft documents this behavior in its &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/web-api" rel="noopener noreferrer"&gt;web API controller guidance&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, the following request contains an invalid price:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Keyboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;-10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isAvailable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The price violates the model's validation rule, so the framework rejects the request before the controller action executes.&lt;/p&gt;

&lt;p&gt;For more robust applications, consider using request DTOs, domain-level validation, consistent error formats, and tests for boundary conditions.&lt;/p&gt;

&lt;p&gt;Validation should also cover business rules. A syntactically valid order, for example, might still be invalid if the requested quantity exceeds available inventory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Test the API
&lt;/h2&gt;

&lt;p&gt;Run the project:&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;Use the local URL displayed in the terminal. If Swagger is configured, open its interface and test the endpoints. Alternatively, use an API client or a command-line tool such as &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Retrieve products:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; https://localhost:7000/api/products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The port and protocol may differ in your environment. Use the actual HTTPS URL printed by the application.&lt;/p&gt;

&lt;p&gt;Create a product:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  https://localhost:7000/api/products &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"name":"Mouse","price":25.50,"isAvailable":true}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server should return a creation response if the request is valid.&lt;/p&gt;

&lt;p&gt;Next, test a request for a nonexistent ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-i&lt;/span&gt; https://localhost:7000/api/products/999
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expected result is &lt;code&gt;404 Not Found&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A good testing strategy covers successful requests, invalid input, missing resources, unauthorized requests, and unexpected failures. Automated integration tests are especially useful because they help detect regressions when the API changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: Add a Database and Dependency Injection
&lt;/h2&gt;

&lt;p&gt;The next step is to replace the in-memory collection with persistent storage.&lt;/p&gt;

&lt;p&gt;Entity Framework Core is commonly used with ASP.NET Core to work with relational databases. Instead of accessing a static list directly from a controller, introduce a database context or a repository/service abstraction.&lt;/p&gt;

&lt;p&gt;Register the required services in &lt;code&gt;Program.cs&lt;/code&gt;, configure the database connection, and inject the relevant dependency into the controller.&lt;/p&gt;

&lt;p&gt;Dependency injection makes it easier to test components independently and change implementation details without rewriting every controller.&lt;/p&gt;

&lt;p&gt;The exact database setup depends on your chosen database provider, deployment environment, and migration strategy. Connection strings and credentials should be stored securely rather than hard-coded into source files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: Secure the API Before Deployment
&lt;/h2&gt;

&lt;p&gt;A working API is not necessarily a secure API.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://owasp.org/www-project-api-security/" rel="noopener noreferrer"&gt;OWASP API Security Top 10&lt;/a&gt; identifies common API security risks. Its 2023 edition highlights authorization problems among the leading concerns: &lt;strong&gt;three of the five highest-ranked risks involve authorization-related weaknesses&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This illustrates why access control needs to be considered during design, not added as an afterthought.&lt;/p&gt;

&lt;p&gt;Before deploying an API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require authentication for protected endpoints.&lt;/li&gt;
&lt;li&gt;Check authorization for each resource and operation.&lt;/li&gt;
&lt;li&gt;Validate and constrain incoming data.&lt;/li&gt;
&lt;li&gt;Apply rate limits and resource-consumption controls where appropriate.&lt;/li&gt;
&lt;li&gt;Avoid returning sensitive information in error responses.&lt;/li&gt;
&lt;li&gt;Use HTTPS and protect secrets.&lt;/li&gt;
&lt;li&gt;Log relevant security events without exposing passwords, tokens, or private data.&lt;/li&gt;
&lt;li&gt;Keep dependencies and the .NET runtime patched.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Authentication determines who the caller is; authorization determines what that caller can do. A validly authenticated user should not automatically be allowed to access every product, customer, or order in the system.&lt;/p&gt;

&lt;p&gt;For example, an API that accepts an order ID must verify that the requesting user is permitted to access that specific order. Merely checking that the ID exists is insufficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 9: Prepare the API for Real-World Use
&lt;/h2&gt;

&lt;p&gt;Before releasing the API, consider additional requirements that become important as traffic and functionality grow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pagination:&lt;/strong&gt; Avoid returning thousands of records in one response. Support page-based or cursor-based retrieval.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Logging and monitoring:&lt;/strong&gt; Track response times, error rates, and service health so problems can be identified quickly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation:&lt;/strong&gt; Describe endpoints, request schemas, authentication requirements, and error responses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Versioning:&lt;/strong&gt; Plan how clients will continue working when API contracts change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance:&lt;/strong&gt; Measure database query execution, network latency, and resource usage before optimizing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automated testing:&lt;/strong&gt; Include unit tests and integration tests in the development workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment:&lt;/strong&gt; Configure environment-specific settings, health checks, backups, and a rollback strategy.&lt;/p&gt;

&lt;p&gt;These practices help transform a learning project into a maintainable service that other applications can depend on.&lt;/p&gt;

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

&lt;p&gt;Building a REST API with ASP.NET Core involves more than defining routes and returning JSON. Developers must also consider data validation, persistence, status codes, security, testing, and maintenance.&lt;/p&gt;

&lt;p&gt;The example in this guide provides a starting point for understanding controller-based APIs. As your application grows, replace in-memory storage with a database, introduce dependency injection for business logic, implement appropriate authentication and authorization, and add automated tests.&lt;/p&gt;

&lt;p&gt;These principles also help when evaluating &lt;a href="https://www.iqlance.com/asp-net-development-company-usa/" rel="noopener noreferrer"&gt;custom asp net development services&lt;/a&gt; for a project: the important questions concern architecture, code quality, security, documentation, and the team's ability to maintain the application over time.&lt;/p&gt;

&lt;p&gt;For readers researching technology providers as a separate task, this &lt;a href="https://www.iqlance.com/top-software-development-companies-in-texas/" rel="noopener noreferrer"&gt;overview of the best software development companies in texas&lt;/a&gt; offers a starting point for comparing companies. It is separate from the technical steps in this tutorial and is not required to build the API.&lt;/p&gt;

&lt;p&gt;The most effective way to learn is to extend the sample: connect a database, add authentication, write integration tests, and document the endpoints. Each improvement introduces practical engineering decisions that are valuable in real-world software development.&lt;/p&gt;

</description>
      <category>restapi</category>
      <category>asp</category>
      <category>aspnet</category>
      <category>aspdotnet</category>
    </item>
  </channel>
</rss>
