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网关实现短网址生成、解析、转发相关推荐

  1. Spring Cloud Gateway网关

    Spring Cloud Gateway网关 1. 简介 Spring Cloud Gateway是Spring官网基于Spring 5.0. Spring Boot 2.0.Project Reac ...

  2. Spring Cloud Gateway (七)处理流程解析

    Spring Cloud Gateway (七)处理流程解析 简介     初步梳理 Spring Cloud Gateway 的处理流程 过程记录 主要请求流程     在前面的分析中,我们知道在 ...

  3. Spring Cloud Gateway — 网关基本功能API暴露

    API网关 API网关是一种设计模式,一种在微服务体系下的经典构件.要了解最新API网关模式可以参考敖小剑写的<Service Mesh和Api Gateway关系深度探讨> 早期SOA阶 ...

  4. spring cloud gateway网关和链路监控

    文章目录 目录 文章目录 前言 一.网关 1.1 gateway介绍 1.2 如何使用gateway 1.3 网关优化 1.4自定义断言和过滤器 1.4.1 自定义断言 二.Sleuth--链路追踪 ...

  5. 从0开始构建你的api网关--Spring Cloud Gateway网关实战及原理解析

    API 网关 API 网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题 ...

  6. spring cloud gateway 网关_微服务网关Spring Cloud Gateway全搞定

    一.微服务网关Spring Cloud Gateway 1.1 导引 文中内容包含:微服务网关限流10万QPS.跨域.过滤器.令牌桶算法. 在构建微服务系统中,必不可少的技术就是网关了,从早期的Zuu ...

  7. 【硬核】Spring Cloud Gateway(网关)

    概念 Gateway是基于异步非阻塞模型上进行开发的,有springcloud团队开发.用来代替Zuul. 近几个月收集了收集了N份精校过的PDF版的Java八股文大全,涉及Java后端的方方面面,分 ...

  8. Spring Cloud Gateway 网关整合 Knife4j

    文章目录 1:环境准备 2:gateway服务设置 1:导包 2:yml配置 3:添加配置类,从网关服务中获取服务列表 4:重写并覆盖/swagger-resources接口 3:其他业务逻辑服务设置 ...

  9. Spring Cloud Gateway网关实战

    文章目录 介绍 基础示例 spring-cloud-gateway-service spring-cloud-gateway-sample网关 介绍 Spring Cloud Gateway是Spri ...

最新文章

  1. 计算机专业的金书,《计算机专业英语》书评,金书网
  2. nacos集成dubbo实现远程服务调用
  3. 做公益的飞秋(FeiQ)程序代码
  4. 系统gpu 调试_KubeFlow上的GPU即服务:快速,可扩展且高效的ML
  5. iPhone 12 5G更耗电?续航时间较4G妥妥地缩短不少
  6. iStat Menus 6.51 mac中文版
  7. Android 7.1关机充电流程
  8. 非因解读 | 单细胞空间蛋白质组学分析揭示临床显著性前列腺癌多参数相关MRI检查的异质性
  9. ppt太大如何压缩到最小,这个方法你得知道
  10. php电子邮件群发源码,php电子邮件群发源码
  11. 计算上周一开始时间戳
  12. 腾讯云对象存储临时密钥qcloud-python-sts库安装失败解决办法
  13. Vanishing Point Detection 消影点/消失点/灭点检测代码学习整理笔记
  14. 瑞思拜 我儿豁 SpringMVC基础 兄弟们冲冲冲
  15. Java通过名字查询缘分,姓名缘分配对 从姓名笔画看两人姻缘
  16. ArcGIS 发布GP服务
  17. javaScript 美化上传文件框(加预览移除效果)
  18. [洛谷P2698] [USACO12MAR]花盆Flowerpot
  19. 阿蒙森 斯科特_斯科特的单元测试定律
  20. 新浪博客和微博已经成为黄色网站的…

热门文章

  1. Lasso regression(稀疏学习,R)
  2. Java CAS底层原理
  3. Word如何取消自动拼写检查、自动句首字母大写
  4. 爬虫训练场项目前端基础,Bootstrap5排版、表格、图像
  5. 常用搜索引擎查询命令收集:site,intitle,and,or,domain,mp3,inur
  6. 在 NVIDIA jetson tx2 上基于 realsense d435i 相机运行 vins 相关配置
  7. 简单粗暴PyTorch之优化器
  8. Keil环境下用STM32汇编语言工程分析HEX文件内容
  9. java 数组旋转_旋转数组或是数组的部分值
  10. c语言回文字符串用指针,C语言-判断回文字符串【函数+指针】(一)