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

  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. Leetcode: Most Stones Removed with Same Row or Column

    On a 2D plane, we place stones at some integer coordinate points. Each coordinate point may have at ...

  2. logrotate机制&原理

    logrotate机制&原理 centos系统中默认安装logrotate,logrotate主配置文件:/etc/logrotate.conf,其中定义了系统默认的logrotate规则,当 ...

  3. 读《中国人工智能与 IJCAI 的 40 周年,还有哪些未曾对外诉说的故事?》

    原文地址: https://mbd.baidu.com/newspage/data/landingsuper?context=%7B%22nid%22%3A%22news_10186783044528 ...

  4. Python3基础 str : 字符串的逆序

             Python : 3.7.3          OS : Ubuntu 18.04.2 LTS         IDE : pycharm-community-2019.1.3    ...

  5. ES6深入浅出-10 ES6新增的数据类型-1.Symbol与隐藏属性

    ES5现有的数据类型.7种数据类型. 新的类型是属于Object 最普通的类型.plain object 数组array 函数function 下面这些都属于Object类型. 今天要讲的 set类型 ...

  6. 转 【MySQL】常用拼接语句 shell 下执行mysql 命令

    [MySQL]常用拼接语句 前言:在MySQL中 CONCAT ()函数用于将多个字符串连接成一个字符串,利用此函数我们可以将原来一步无法得到的sql拼接出来,在工作中也许会方便很多,下面主要介绍下几 ...

  7. Spring Boot与MyBatis的集成

    SSM(Spring+Spring MVC+MyBatis)是当前主流的框架组合开发方式之一,普遍被应用于互联网项目中.如果要使用Spring Boot开发一个基于SSM框架的应用,那么我们要怎么做呢 ...

  8. web端自动化——Selenium3+python自动化(3.7版本)-火狐62版本环境搭建

    前言 目前selenium版本已经升级到3.0了,网上的大部分教程是基于2.0写的,所以在学习前先要弄清楚版本号,这点非常重要.本系列依然以selenium3为基础. 一.selenium简介 Sel ...

  9. .net core 使用SignalR实现实时通信

    这几天在研究SignalR,网上大部分的例子都是聊天室,我的需求是把服务端的信息发送给前端展示.并且需要实现单个用户推送. 用户登录我用的是ClaimsIdentity,这里就不多解释,如果不是很了解 ...

  10. AWS 架构最佳实践概述(十一)

    AWS 架构最佳实践 AWS合理架构的框架支柱 安全性 - 保护并监控系统 能够保护信息.系统和资产 通过风险评估和缓解策略 可靠性 - 从故障中恢复并减少中断 从基础设施或服务故障中恢复 动态获取计 ...