首先说一下这种业务的应用场景:

  1. 把一个长 url 转换为一个短 url 网址
  2. 主要用于微博,二维码,等有字数限制的场景

主要实现的功能分析:

  1. 把长 url 地址转换为短 url 地址
  2. 通过短 url 获取对应的原始长 url 地址
  3. 相同长 url 地址是否需要同样的短 url 地址

这是实现的是一个 api 服务:

数据库设计

数据库只有一张表,5个字段,如图所示:

建表语句

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0; -- ----------------------------
-- Table structure for short_url
-- ----------------------------
DROP TABLE IF EXISTS `short_url`;
CREATE TABLE `short_url` (
`id` bigint NOT NULL AUTO_INCREMENT,
`short_url` varchar(255) DEFAULT NULL,
`origin_url` varchar(255) DEFAULT NULL,
`hash_code` varchar(255) DEFAULT NULL,
`create_time` timestamp DEFAULT now(),
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1000000 ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;

这里有个设置需要注意,就是关于数据库表中 id 的设计,需要设置为自增的

并且这里有个问题需要提前知道,我们的思路是根据 id 的值会转换为 62 进制,关于进制转换的代码

所以这里需要设置一下数据库 id 的起始值,可以设置的大一点,这样转换为 62 进制之后不至于太短

// 将十进制转换为62进制   0-9a-zA-Z 六十二进制
func transTo62(id int64)string{
// 1 -- > 1
// 10-- > a
// 61-- > Z
charset := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
var shortUrl []byte
for{
var result byte
number := id % 62
result = charset[number]
var tmp []byte
tmp = append(tmp,result)
shortUrl = append(tmp,shortUrl...)
id = id / 62
if id == 0{
break
}
}
fmt.Println(string(shortUrl))
return string(shortUrl)
}

  

代码逻辑

代码的目录结构

|____logic
| |____logic.go
|____model
| |____data.go
|____api
| |____api.go
|____client
| |____client.go

logic 目录下是主要的处理逻辑

model 目录下是定义了 request 和 response 结构体

api 目录下是程序的入口程序

client 为测试请求,进行地址的转换

model 代码

package model

type Long2ShortRequest struct {
OriginUrl string `json:"origin_url"`
} type Short2LongRequest struct {
ShortUrl string `json:"short_url"`
} type ResponseHeader struct {
Code int `json:"code"`
Message string `json:"message"`
} type Long2ShortResponse struct {
ResponseHeader
ShortUrl string `json:"short_url"`
} type Short2LongResponse struct {
ResponseHeader
OriginUrl string `json:"origin_url"`
}

  

logic 代码

package logic

import(
"short_url/model"
"github.com/jmoiron/sqlx"
"fmt"
"crypto/md5"
"database/sql"
) var (
Db *sqlx.DB
) type ShortUrl struct {
Id int64 `db:"id"`
ShortUrl string `db:"short_url"`
OriginUrl string `db:"origin_url"`
HashCode string `db:"hash_code"`
} func InitDb(dsn string)(err error) {
// 数据库初始化
Db, err = sqlx.Open("mysql",dsn)
if err != nil{
fmt.Println("connect to mysql failed:",err)
return
}
return
} func Long2Short(req *model.Long2ShortRequest) (response *model.Long2ShortResponse, err error) {
response = &model.Long2ShortResponse{}
urlMd5 := fmt.Sprintf("%x",md5.Sum([]byte(req.OriginUrl)))
var short ShortUrl
err = Db.Get(&short,"select id,short_url,origin_url,hash_code from short_url where hash_code=?",urlMd5)
if err == sql.ErrNoRows{
err = nil
// 数据库中没有记录,重新生成一个新的短url
shortUrl,errRet := generateShortUrl(req,urlMd5)
if errRet != nil{
err = errRet
return
}
response.ShortUrl = shortUrl
} else {
if err != nil{
return
}
response.ShortUrl = short.ShortUrl
}
response.Code = 0
response.Message = "success"
return
} func generateShortUrl(req *model.Long2ShortRequest,hashcode string)(shortUrl string,err error){
result,err := Db.Exec("insert INTO short_url(origin_url,hash_code)VALUES (?,?)", req.OriginUrl, hashcode)
if err != nil{
return
}
// 0-9a-zA-Z 六十二进制
insertId,_:= result.LastInsertId()
shortUrl = transTo62(insertId)
_,err = Db.Exec("update short_url set short_url=? where id=?",shortUrl,insertId)
if err != nil{
fmt.Println(err)
return
}
return
} // 将十进制转换为62进制 0-9a-zA-Z 六十二进制
func transTo62(id int64)string{
// 1 -- > 1
// 10-- > a
// 61-- > Z
charset := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
var shortUrl []byte
for{
var result byte
number := id % 62
result = charset[number]
var tmp []byte
tmp = append(tmp,result)
shortUrl = append(tmp,shortUrl...)
id = id / 62
if id == 0{
break
}
}
fmt.Println(string(shortUrl))
return string(shortUrl)
} func Short2Long(req *model.Short2LongRequest) (response *model.Short2LongResponse, err error) {
response = &model.Short2LongResponse{}
var short ShortUrl
err = Db.Get(&short,"select id,short_url,origin_url,hash_code from short_url where short_url=?",req.ShortUrl)
if err == sql.ErrNoRows{
response.Code = 404
return
}
if err != nil{
response.Code = 500
return
}
response.OriginUrl = short.OriginUrl
response.Code = 0
response.Message = "success"
return
}

api 代码

package main
import (
"io/ioutil"
"net/http"
"fmt"
"encoding/json"
"short_url/logic"
"short_url/model"
_ "github.com/go-sql-driver/mysql"
) const (
//ErrSuccess = 0
ErrInvalidParameter = 1001
ErrServerBusy = 1002
) func getMessage(code int) (msg string){
switch code {
//case ErrSuccess:
// msg = "success"
case ErrInvalidParameter:
msg = "invalid parameter"
case ErrServerBusy:
msg = "server busy"
default:
msg = "unknown error"
} return
} // 用于将返回序列化数据,失败的返回
func responseError(w http.ResponseWriter, code int) {
var response model.ResponseHeader
response.Code = code
response.Message = getMessage(code) data, err := json.Marshal(response)
if err != nil {
w.Write([]byte("{\"code\":500, \"message\": \"server busy\"}"))
return
} w.Write(data)
} // 用于将返回序列化数据,成功的返回
func responseSuccess(w http.ResponseWriter, data interface{}) {
dataByte, err := json.Marshal(data)
if err != nil {
w.Write([]byte("{\"code\":500, \"message\": \"server busy\"}"))
return
} w.Write(dataByte)
} // 长地址到短地址
func Long2Short(w http.ResponseWriter, r *http.Request) {
// 这里需要说明的是发来的数据是通过post发过来一个json格式的数据
data, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println("read all failded, ", err)
responseError(w, 1001)
return
} var req model.Long2ShortRequest
// 将反序列化的数据保存在结构体中
err = json.Unmarshal(data, &req)
if err != nil {
fmt.Println("Unmarshal failded, ", err)
responseError(w, 1002)
return
} resp, err := logic.Long2Short(&req)
if err != nil {
fmt.Println("Long2Short failded, ", err)
responseError(w, 1003)
return
} responseSuccess(w, resp)
} // 短地址到长地址
func Short2Long(w http.ResponseWriter, r *http.Request) {
// 这里需要说明的是发来的数据是通过post发过来一个json格式的数据
data, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Println("read all failded, ", err)
responseError(w, 1001)
return
} var req model.Short2LongRequest
// 将反序列化的数据保存在结构体中
err = json.Unmarshal(data, &req)
if err != nil {
fmt.Println("Unmarshal failded, ", err)
responseError(w, 1002)
return
} resp, err := logic.Short2Long(&req)
if err != nil {
fmt.Println("Long2Short failded, ", err)
responseError(w, 1003)
return
}
responseSuccess(w, resp)
} func main(){
err := logic.InitDb("chenkai:chenkai@tcp(192.168.0.115:3306)/golang_db?parseTime=true&loc=Local")
if err != nil{
fmt.Printf("init db failed,err:%v\n",err)
return
}
http.HandleFunc("/trans/long2short", Long2Short)
http.HandleFunc("/trans/short2long", Short2Long)
http.ListenAndServe(":18888", nil)
}

