Spring Cloud Gateway网关实现短网址生成、解析、转发
Spring Cloud Gateway网关实现短网址生成、解析、转发
- 1、概述
- 2、基础实现
- 3、路由处理HandlerFunction
- 4、配置路由
- 5、测试
1、概述
在一些生成二维码等场景中,需要使用短网址,本文讲解如何在Spring Cloud Gateway中实现短网址生成、解析、转发,其中使用到了Redis作为存储
2、基础实现
1、定义长网址转短网址的算法API(本算法逻辑来源于网络)
public class ShortUrl {private static final List<Character> BASE64_CHARS = Arrays.asList('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g','h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3','4', '5', '6', '7', '8', '9', '+', '_', '&', '=', '?');public static final int SHORT_URL_LEN = 6;private final byte[] hash;public ShortUrl() {this(random());}public ShortUrl(byte[] hash) {this.hash = Arrays.copyOf(hash, hash.length);}public ShortUrl(String hash) {this(hash(hash));}private static char byte2char(byte b) {return BASE64_CHARS.get(b);}private static byte char2byte(char charAt) {return (byte) BASE64_CHARS.indexOf(charAt);}private static byte[] hash(String hash) {final byte[] res = new byte[SHORT_URL_LEN];for (int i = 0; i < SHORT_URL_LEN; i++) {res[i] = char2byte(hash.charAt(i));}return res;}/*** Random short url.** @return*/private static byte[] random() {final Random rand = ThreadLocalRandom.current();return new byte[] {// 1st random char(byte) rand.nextInt(BASE64_CHARS.size()),// 2nd random char(byte) rand.nextInt(BASE64_CHARS.size()),// 3rd random char(byte) rand.nextInt(BASE64_CHARS.size()),// 4th random char(byte) rand.nextInt(BASE64_CHARS.size()),// 5th random char(byte) rand.nextInt(BASE64_CHARS.size()),// 6th random char(byte) rand.nextInt(BASE64_CHARS.size()) };}@Overridepublic boolean equals(Object o) {if (o == this) {return true;}if (o != null && o.getClass() == this.getClass()) {return Arrays.equals(((ShortUrl) o).hash, this.hash);}return false;}/** hashCode() implementation.** A ShortUrl instance needs 36bits, hashCode() only uses 32b.** (non-Javadoc)** @see java.lang.Object#hashCode()*/@Overridepublic int hashCode() {int hashCode = this.hash[0] & 3;hashCode = hashCode * 64 + this.hash[1];hashCode = hashCode * 64 + this.hash[2];hashCode = hashCode * 64 + this.hash[3];hashCode = hashCode * 64 + this.hash[4];hashCode = hashCode * 64 + this.hash[5];return hashCode;}/** Human readable form.** (non-Javadoc)** @see java.lang.Object#toString()*/@Overridepublic String toString() {final StringBuilder sb = new StringBuilder();int idx = 0;sb.append(byte2char(this.hash[idx++]));sb.append(byte2char(this.hash[idx++]));sb.append(byte2char(this.hash[idx++]));sb.append(byte2char(this.hash[idx++]));sb.append(byte2char(this.hash[idx++]));sb.append(byte2char(this.hash[idx]));return sb.toString();}
}
2、创建抽象类AbstractDwzService
和子类LocalDwzService
创建属性类:DwzProperties
@ConfigurationProperties(prefix = "dwz")
@RefreshScope
public class DwzProperties {/*** 短网址类型,支持:local*/private String type;/*** 短网址失效时间*/private Duration ttl = Duration.ofDays(365);/*** host*/private String host;
}
创建抽象类:AbstractDwzService
public abstract class AbstractDwzService {@Autowiredprotected StringRedisTemplate redisTemplate;protected static final String code2UrlKeyPrefix = "_gateway:short-code-url:";/*** 长网址转换成短码** @param longUrl* @return 短网址* 调用接口错误时,返回长网址*/abstract String getShortCode(String longUrl);/*** 根据短网址编码获取长网址** @param shortCode 短网址编码* @return 长网址*/protected String getLongUrl(String shortCode) {String codeUrlKey = code2UrlKeyPrefix + shortCode;return redisTemplate.opsForValue().get(codeUrlKey);}
}
创建实现类LocalDwzService
,通过@ConditionalOnProperty
很容易切换到一些互联网第三方的如百度短网址等
@ConditionalOnProperty(name = "dwz.type", havingValue = "local", matchIfMissing = true)
@Service
@RequiredArgsConstructor
public class LocalDwzService extends AbstractDwzService {private final DwzProperties properties;//第三个参数设为true时,在被使用时会把被使用的数据放到结尾。private final Map<String, String> lruMap = new LinkedHashMap<String, String>(1024,0.75F ,true){@Overrideprotected boolean removeEldestEntry(Map.Entry<String, String> var){// 当容量大于指定值时候删除头部元素return this.size() > 102400;}};@Overridepublic String getShortCode(String longUrl) {String md5Hex = DigestUtils.md5Hex(longUrl);return lruMap.computeIfAbsent(md5Hex, key -> {ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();String shortCode = new ShortUrl(md5Hex).toString();String codeUrlKey = code2UrlKeyPrefix + shortCode;valueOperations.set(codeUrlKey, longUrl, properties.getTtl());return shortCode;});}
}
3、路由处理HandlerFunction
DwzDecodeHandler
(短网址转长网址)
DwzEncodeHandler
(长网址转短网址)
DwzRedirectHandler
(短网址重定向长网址)
@Component
public class DwzDecodeHandler implements HandlerFunction<ServerResponse> {@Autowiredprivate AbstractDwzService dwzService;@Overridepublic @NotNull Mono<ServerResponse> handle(@NotNull ServerRequest request) {ServerHttpRequest httpRequest = request.exchange().getRequest();String code = httpRequest.getQueryParams().getFirst("code");try {String longUrl = dwzService.getLongUrl(code);return ServerResponse.status(HttpStatus.OK).header("Long-Url", longUrl).body(BodyInserters.fromValue(Collections.singletonMap("longUrl", longUrl)));} catch (Exception e) {return Mono.error(e);}}
}@Slf4j
@Component
public class DwzEncodeHandler implements HandlerFunction<ServerResponse> {@Autowiredprivate AbstractDwzService dwzService;@Autowiredprivate DwzProperties properties;@Overridepublic @NotNull Mono<ServerResponse> handle(@NotNull ServerRequest request) {ServerHttpRequest httpRequest = request.exchange().getRequest();String longUrl = httpRequest.getQueryParams().getFirst("url");try {String shortCode = dwzService.getShortCode(longUrl);String shortRedirectUrl = getHost(httpRequest) + "/=" + shortCode;Map<String, String> body = new HashMap<>(2);body.put("code", shortCode);body.put("redirectUrl", shortRedirectUrl);log.info("生成短网址成功,短码:{} ,短网址:{},原网址:{}", shortCode, shortRedirectUrl, longUrl);return ServerResponse.status(HttpStatus.OK).header(HttpHeaders.LOCATION, shortRedirectUrl).body(BodyInserters.fromValue(body));} catch (Exception e) {return Mono.error(e);}}private String getHost(ServerHttpRequest httpRequest) {String host = properties.getHost();if (StringUtils.isNotBlank(host)) {return host;}URI uri = httpRequest.getURI();return StrUtil.format("{}://{}:{}", uri.getScheme(), uri.getHost(), uri.getPort());}
}@Component
public class DwzRedirectHandler implements HandlerFunction<ServerResponse> {@Autowiredprivate AbstractDwzService dwzService;@Overridepublic @NotNull Mono<ServerResponse> handle(@NotNull ServerRequest request) {ServerWebExchange exchange = request.exchange();ServerHttpRequest httpRequest = exchange.getRequest();ServerHttpResponse response = exchange.getResponse();String path = httpRequest.getPath().value();String code = path.substring(path.lastIndexOf('/') + 2);String longUrl = dwzService.getLongUrl(code);if (StringUtils.isBlank(longUrl)) {return ServerResponse.status(HttpStatus.NOT_FOUND).build();}String url = httpRequest.getURI().toString();String paramDelimiter = "?";int paraIndex = url.lastIndexOf(paramDelimiter);if (paraIndex >= 0 && !url.endsWith(paramDelimiter)) {if (longUrl.contains(paramDelimiter)) {longUrl = longUrl + "&" + url.substring(paraIndex + 1);} else {longUrl = longUrl + paramDelimiter + url.substring(paraIndex + 1);}}response.getHeaders().set(HttpHeaders.LOCATION, longUrl);//303状态码表示由于请求对应的资源存在着另一个URI,应使用GET方法定向获取请求的资源return ServerResponse.status(HttpStatus.SEE_OTHER).header(HttpHeaders.LOCATION, longUrl).header(HttpHeaders.CONTENT_TYPE, "text/plain;charset=UTF-8").build();}
}
4、配置路由
@Configuration
public class RouterFunctionConfiguration {@Autowiredprivate DwzEncodeHandler dwzHandler;@Autowiredprivate DwzDecodeHandler decodeHandler;@Autowiredprivate DwzRedirectHandler redirectHandler;@Beanpublic RouterFunction routerFunction() {return RouterFunctions.route(RequestPredicates.GET("/url/short"), dwzHandler).andRoute(RequestPredicates.GET("/url/long"), decodeHandler).andRoute(RequestPredicates.GET("/=*"), redirectHandler);}
}
然后在application.yml
中增加配置即可
dwz:type: localttl: 365Dhost: http://localhost:8080
5、测试
长网址转短网址,GET /url/short?url={url}
,其中,如果url中携带参数需要encode,示例
以淘宝为例:生成淘宝的短网址并且携带参数,长网址:http://www.taobao.com?a=001,URL Encode长网址结果:http%3a%2f%2fwww.taobao.com%3fa%3d001
调用
POST http://localhost:8080/url/short?url=http%3a%2f%2fwww.taobao.com%3fa%3d001
结果
{"code": "89be85","redirectUrl": "http://localhost:8080/=89be85"
}
访问redirectUrl
,即可重定向到淘宝页面。还可以在短网址中携带额外参数,如:http://localhost:8080/=89be85?from=csdn
Spring Cloud Gateway网关实现短网址生成、解析、转发相关推荐
- Spring Cloud Gateway网关
Spring Cloud Gateway网关 1. 简介 Spring Cloud Gateway是Spring官网基于Spring 5.0. Spring Boot 2.0.Project Reac ...
- Spring Cloud Gateway (七)处理流程解析
Spring Cloud Gateway (七)处理流程解析 简介 初步梳理 Spring Cloud Gateway 的处理流程 过程记录 主要请求流程 在前面的分析中,我们知道在 ...
- Spring Cloud Gateway — 网关基本功能API暴露
API网关 API网关是一种设计模式,一种在微服务体系下的经典构件.要了解最新API网关模式可以参考敖小剑写的<Service Mesh和Api Gateway关系深度探讨> 早期SOA阶 ...
- spring cloud gateway网关和链路监控
文章目录 目录 文章目录 前言 一.网关 1.1 gateway介绍 1.2 如何使用gateway 1.3 网关优化 1.4自定义断言和过滤器 1.4.1 自定义断言 二.Sleuth--链路追踪 ...
- 从0开始构建你的api网关--Spring Cloud Gateway网关实战及原理解析
API 网关 API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题 ...
- spring cloud gateway 网关_微服务网关Spring Cloud Gateway全搞定
一.微服务网关Spring Cloud Gateway 1.1 导引 文中内容包含:微服务网关限流10万QPS.跨域.过滤器.令牌桶算法. 在构建微服务系统中,必不可少的技术就是网关了,从早期的Zuu ...
- 【硬核】Spring Cloud Gateway(网关)
概念 Gateway是基于异步非阻塞模型上进行开发的,有springcloud团队开发.用来代替Zuul. 近几个月收集了收集了N份精校过的PDF版的Java八股文大全,涉及Java后端的方方面面,分 ...
- Spring Cloud Gateway 网关整合 Knife4j
文章目录 1:环境准备 2:gateway服务设置 1:导包 2:yml配置 3:添加配置类,从网关服务中获取服务列表 4:重写并覆盖/swagger-resources接口 3:其他业务逻辑服务设置 ...
- Spring Cloud Gateway网关实战
文章目录 介绍 基础示例 spring-cloud-gateway-service spring-cloud-gateway-sample网关 介绍 Spring Cloud Gateway是Spri ...
最新文章
- 计算机专业的金书,《计算机专业英语》书评,金书网
- nacos集成dubbo实现远程服务调用
- 做公益的飞秋(FeiQ)程序代码
- 系统gpu 调试_KubeFlow上的GPU即服务:快速,可扩展且高效的ML
- iPhone 12 5G更耗电?续航时间较4G妥妥地缩短不少
- iStat Menus 6.51 mac中文版
- Android 7.1关机充电流程
- 非因解读 | 单细胞空间蛋白质组学分析揭示临床显著性前列腺癌多参数相关MRI检查的异质性
- ppt太大如何压缩到最小,这个方法你得知道
- php电子邮件群发源码,php电子邮件群发源码
- 计算上周一开始时间戳
- 腾讯云对象存储临时密钥qcloud-python-sts库安装失败解决办法
- Vanishing Point Detection 消影点/消失点/灭点检测代码学习整理笔记
- 瑞思拜 我儿豁 SpringMVC基础 兄弟们冲冲冲
- Java通过名字查询缘分,姓名缘分配对 从姓名笔画看两人姻缘
- ArcGIS 发布GP服务
- javaScript 美化上传文件框(加预览移除效果)
- [洛谷P2698] [USACO12MAR]花盆Flowerpot
- 阿蒙森 斯科特_斯科特的单元测试定律
- 新浪博客和微博已经成为黄色网站的…
热门文章
- Lasso regression(稀疏学习,R)
- Java CAS底层原理
- Word如何取消自动拼写检查、自动句首字母大写
- 爬虫训练场项目前端基础,Bootstrap5排版、表格、图像
- 常用搜索引擎查询命令收集:site,intitle,and,or,domain,mp3,inur
- 在 NVIDIA jetson tx2 上基于 realsense d435i 相机运行 vins 相关配置
- 简单粗暴PyTorch之优化器
- Keil环境下用STM32汇编语言工程分析HEX文件内容
- java 数组旋转_旋转数组或是数组的部分值
- c语言回文字符串用指针,C语言-判断回文字符串【函数+指针】(一)