How to build a URL Shortener with C# .NET and Redis
Time to read: 9 minutes
URLs are used to locate resources on the internet, but URLs can be long making them hard to use. Especially when you have to type the URL manually, even more so when having to do so on a mobile phone. Long URLs are also problematic when you have a limited amount of characters such as within an SMS segment or a Tweet.
A solution to this is to use a URL shortener which will create URLs that are short and sweet. When you open the shortened URL, the URL will forward you to the long destination URL. This makes it easier to manually type the URL and also save precious characters.
In this tutorial, you'll learn how to create your own URL shortener using C#, .NET, and Redis.
Prerequisites
Here’s what you will need to follow along:
- .NET 6 SDK (later versions will work too)
- A code editor or IDE. I recommend JetBrains Rider, Visual Studio, or VS Code with the C# plugin.
- A Redis database
You can find the source code for this tutorial on GitHub. Use it if you run into any issues, or submit an issue, if you run into problems.
Before creating your application, let's take a look at how URL shorteners work and what you'll be building.
URL Shortener Solution
There are two parts to URL shorteners: forwarding the shortened URLs to the destination URL and the management of the shortened URLs. While these two parts could live within a single application, you will build separate applications for URL management and for URL forwarding.
The two applications will work together as shown in the diagram above.
An administrator can use the CLI CRUD (Create Read Update Delete) application (1) to manage the shortened URL data in a Redis Database (2).
Let's say a mobile user received an SMS with a shortened URL. When the user clicks on the shortened URL, the web browser sends an HTTP GET request to that URL which is received by the Forwarder App (1). The forwarder app will look at the path of the request and look up the destination URL in the same Redis Database and return it to the Forwarder App (2). The Forwarder App then sends back an HTTP response with status code 307 Temporary Redirect pointing towards the destination URL (3).
You'll build the Forwarder App using an ASP.NET Core Minimal API, and you'll also build a command-line interface (CLI) application to provide the CRUD functionality. The CLI application will take advantage of the open-source System.CommandLine libraries. While these libraries are still in preview, they work well and provide common functionality needed to build CLI applications. While you'll build the CRUD functionality in a CLI app, you could build the same app as a website or API using ASP.NET Core, or client app using WPF, WinForms, Avalonia, MAUI, etc.
The CLI application will store the collection of shortened URLs in a Redis database. The key of each shortened URL will be the unique path that will be used to browse to the URL, and the value will be the destination URL.
I chose Redis for this application because it works well as a key-value database, it is well-supported, and there's a great .NET library to interact with Redis. However, you could use any data store you'd like in your own implementation.
Lastly, since these two applications will interact with the same data store and perform some of the same functionality, you'll create a class library for shared functionality for the CRUD operations and validation.
Create the Data Layer
First, open a terminal and run the following commands to create a folder, navigate into it, and create a solution file using the .NET CLI:
Then, create a new class library project for the data layer, and add the library to the solution:
Now open the solution with your preferred editor.
Rename the default C# file called Class1.cs to ShortUrl.cs, and add the following C# code to ShortUrl.cs:
This record will hold on to the Destination
and the Path
. Path
is the unique key that will be matched with the path from the incoming HTTP request in the Forwarder App, and Destination
will be the URL the Forwarder App will forward the user to.
Next, create the ShortUrlValidator.cs file which will have code to validate the ShortUrl
and its properties. Then, add the following C# code to the file:
ShortUrlValidator
has methods for validating the ShortUrl
record and for its individual properties. For each validation rule that is not met, a string is added to the validationResults
list. The validationResults
are then added together into a dictionary. These are:
- The
ShortUrl.Path
property can not be null or empty, not be longer than 10 characters, and has to match thePathRegex
which only allows alphanumeric characters, underscores, and dashes. - The
ShortUrl.Destination
property can not be null or empty, and has to be a well formed absolute URL, meaning a URL of format<scheme>://<hostname>:<port>
and optionally a path, query, and fragment.
These validation rules will be used in both the Forwarder App and the CLI CRUD App.
Now, the most important responsibility of this project is to manage the data in the Redis database. There's a great library for .NET by StackExchange, the StackOverflow company, for interacting with Redis databases called StackExchange.Redis. Add the StackExchange.Redis NuGet package to the data project using the .NET CLI:
Now, create a new file ShortUrlRepository.cs and add the following C# code:
The ShortUrlRepository
is responsible for interacting with the Redis database to manage the shortened URLs. ShortUrlRepository
accepts an instance of ConnectionMultiplexer
via its constructor which is used to get an IDatabase
instance via redisConnection.GetDatabase()
. ConnectionMultiplexer
takes care of connecting to the Redis database, while the IDatabase
class provides the Redis commands as .NET methods.
The ShortUrlRepository
has the following methods:
Create
to create aShortUrl
in the Redis database with the key being the path of the shortened URL, and the value being the destination URL.Update
to update an existingShortUrl
in the Redis database.Delete
to delete aShortUrl
by deleting the key in the Redis database.Get
to get theShortUrl
via the path.GetAll
to retrieve all the shortened URLs by retrieving all keys from all the connected Redis servers and then retrieve the values from the Redis database.
This provides all the CRUD functionality for shortened URLs.
Now let's use these classes to create the CRUD CLI App.
Build the Command-Line CRUD Application
Create a new console project in your solution folder, and add the project to the solution:
Then, add a reference from the CLI project to the data project:
Now you can use the public classes from the data project in your CLI project.
Next, add the StackExchange.Redis NuGet package to this project as well:
Now, add the System.CommandLine NuGet package:
The System.CommandLine
libraries provide common functionality for building CLI applications. You can define your arguments and commands in C# and the library will execute your commands, and generate help text, command completion, and more.
Open the UrlShortener.Cli/Program.cs file, and replace the code with the following C#:
This code defines the options that you can pass into the CLI application:
destinationOption
can be passed in using-d
or--destination-url
and is required.pathOption
can be passed in using-p
or--path
and is also required.connectionStringOption
can be passed in using-c
or--connection-string
. Alternatively, you can set theURL_SHORTENER_CONNECTION_STRING
environment variable which is preferred over passing sensitive strings as an argument. If theURL_SHORTENER_CONNECTION_STRING
environment is not present, then theconnectionStringOption
is required.
The destinationOption
and pathOption
validate the argument by passing in the argument value to their respective ShortUrlValidator
methods. The validation results are then concatenated and set to result.ErrorMessage
which will be displayed as errors to the user.
Now that the options are defined, you can create commands that receive the options.
Add the following code after your existing code:
The rootCommand
is the command that is invoked when you invoke the CLI application without passing any subcommands. The create
command takes in the destinationOption
, pathOption
, and connectionStringOption
. The lambda passed into the SetHandler
method receives the values for the options and will be executed when the command is run from the CLI.
Since the connectionStringOption
may not be required, the connectionString
parameter may be null. The GetRedisConnection
method checks if it is null, and if so, returns the value from the environment variables. If that is also null, it'll throw an exception.
The command handler will create a new ShortUrlRepository
passing in the connection string, and use the Create
method to create a new shortened URL.
Now, add the following code which will add the update
, delete
, get
, and list
commands:
Note how some commands take less options than others.
Lastly, add the following line of code:
This line is responsible for running the commands and passing in the args
string array.
Go back to your terminal and configure the URL_SHORTENER_CONNECTION_STRING
environment variable:
If you use PowerShell:
If you use CMD:
If you use Unix based shells such as Bash or Zsh:
Now you can run the project and will see helpful information about the available commands:
The output looks like this:
To get help information about specific commands, specify the command and add the --help
argument:
The output looks like this:
Next, try the following commands:
Alternatively, you could publish the project and interact directly with the executable:
Great job! You built the CRUD as a CLI application, now let's build the Forwarder Application.
Build the ASP.NET Core Forwarder Application
In your terminal, head back to the solution folder and then create a new ASP.NET Core Minimal API project:
Just like with the CRUD CLI project, add a reference in the Forwarder project to the data project, and add the StackExchange.Redis NuGet package:
Now, open UrlShortener.Forwarder/Program.cs and replace the contents with the following code:
Let's look at lines 6 to 10 first. The program will retrieve the UrlsDb
Redis connection string from the configuration, and if null, throw an exception. Then, a connection to the Redis server is made which is added to the Dependency Injection (DI) container as a singleton.
Next, the ShortUrlRepository
is added as a transient service to the DI container. Since ShortUrlRepository
accepts a ConnectionMultiplexer
object via its constructor, the DI container is able to construct the ShortUrlRepository
for you passing in the singleton ConnectionMultiplexer
.
Next, let's look at lines 14 to 27. A new HTTP GET endpoint is added with the route /{path}
. This endpoint will be invoked by any HTTP request with a single level path. Without a path, ASP.NET Core will return an HTTP status code of 404 Not Found, and so will requests with subdirectory paths.
When the endpoint is invoked, the path of the URL is passed into the path
parameter of the lambda. The second lambda parameter, an instance of ShortUrlRepository
, is injected by the DI container.
The path is then validated using ShortUrlValidator.ValidatePath
. If the path is not valid, an HTTP status code of 400 Bad Request is returned.
If it is valid, the shortened URL is retrieved using the ShortUrlRepository.Get
method. If no shortened URL is found, shortUrl
will be null. If the shortUrl
is null, or when it is not null but the ShortUrl.Destination
is null or empty, then an HTTP status code 404 Not Found is returned. Otherwise, the endpoint responds with HTTP status code 307 Temporary Redirect
, redirecting to the ShortUrl.Destination
.
Now that your code is ready, you'll need to configure the ShortenedUrlsDb
connection string. Run the following commands to enable user-secrets, and then configure your connection string as a user-secret:
Finally, run the Forwarder application:
The output of this command will show you the URLs of your application. Open one of the URLs and try some of the shortened URLs you created earlier, like /rr, /sb, /tw, and /sg.
Next steps
You've developed a CLI application to manage shortened URLs and a web application that forwards the shortened URLs to the destination URLs. However, to make the URL as short as possible, you need to also buy a domain that is short and host your URL shortener at that domain. For example, Twilio uses twil.io as a short domain, which is much shorter than www.twilio.com. This is also a good example of how the name of your brand can still be represented in your short domain name.
There are some other ways you could improve the solution:
- You could make the
path
optional when creating and updating a shortened URL, and instead randomly generate the path. - You could store a time to live (TTL) for every shortened URL and remove the shortened URL when the TTL expires.
- You could track how many times a shortened URL is used for analytics purposes. You could store this data in the Redis database, or track it as an event in Segment.
- You could add an API that is consumable from other applications. For example, an SMS application could dynamically generate shortened URLs via the API for the long URLs they are trying to send to users.
- You could create a Graphical User Interface (GUI) to manage the shortened URL data instead of a CLI application.
Want to keep learning? Learn how to respond to SMS and Voice calls using ASP.NET Core Minimal APIs.
Niels Swimberghe is a Belgian American software engineer and technical content creator at Twilio. Get in touch with Niels on Twitter @RealSwimburger and follow Niels’ personal blog on .NET, Azure, and web development at swimburger.net.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.