使用 postman 测试

参考链接:https://www.cnblogs.com/zhaof/p/8576946.html

ending ~

Go 实现短 url 项目的更多相关文章

  1. Go实现短url项目

    首先说一下这种业务的应用场景: 把一个长url转换为一个短url网址 主要用于微博,二维码,等有字数限制的场景 主要实现的功能分析: 把长url的地址转换为短url地址 通过短url获取对应的原始长u ...

  2. 通过Beego将之前实现的短url项目实现

    正好通过这个小例子对之前了解的beego框架的基本内容进行一个简单的应用 实现的完整代码地址:https://github.com/pythonsite/go_simple_code/tree/mas ...

  3. mongodb:短网址项目

    短网址项目概述 1.短网址项目,是将给定的长网址,转换成短网址. 如 新浪 http://t.cn/zQd5NPw ,其中zQd5NPw就是短网址 前段页面如下

  4. SharePoint 2010 Url Shortener --SharePoint 2010 短URL生成器

    SharePoint 2010 Url Shortener --SharePoint 2010 短URL生成器 项目描写叙述 本项目加入了这种功能.在SP站点中能够生成短URLs. 这些URLs指向列 ...

  5. 短URL

    短网址应用已经在全国各大微博上开始流行了起来.例如QQ微博的url.cn,新郎的sinaurl.cn等. 我们在QQ微博上发布网址的时候,微博会自动判别网址,并将其转换,例如:http://url.c ...

  6. 二维码及二维码接合短URL的应用

    二维码 1.什么是二维码? 二维条形码,最早发明于日本,它是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的,在代码编制上巧妙地利用构成计算机内部逻辑基础的“0 ...

  7. .NetCore3.0短网址项目

    Wei.TinyUrl 基于.NetCore3.0 + Mysql开发的短网址项目 项目地址:https://github.com/a34546/Wei.TinyUrl 演示效果: 快速开始 1. 修 ...

  8. 短URL生成

    算法原理 算法一 1)将长网址md5生成32位签名串,分为4段, 每段8个字节; 2)对这四段循环处理, 取8个字节, 将他看成16进制串与0x3fffffff(30位1)与操作, 即超过30位的忽略 ...

  9. django简单实现短url

    一.短url的原理 什么是短url: 简单讲就是把普通正常访问的网址,转换成比较短的网址,例如:https://www.cnblogs.com/angelyan/articles/10667354.h ...

