Skip to main content
Redhat Developers  Logo
  • Products

    Platforms

    • Red Hat Enterprise Linux
      Red Hat Enterprise Linux Icon
    • Red Hat AI
      Red Hat AI
    • Red Hat OpenShift
      Openshift icon
    • Red Hat Ansible Automation Platform
      Ansible icon
    • View All Red Hat Products

    Featured

    • Red Hat build of OpenJDK
    • Red Hat Developer Hub
    • Red Hat JBoss Enterprise Application Platform
    • Red Hat OpenShift Dev Spaces
    • Red Hat OpenShift Local
    • Red Hat Developer Sandbox

      Try Red Hat products and technologies without setup or configuration fees for 30 days with this shared Openshift and Kubernetes cluster.
    • Try at no cost
  • Technologies

    Featured

    • AI/ML
      AI/ML Icon
    • Linux
      Linux Icon
    • Kubernetes
      Cloud icon
    • Automation
      Automation Icon showing arrows moving in a circle around a gear
    • View All Technologies
    • Programming Languages & Frameworks

      • Java
      • Python
      • JavaScript
    • System Design & Architecture

      • Red Hat architecture and design patterns
      • Microservices
      • Event-Driven Architecture
      • Databases
    • Developer Productivity

      • Developer productivity
      • Developer Tools
      • GitOps
    • Automated Data Processing

      • AI/ML
      • Data Science
      • Apache Kafka on Kubernetes
    • Platform Engineering

      • DevOps
      • DevSecOps
      • Ansible automation for applications and services
    • Secure Development & Architectures

      • Security
      • Secure coding
  • Learn

    Featured

    • Kubernetes & Cloud Native
      Openshift icon
    • Linux
      Rhel icon
    • Automation
      Ansible cloud icon
    • AI/ML
      AI/ML Icon
    • View All Learning Resources

    E-Books

    • GitOps Cookbook
    • Podman in Action
    • Kubernetes Operators
    • The Path to GitOps
    • View All E-books

    Cheat Sheets

    • Linux Commands
    • Bash Commands
    • Git
    • systemd Commands
    • View All Cheat Sheets

    Documentation

    • Product Documentation
    • API Catalog
    • Legacy Documentation
  • Developer Sandbox

    Developer Sandbox

    • Access Red Hat’s products and technologies without setup or configuration, and start developing quicker than ever before with our new, no-cost sandbox environments.
    • Explore Developer Sandbox

    Featured Developer Sandbox activities

    • Get started with your Developer Sandbox
    • OpenShift virtualization and application modernization using the Developer Sandbox
    • Explore all Developer Sandbox activities

    Ready to start developing apps?

    • Try at no cost
  • Blog
  • Events
  • Videos

ASP.NET Core Hello World Explained

 

October 20, 2017
Tom Deseyn
Related topics:
.NET
Related products:
Developer Tools

Share:

    Most books teaching C# start with a 'Hello World' application. This simple program is used to explain concepts like namespaces, classes, Main and Console.WriteLine. When every line of the code has been dissected, it's clear how it works.

    It's less obvious for an ASP.NET Core application. We are no longer invoking our code; instead, the ASP.NET Core framework is doing that for us. In this blog post, we'll look at a simple ASP.NET Core application and explain how ASP.NET Core makes it tick.

    This is the code:

    class Program {
        public static void Main(string[] args) =>
            BuildWebHost(args).Run();
    
        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup()
                .Build();
    }
    
    class Startup {
        private IConfiguration _configuration;
    
        public Startup(IConfiguration configuration) =>
            _configuration = configuration;
    
        public void ConfigureServices(IServiceCollection services) {
            services.AddMvc();
            services.AddDbContext(
                options => options.UseSqlServer(_configuration.GetConnectionString("Default")));
        }
    
        public void Configure(IApplicationBuilder app) {
            app.UseMvc();
            app.Run(context => context.Response.WriteAsync("Hello World!"));
        }
    }
    
    public class ToDoItem {
        public long Id { get; set; }
        public string Description { get; set; }
        public bool IsDone { get; set; }
    }
    
    public class ToDoDbContext : DbContext {
        public ToDoDbContext(DbContextOptions options) :
            base(options)
        {}
    
        public DbSet ToDoItems { get; set; }
    }
    
    [Route("/api/todo")]
    public class ToDoController {
        private readonly ToDoDbContext _dbContext;
    
        public ToDoController(ToDoDbContext context) =>
            _dbContext = context;
    
        [HttpGet]
        public IEnumerable GetAll() =>
            _dbContext.ToDoItems.ToList(); 
    }

    This application returns a list of ToDoItems from a SQL server database at the URL '/api/todo'. At other URLs, "Hello World!" is returned.

    You probably recognize the patterns you copied when doing your first ASP.NET Core tutorial.

    In Program.Main, we hand over control to ASP.NET Core. We can see how Main finds the Startup class via BuildWebHost. However, there doesn't seem to be a code-path leading to the ToDoController. ASP.NET Core finds this class by making use of reflection. Reflection is the ability to find information about the type in loaded assemblies at runtime.

    Often, attributes are used to identify certain types or methods. In this case, our ToDoController is found by a naming convention: public classes ending with "Controller" are considered MVC Controllers. Similar, Startup’s Configure and ConfigureServices are found by their name. The Route and HttpGet attributes are used to determine the URL and HTTP method handled by ToDoController.GetAll. This reflection is done by ASP.NET Core’s MVC middleware, which is the default approach for building web APIs and applications. We’ve enabled this middleware in our application via the AddMvc and UseMvc calls.

    The Configure and ConfigureServices methods serve two important, distinct goals. ConfigureServices is used to configure dependency injection (DI). And Configure is used to set up the request-handling pipeline. Let's go a bit deeper into what that means.

    Dependency injection is a pattern that enables loose coupling between high-level components and the bits they need to do their work. For example, our ToDoController needs a data source for the to-do items. Instead of making a connection to the database inside the controller, we take it as a constructor argument. As a result, this important dependency is no longer hidden in the implementation but surfaces to the public API. It also allows other data sources to be easily used with the same controller. This is especially useful for testing. Instead of using a real database, we can use an in-memory object store for example.

    Dependency injection in ASP.NET Core is based on the type system. In DI, a container is the name of the class responsible for providing dependencies. When asked to provide a dependency of a certain type, the container will look at the public constructors of that type. The container will try to find a constructor for which it knows how to create the argument types. For example, when we ask the container for a type of ToDoController, based on the public constructor, it will first try to figure out how to create a ToDoDbContext instance. This is where the Startup.ConfigureServices comes into play. In this method, we register additional types with the DI container. The IServiceCollection represents the types known to the container. By calling services.AddDbContext the container learns about the DbContextOptions and ToDoDbContext types needed to build the ToDoController.

    Types can be registered with 3 scopes: singleton, scoped or transient. Singleton means the same instance is used for the application lifetime. Scoped means the same instance is used per HTTP request and transient means a new instance is created each time. So, when two constructor argument types depend on the same type, they will get the same instance when scoped and a unique instance when transient. AddDbContext registers DbContextOptions and ToDoDbContext with scoped lifetime. When a type is registered, it’s possible to tell the container it should create instances of a more specialized type. For example, we can register requests for ISomeFeature should instantiate ActualFeature. This enables us to change the implementation used by our dependent classes via the DI container (e.g. use ActualFeatureV2) without having to changes those classes (they depend on ISomeFeature). IServiceCollection provides a simple interface for registering types. Most methods called in ConfigureServices (like AddDbContext) are extension methods that register a number of types. When we look at the name "ConfigureServices", "Configure" reflects that we are in the process of setup and configuration and "Services" refers to the types we are registering with DI.

    Notice the Startup class is using constructor injection to obtain the application configuration. The IConfiguration is added to the DI container as part of WebHost.CreateDefaultBuilder. The default builder will populate the configuration from appsettings.json, appsettings.{environment}.json and environment variables. We use the configuration to retrieve the SQL server connection string, which means it can be set via the environment variable "ConnectionStrings__Default" or as a json string property "Default" in a "ConnectionStrings" object in one of the appsettings json files. Note that the settings lookup is case-insensitive, "CONNECTIONSTRINGS__DEFAULT" works too.

    Now we'll have a look at the Startup.Configure method. This method configures how our application is handling requests. Request handling is set up as a sequence of handlers (pipeline). The order we are adding things in this method is important. If we'd reverse the app.Run and app.UseMvc in ConfigureServices, each request would be replied with "Hello World!" and the '/api/todo' handler becomes inaccessible. Each handler is in control of invoking the handlers behind it (next). That means that a handler can decide for itself if it wants to invoke its own logic first and then call next; or it can invoke next first, which is typically done to wrap its behavior (e.g. catching an exception and returning an error page). UseMvc tries to find an appropriate handler and if it can't, it delegates the request to next. app.Run ignores anyone behind it and always returns "Hello World!". Most methods called in Configure are extension methods that add a request handler (middleware) to the pipeline.

    That concludes our blog post. We’ve learned about the basic structure of an ASP.NET Core application and how reflection and naming conventions are used to tie things together. We’ve also learned about the role of the Startup class and its Configure and ConfigureServices methods. To learn more, check out the official ASP.NET Core documentation.


    For more information about .NET Core on Red Hat platforms, visit RedHatLoves.NET.

    Last updated: February 6, 2024

    Recent Posts

    • Staying ahead of artificial intelligence threats

    • Strengthen privacy and security with encrypted DNS in RHEL

    • How to enable Ansible Lightspeed intelligent assistant

    • Why some agentic AI developers are moving code from Python to Rust

    • Confidential VMs: The core of confidential containers

    Red Hat Developers logo LinkedIn YouTube Twitter Facebook

    Products

    • Red Hat Enterprise Linux
    • Red Hat OpenShift
    • Red Hat Ansible Automation Platform

    Build

    • Developer Sandbox
    • Developer Tools
    • Interactive Tutorials
    • API Catalog

    Quicklinks

    • Learning Resources
    • E-books
    • Cheat Sheets
    • Blog
    • Events
    • Newsletter

    Communicate

    • About us
    • Contact sales
    • Find a partner
    • Report a website issue
    • Site Status Dashboard
    • Report a security problem

    RED HAT DEVELOPER

    Build here. Go anywhere.

    We serve the builders. The problem solvers who create careers with code.

    Join us if you’re a developer, software engineer, web designer, front-end designer, UX designer, computer scientist, architect, tester, product manager, project manager or team lead.

    Sign me up

    Red Hat legal and privacy links

    • About Red Hat
    • Jobs
    • Events
    • Locations
    • Contact Red Hat
    • Red Hat Blog
    • Inclusion at Red Hat
    • Cool Stuff Store
    • Red Hat Summit
    © 2025 Red Hat

    Red Hat legal and privacy links

    • Privacy statement
    • Terms of use
    • All policies and guidelines
    • Digital accessibility

    Report a website issue