Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for How to Build a URL Shortener With .NET
Milan Jovanović
Milan Jovanović

Posted on • Originally published atmilanjovanovic.tech on

How to Build a URL Shortener With .NET

AURL shortener is a simple yet powerful tool that converts long URLs into more manageable, shorter versions. This is particularly useful for sharing links on platforms with character limits or improving user experience by reducing clutter. Two popular URL shorteners areBitly andTinyURL. Designing a URL shortener is an interesting challenge with fun problems to solve.

But how would you build a URL shortener in .NET?

URL shorteners have two core functionalities:

  • Generating a unique code for a given URL
  • Redirecting users who access the short link to the original URL

Today, I'll guide you through the design, implementation, and considerations for creating your URL shortener.

URL Shortener System Design

Here's the high-level system design for our URL shortener. We want to expose two endpoints. One to shorten a long URL and the other to redirect users based on a shortened URL. The shortened URLs are stored in aPostgreSQL database in this example. We can introduce a distributed cache likeRedis to the system to improve read performance.

Image description

We first need to ensure a large number of short URLs. We're going to assign a unique code to each long URL, and use it to generate the shortened URL. The unique code length and set of characters determine how many short URLs the system can generate. We will discuss this in more detail when we implement unique code generation.

We're going to use the random code generation strategy. It's straightforward to implement and has an acceptably low rate of collisions. The trade-off we're making is increased latency, but we will also explore other options.

The Data Model

Let's start by figuring out what we will store in the database. Our data model is straightforward. We have aShortenedUrl class representing the URLs stored in our system:

publicclassShortenedUrl{publicGuidId{get;set;}publicstringLongUrl{get;set;}=string.Empty;publicstringShortUrl{get;set;}=string.Empty;publicstringCode{get;set;}=string.Empty;publicDateTimeCreatedOnUtc{get;set;}}
Enter fullscreen modeExit fullscreen mode

This class includes properties for the original URL (LongUrl), the shortened URL (ShortUrl), and a unique code (Code) that represents the shortened URL. TheId andCreatedOnUtc fields are used for database and tracking purposes. The users will send the uniqueCode to our system, which will try to find a matchingLongUrl and redirect them.

In addition, we will also define an EFApplicationDbContext class, which is responsible for configuring our entity and setting up our database context. I'm doing two things here to improve performance:

  • Configuring theCode maximum length withHasMaxLength
  • Defining a unique index on theCode column

A unique index shields us from concurrency conflicts, so we will never have duplicateCode values persisted in the database. Setting the maximum length for this column saves storage space, and it's a requirement for indexing string columns in some databases.

Note that some databases treat strings in a case-insensitive way. This severely reduces the number of available short URLs. You want to configure the database to treat the unique code in a case-sensitive way.

publicclassApplicationDbContext:DbContext{publicApplicationDbContext(DbContextOptionsoptions):base(options){}publicDbSet<ShortenedUrl>ShortenedUrls{get;set;}protectedoverridevoidOnModelCreating(ModelBuildermodelBuilder){modelBuilder.Entity<ShortenedUrl>(builder=>{builder.Property(shortenedUrl=>shortenedUrl.Code).HasMaxLength(ShortLinkSettings.Length);builder.HasIndex(shortenedUrl=>shortenedUrl.Code).IsUnique();});}}
Enter fullscreen modeExit fullscreen mode

Unique Code Generation

The most crucial part of our URL shortener is generating a unique code for each URL. There are a few different algorithms you can choose to implement this. We want an even distribution of unique codes across all possible values. This helps to reduce potential collisions.

I will implement a random, unique code generator with a predefined alphabet. It's simple to implement, and the chance of collision is relatively low. Still, there are more performant solutions than this, but more on this later.

Let's define aShortLinkSettings class that contains two constants. One is for defining the length of the unqualified code we will generate. The other constant is the alphabet we will use to generate the random code.