随机推荐

  1. iptables规则保存

    /etc/init.d/iptables save #查看 vim /etc/sysconfig/iptables #将iptables设置为开机启动 chkconfig iptables on #查 ...

  2. 生成model笔记

    https://github.com/yscacaca/DeepSense/tree/master/android_test这个才是真正的部署代码,跑这个代码就好. 跑python sample_mo ...

  3. Flutter Offstage、Visibility隐藏/可见

    Offstage是控制组件隐藏/可见的组件,如果感觉有些单调功能不全,我们可以使用Visibility,Visibility也是控制子组件隐藏/可见的组件.不同是的Visibility有隐藏状态是否留 ...

  4. 软件定义网络基础---SDN的核心思想

    一:SDN包含的核心思想:解耦,抽象,可编程 二:解耦 (一)SDN网络解耦思想 解耦是指将控制平面和数据平面进行分离,主要为了解决传统网络中控制平面和数据平面在物理上紧耦合导致的问题 控制平面和数据 ...

  5. Linux记录-shell自动化批量部署sql脚本并记录日志信息(转载)

    #!/bin/bash #script_version=v110 db_host=127.0.0.1 db_port=3306 db_username=db_test_inst db_passwd=` ...

  6. Oracle索引 详解

    作者:Dave 一.索引介绍 1.1 索引的创建语法: CREATE UNIUQE | BITMAP INDEX <schema>.<index_name> ON <sc ...

  7. git 更新fork的远程仓库

    1.添加远程仓库到本地remote分支 git remote add upstream git@github.com:apache/flink.git # 远程仓库地址 2.查看当前仓库的远程分支 g ...

  8. 线性回归:鸢尾花数据iris

    # encoding: utf-8 from sklearn.linear_model import LogisticRegression import numpy as np from sklear ...

  9. SPSS 习题-2

    1.有关SPSS数据字典的说法,正确的是:  AA. SPSS 数据集的数据字典可以复制到其他数据集中B. SPSS数据集的数据字典是不能复制的C. SPSS的数据字典可以通过“复制”和“粘贴”在不同 ...

  10. IdentityServer4 学习二

    进入identityserver4的官网:https://identityserver.io/ 找到文档 从overview下开始按照官方文档练习: 安装自定义模板 dotnet new -i Ide ...