- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
Guided: Hardening ASP.NET Core 10 Web APIs with Rate Limiting, HybridCache, and Health Checks
Modern APIs need more than correctly implemented endpoints that adhere to functional requirements and trigger the right business logic. They need protection, performance, and visibility. In this guided Code Lab, you’ll harden an existing ASP.NET Core 10 Web API by adding rate limiting, HybridCache, and health checks. By the end, you’ll have a more resilient API that can throttle abusive traffic, serve repeated requests more efficiently, and expose meaningful health signals for operational monitoring.
Lab Info
Table of Contents
-
Challenge
Introduction
Welcome to this guided code lab on hardening ASP.NET Core 10 Web APIs.
Building an API that returns the correct data is only part of the job. In production, an API also needs to protect itself from abusive or accidental overuse, respond efficiently under repeated access patterns, and expose useful operational signals so other systems can determine whether it is running and ready to serve traffic. Those concerns are easy to postpone during early development, but they become essential once an API starts handling real users, integrations, or background workloads.
What You’ll Learn
In this lab, you’ll learn how to:
- Protect endpoint groups from excessive traffic by using a rate limiter
- Cache repeated reads without changing the API contract with a hybrid cache
- Expose liveness and readiness endpoints for operational use via a custom health check endpoint
- Verify that the API behaves correctly under throttling, cache reuse, and health probe scenarios
Scenario
You’ve inherited a working product catalog API. Functionally, it does its job: it serves product data through HTTP endpoints and responds correctly to requests. However, it still behaves like a development-only service.
Right now:
- Clients can send repeated requests without meaningful limits
- Expensive or repeated reads are recalculated every time
- There are no dedicated health endpoints for infrastructure or orchestration tools
Your task is to harden the API so it behaves more like a service that is ready for real-world deployment.
What You’ll Build
By the end of the lab, you will have an ASP.NET Core 10 API that:
- Throttles bursts of incoming traffic on selected endpoints
- Caches repeated read operations using HybridCache
- Exposes separate health endpoints for liveness and readiness 4 Is easier to validate, monitor, and operate
The result is still a simple API, but it is significantly closer to production readiness.
The Application
The lab uses ASP.NET Core 10 Minimal APIs with all REST API endpoints registered in the
Program.csfile. In addition to this, there are the following files:Models/ProductDto.cs--- The data transfer object containing the details of an individual product.Repositories/IProductRepository.cs--- Interface to retrieve the product information from the data store.Repositories/InMemoryProductRepository.cs--- In-memory product catalogue store.Services/ProductService.cs--- Service layer that acts as the middleware between the API endpoints and the inner repositories.
Prerequisites
Before starting this lab, you should already be comfortable with:
- C# and .NET
- basic ASP.NET Core Web API development
- dependency injection
- minimal APIs or endpoint mapping
- reading and modifying existing code
You do not need deep prior experience with rate limiting, HybridCache, or health checks. This lab is designed to teach those features through guided implementation.
Note: Complete the tasks in order. To ensure your changes don’t introduce compiler errors, run
dotnet build src/Catalog.Api.csprojfrequently. -
Challenge
Protect the Read Endpoints with Rate Limiting
ASP.NET Core’s rate limiting middleware is designed to let you define policies in service registration, enable the middleware, and then apply named policies to specific endpoints or groups. That makes it a good fit for protecting only the product read routes, instead of throttling the whole application. Here's the breakdown of what these rate-limiting options mean:
RejectionStatusCode: defines which HTTP status code is returned when the number of incoming requests exceeds the limit, which is 429 by default.AddFixedWindowLimiter(): adds the fixed time window limiter. Its settings are as follows:PermitLimit: defines the maximum number of incoming requests allowed during the set time window.Window: the rate-limiting time window in seconds.QueueLimit: defines how many requests can be queued while the rate limit is exceeded.QueueProcessingOrder: defines the process order of the queue.
-
Challenge
Cache Repeated Reads with HybridCache
Retrieving data from memory is faster than reading from disks. This is why caching is widely used to improve applictaion performance, especially on the endpoints that are used often.
The default caching implementation in ASP.NET Core is
HybridCache. You will apply it to the product endpoints in the application.builder.Services.AddHybridCache()adds theHybridCachedependency to the applictaion, making it injectable into services and usable as the cache. It has the following settings:MaximumPayloadBytes: the maximum size in bytes of the data the cache can store for any given key.MaximumKeyLength: the maximum length of the dictionary-like key for a cache entry.DefaultEntryOptions: defines the settings for each individual cache entry, such as these:Expiration: sets the expiration time in seconds for the distributed portion of the cache.LocalExpiration: sets the expiration time in seconds for the local in-memory portion of the cache.
-
Challenge
Add Liveness and Readiness Health Checks
When you run your applictaion in production, you will need a mechanism that allows you to check whether your applictaion is running and what state it is in. This is what liveness and health check endpoints are for.
ASP.NET Core health checks can be registered with
AddCheck(), filtered by tags, and exposed on separate endpoints withMapHealthChecks(). The readiness endpoints filtered by tags and liveness endpoints that exclude dependency checks, plus status-code mapping for unhealthy responses. TheCatalogStoreHealthCheckclass implements theIHealthCheckinterface. This is how you tell the ASP.NET Core middleware that this class contains health check logic. Based on the conditions defined by this logic, the application status would either be shown as healthy, unhealthy, or degraded.- Healthy means the application is in a good state.
- Unhealthy means that there is a major problem with the application.
- Degraded means that, while the application is running and is performing its general functionality, there are some problems with it.
In this specific example, this is what triggers different statuses:
- The application is considered healthy when the product store is reachable, and it returns data.
- The application is considered degraded if the product store is reachable, but there is no data.
- The application is considered unhealthy when an attempt to reach the product store throws an error. The
builder.Services.AddHealthChecks()method registers health check dependencies.AddCheck()adds a specific health check mechanism, such as a custom class that implements theIHealthCheckinterface. In this example, you are adding the mapping to the newly addedCatalogStoreHealthCheckclass.
These are the parameters you are using:
catalog-storeis the custom name of the healthcheck.failureStatus: HealthStatus.Unhealthysets what status is considered as the application failure.tags: new[] { "ready" }sets the custom tags the healthcheck can be filtered by. In this example, it's a singlereadytag.
-
Challenge
Conclusion
Congratulations! You’ve completed the lab.
Over the course of these exercises, you took a working ASP.NET Core 10 Web API and made it significantly more production-ready. Rather than changing the API’s business purpose or redesigning its architecture, you focused on three high-value platform capabilities that improve how the application behaves under real operational conditions: rate limiting,
HybridCache, and health checks.All these features are important because production readiness is not just about whether an endpoint returns the right data. It is also about whether the service can protect itself, respond efficiently, and communicate its state clearly to the systems and people that depend on it.
What You Accomplished
In this lab, you implemented a set of practical improvements that directly affect resilience, performance, and observability.
- You protected the API with rate limiting
- You improved repeated reads with HybridCache
- You added liveness and readiness health checks
These principles, when combined, make your APIs more robust and improve the performance of your application.
Why These Changes Matter Together
Each feature you added solves a different problem:
- Rate limiting controls incoming pressure
- Caching reduces repeated internal work
- Health checks expose operational status
Individually, each one is useful. Together, they create a more reliable service boundary.
A hardened API should not only return correct responses under ideal conditions. It should also:
- Behave sensibly when traffic spikes
- Avoid unnecessary repeated work
- Communicate clearly when it is healthy or unhealthy
That is exactly what you built in this lab.
Final Reflection
This lab focused on a valuable mindset shift: a good API is not defined only by its business behavior, but also by how well it performs under pressure, how efficiently it uses resources, and how clearly it reports its condition.
That perspective becomes more important as applications move from local development into shared environments, cloud deployments, and production operations.
You now have hands-on experience implementing three practical features that move an ASP.NET Core 10 API closer to that standard.
Next Steps
After this lab, a good next step would be to continue exploring areas such as:
- API security and authentication
- Deeper caching strategies and distributed systems considerations
- Structured observability with logs, metrics, and tracing
- Deployment and readiness patterns in containerized environments
- Resilience patterns for downstream dependency failures
Each of those topics builds on the same goal you practiced here: making services not just functional, but dependable.
Great work completing the lab.
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.