本文介绍:

利用Azure Functions 和Cosmos DB实现一个短网址平台。

利用Functions 和Cosmos DB的免费额度,自己用可以用很久了,撸一把羊毛。

 

介绍的知识点有:

Functions的 自定义路由;

Cosmos DB 表存储的用法;

Functions 绑定自定义域名及SSL;

 

结构如下图:

1.管理员向短网址平台注册新的短网址,例如 go.abc.com/c10001,其中c10001代表某个长网址,例如 https://www.51azure.cloud/post/2021/6/7/using-javascript-azure-functions-process-telemetry-message-from-iot-hub-and-get-device-id

 

2. 普通用户通过 go.abc.com/c10001访问,则实际会通过浏览器访问http get 触发的 Azure functions, functions 去后台cosmos db 查询c10001对应的长地址,然后发起浏览器跳转。

 

 

代码如下:

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Web;
using System.Net;
using System.Net.Http;
using Microsoft.Azure.Cosmos.Table;

namespace Company.Function
{
    public static class go
    {
        [FunctionName("go")]
        public static async  Task<HttpResponseMessage> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route ="{key?}")] HttpRequest req,
            string key,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            string Url = System.Environment.GetEnvironmentVariable("Default404Url");

            log.LogInformation($"Short-Url-Key:{key}");

            if (!string.IsNullOrEmpty(key) && key.Length > 5)
            {

                string storageConnectionString = System.Environment.GetEnvironmentVariable("CosmosDBString");

                // Retrieve storage  information from connection string.
                CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnectionString);

                // Create a table client for interacting with the table service
                CloudTableClient tableClient = storageAccount.CreateCloudTableClient(new TableClientConfiguration());

                Console.WriteLine("Create a Table for the demo");

                // Create a table client for interacting with the table service 
                CloudTable table = tableClient.GetTableReference("short-url-keys");


                try
                {
                    var partitionKey = key.Substring(0, 3);
                    TableOperation retrieveOperation = TableOperation.Retrieve<ShortUrlEntity>(partitionKey, key);
                    TableResult result = await table.ExecuteAsync(retrieveOperation);
                    ShortUrlEntity shortKeyEntity = result.Result as ShortUrlEntity;
                    if (shortKeyEntity != null)
                    {
                        log.LogInformation("\t{0}\t{1}\t{2}\t{3}", shortKeyEntity.PartitionKey, shortKeyEntity.RowKey, shortKeyEntity.Email, shortKeyEntity.Url);
                        Url = shortKeyEntity.Url;
                    }

                    else
                    {
                        Url = System.Environment.GetEnvironmentVariable("Default404Url");
                    }

                    //if (result.RequestCharge.HasValue)
                    //{
                    //   log.LogInformation("Request Charge of Retrieve Operation: " + result.RequestCharge);
                    //}


                }
                catch (StorageException e)
                {
                    Url = System.Environment.GetEnvironmentVariable("DefaultExceptionUrl");
                }

            }
            else
            {
                Url = System.Environment.GetEnvironmentVariable("Default404Url");
            }

            var res = new HttpResponseMessage();
            res.StatusCode = HttpStatusCode.Redirect;
            res.Headers.Add("Location", Url);
            return res;

        }

    }
}

 

host.json修改为如下:

{
    "version": "2.0",
    "logging": {
        "applicationInsights": {
            "samplingSettings": {
                "isEnabled": true,
                "excludedTypes": "Request"
            }
        }
    },
    
        "extensions": {
            "http": {
                "routePrefix": ""
            }
        }
    
}

 

在上述代码中,通过 Route=“{key?}” 的自定义路由,识别go.abc.com/c10001 中的 c10001,通过host.json中的 http extensions 配置,去掉默认的/api 路由。

参见:

https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook-trigger?WT.mc_id=AZ-MVP-5003757

默认情况下,所有函数路由的前缀均为 api。 还可以使用 host.json 文件中的 extensions.http.routePrefix 属性自定义或删除前缀。 以下示例通过将空字符串用于 host.json 文件中的前缀删除 api 路由前缀。

{
    "extensions": {
        "http": {
            "routePrefix": ""
        }
    }
}

 

cosmos db中使用Table API做了如下表结构:

 

关于PartitionKey 和RowKey的设计仅供参考:

本例子中,将通过URL传递进来的Key作为RowKey,前三位作为PartitionKey,在表API中,PartitionKey 和Row Key共同决定了某个唯一值。

 

 

 

视频演示: