DEV Community

Emin Vergil
Emin Vergil

Posted on • Edited on

How to create a URL Shortener in dotnet ?

Target Audience

The focus of this article is to provide insights to those seeking to acquire knowledge on designing a dotnet-based URL shortening service.

Learning Objectives

After completing this article, you will know how to do the following:

  • Desgin an url shortener
  • Create an API service for url shortener

What is URL Shortening ?

Wikipedia Definition

URL shortening is a technique on the World Wide Web in which a Uniform Resource Locator (URL) may be made substantially shorter and still direct to the required page. This is achieved by using a redirect which links to the web page that has a long URL.

What are the benefits of using an url shortener ?

  1. Saves characters: URL shorteners help save characters in tweets, texts, and other social media posts where character count is limited. This can make your message more concise and easier to share.
  2. Track clicks: URL shorteners often provide analytics that allow you to track clicks on your links. This can be useful for measuring the success of your marketing campaigns, and for determining which types of content are most engaging to your audience.
  3. Makes links more manageable: Long and complex URLs can be difficult to remember or type out. URL shorteners can make links more manageable and easier to share.
  4. Mask the original URL: Some URL shorteners allow you to mask the original URL, which can be useful for hiding affiliate links or links to sites that you don't want to reveal.
  5. Customize links: Many URL shorteners allow you to customize the shortened link to include a keyword or brand name. This can help increase brand awareness and make links more memorable.

Design of the service

Here is the general structure of the url shortener system.

System Design of Url Shortener

This design can be modified for the requirements. For example, if we want to handle high volume, we can use a load balancer to handle that traffic. And if we want to provide high availibility, we can use clustered databases.

Example implementation in dotnet

Here are the endpoints:

app.MapGet("{code}", (string code) => $"You are redirected to. Code: {code}");
app.MapGet("/get/{code}", (string code) => $"Here is the code: {code}");
app.MapGet("/get", () => $"Here is the list of urls. List: {GetAll()}");
app.MapPost("/create", ([FromBody] CreateShortUrlRequest request) =>
{
    var url = request.Url;
    return $"Here is your url: {url}";
});
app.MapDelete("/delete/{code}", (string code) => $"Url in {code} deleted successfully");
Enter fullscreen mode Exit fullscreen mode

In this demo, we will use create endpoint to create a code to represent urls.

Here is the whole implementation:

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCors();
builder.Services.AddSingleton(_ => new UrlShortenerService(new Database()));
var app = builder.Build();


app.UseCors(x =>
{
    x.AllowAnyHeader();
    x.AllowAnyOrigin();
    x.AllowAnyMethod();
});

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}



// here is the middleware to redirect

app.Use(async (context,next) =>
{
    var currentEndpoint = context.GetEndpoint();
    if (currentEndpoint is {DisplayName: "HTTP: GET {code}"} and RouteEndpoint routeEndpoint && routeEndpoint.RoutePattern.RawText == "{code}" && routeEndpoint.RoutePattern.Parameters.Count == 1)
    {
        var urlShortenerService = context.RequestServices.GetService<UrlShortenerService>();
        var reqUrl = context.Request.Path.Value[1..];
        var url = urlShortenerService.Get(reqUrl);
        if (string.IsNullOrWhiteSpace(url))
            await next();
        context.Response.Redirect(url);
    }

    await next();
});

app.MapGet("{code}", (UrlShortenerService service, string code) => service.RedirectTo(code));
app.MapGet("/get/{code}", (UrlShortenerService service,string code) => service.Get(code));
app.MapGet("/get", (UrlShortenerService service) => $"Here is the list of urls. List: {service.GetAll()}");
app.MapPost("/create", (UrlShortenerService service, [FromBody] CreateShortUrlRequest request) => service.Create(request.Url));
app.MapDelete("/delete/{code}", (UrlShortenerService service,string code) => service.Delete(code));

app.Run();

public class Database : Dictionary<string, string>
{
}

public class UrlShortenerService
{
    private readonly Database _database;

    public UrlShortenerService(Database database)
    {
        _database = database;
    }

    public string RedirectTo(string code)
    {
        if (!_database.ContainsKey(code))
        {
            return "Not Found";
        }
        var url = _database[code];
        return $"You are redirected to: {url}";
    }

    public string GetAll()
    {
        if (_database.Count < 0)
        {
            return string.Empty;
        }

        var list = JsonConvert.SerializeObject(_database.Keys);

        return list;
    }
    public string Get(string code)
    {
        if (_database.Count <= 0 || !_database.ContainsKey(code))
        {
            return string.Empty;
        }

        return _database[code];
    }

    public string Create(string url)
    {
        var rnd = Guid.NewGuid().ToString("n").Substring(0, 8);
        _database.Add(rnd, url);
        return $"Here is your new code for url: {rnd}";
    }

    public string Delete(string code)
    {
        _database.Remove(code);
        return $"Successfully removed. Code: {code}";
    }
}

public class CreateShortUrlRequest
{
    [JsonConstructor]
    public CreateShortUrlRequest(string url)
    {
        Url = url;
    }

    [JsonRequired]
    [JsonProperty("url")]
    public string Url { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

In this implementation, we created CRUD endpoints for a URL shortener and used a Dictionary to represent a key-value pair database. In the middleware, we check the current request method to ensure it matches the intended method, and if it does, we use the unique code to search the database for the corresponding long URL and redirect the user to it.

Overall, this implementation demonstrates how we can use basic CRUD operations and a key-value pair database to create a functional URL shortener. Additionally, it showcases how middleware can be used to handle the redirection of shortened URLs to their corresponding long URLs.

Here is a snapshot from network tab:

This is how redirection happens

Top comments (0)