publicstaticclassShortLinkSettings{publicconstintLength=7;publicconststringAlphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";}
Enter fullscreen modeExit fullscreen mode

The alphabet has62 characters, which gives us62^7 possible unique code combinations.

If you're wondering, this is3,521,614,606,208 combinations.

Spelled out: three trillion, five hundred twenty-one billion, six hundred fourteen million, six hundred six thousand, two hundred eight.

Those are quite a few unique codes, which will be enough for our URL shortener.

Now, let's implement ourUrlShorteningService, which handles generating unique codes. This service generates a random string of the specified length using our predefined alphabet. It checks against the database to ensure uniqueness.

publicclassUrlShorteningService(ApplicationDbContextdbContext){privatereadonlyRandom_random=new();publicasyncTask<string>GenerateUniqueCode(){varcodeChars=newchar[ShortLinkSettings.Length];constintmaxValue=ShortLinkSettings.Alphabet.Length;while(true){for(vari=0;i<ShortLinkSettings.Length;i++){varrandomIndex=_random.Next(maxValue);codeChars[i]=ShortLinkSettings.Alphabet[randomIndex];}varcode=newstring(codeChars);if(!awaitdbContext.ShortenedUrls.AnyAsync(s=>s.Code==code)){returncode;}}}}
Enter fullscreen modeExit fullscreen mode

Downsides and Improvement Points

The downside of this implementation is increased latency because we're checking each code we generate against the database. An improvement point could be generating the unique codes in the database ahead of time.

Another improvement point could be using a fixed number of iterations instead of an infinite loop. In case of multiple collisions in a row, the current implementation would continue until a unique value is found. Consider throwing an exception instead after a few collisions in a row.

URL Shortening

Now that our core business logic is ready, we can expose an endpoint to shorten URLs. We can use a simple Minimal API endpoint.

This endpoint accepts a URL, validates it, and then uses theUrlShorteningService to create a shortened URL, which is then saved to the database. We return the full shortened URL to the client.

publicrecordShortenUrlRequest(stringUrl);app.MapPost("shorten",async(ShortenUrlRequestrequest,UrlShorteningServiceurlShorteningService,ApplicationDbContextdbContext,HttpContexthttpContext)=>{if(!Uri.TryCreate(request.Url,UriKind.Absolute,out_)){returnResults.BadRequest("The specified URL is invalid.");}varcode=awaiturlShorteningService.GenerateUniqueCode();varrequest=httpContext.Request;varshortenedUrl=newShortenedUrl{Id=Guid.NewGuid(),LongUrl=request.Url,Code=code,ShortUrl=$"{request.Scheme}://{request.Host}/{code}",CreatedOnUtc=DateTime.UtcNow};dbContext.ShortenedUrls.Add(shortenedUrl);awaitdbContext.SaveChangesAsync();returnResults.Ok(shortenedUrl.ShortUrl);});
Enter fullscreen modeExit fullscreen mode

There is a minorrace condition here, as we generate a unique code first and then insert it into the database. A concurrent request could generate the same unique code and insert it into the database before we complete our transaction. However, the chances of this happening are low, so I decided not to handle that case.

Remember that the unique index in the database is still guarding us against duplicate values.

URL Redirection

The second use case for a URL shortener is redirection when accessing a shortened URL.

We will expose another Minimal API endpoint for this feature. The endpoint will accept a unique code, find the respective shortened URL, and redirect the user to the original long URL. You can implement additional validation for the specified code before checking if there's a shortened URL in the database.

app.MapGet("{code}",async(stringcode,ApplicationDbContextdbContext)=>{varshortenedUrl=awaitdbContext.ShortenedUrls.SingleOrDefaultAsync(s=>s.Code==code);if(shortenedUrlisnull){returnResults.NotFound();}returnResults.Redirect(shortenedUrl.LongUrl);});
Enter fullscreen modeExit fullscreen mode

This endpoint looks up the code in the database and, if found, redirects the user to the original long URL. The response will have a302 (Found) status code per the HTTP standards.

URL Shortener Improvement Points

While our basic URL shortener is functional, there are several areas we can improve:

  • Caching : Implement caching to reduce database load for frequently accessed URLs.
  • Horizontal Scaling : Design the system to scale horizontally to handle increased load.
  • Data Sharding : Implement data sharding to distribute data across multiple databases.
  • Analytics : Introduce analytics to track URL usage and expose reports to users.
  • User Accounts : Allow users to create accounts to manage their URLs.

We've covered the key components of building a URL shortener using .NET. You can take this further and implement the improvements points for a more robust solution.

If you want to see me build this from scratch, here's avideo tutorial on YouTube.

That's all for today.

See you next week.


P.S. Whenever you're ready, there are 2 ways I can help you:

  1. Pragmatic Clean Architecture: This comprehensive course will teach you the system I use to ship production-ready applications using Clean Architecture. Learn how to apply the best practices of modern software architecture.Join 2,200+ students here.

  2. Patreon Community: Think like a senior software engineer withaccess to the source code I use in my YouTube videos andexclusive discounts for my courses.Join 1,000+ engineers here.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

I'm a seasoned software architect and Microsoft MVP for Developer Technologies. I talk about all things .NET and post new YouTube videos every week.
  • Work
    Software Architect & Microsoft MVP
  • Joined

More fromMilan Jovanović

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp