Java中的42行代码中的URL缩短服务— Java(?!)Spring Boot + Redis

显然,编写URL缩短服务是新的“ Hello,world! ”在IoT /微服务/时代的世界中。 一切始于在45行Scala中的URL缩短服务 -整洁的Scala,以Spray和Redis进行调味以进行存储。 随后, 在35行Clojure中使用了url缩短服务,在 Haskell的43行中使用了URL缩短器 。 所以我内心的反时髦人士问:用Java语言要花多长时间? 但是,出于善意,不是普通的Java。 带有Spring Data Redis的 Spring Boot是一个很好的起点。 我们需要的只是一个处理GET和POST的简单控制器:

import com.google.common.hash.Hashing;
import org.apache.commons.validator.routines.UrlValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
 
import javax.servlet.http.*;
import java.nio.charset.StandardCharsets;
 
@org.springframework.boot.autoconfigure.EnableAutoConfiguration
@org.springframework.stereotype.Controller
public class UrlShortener {
    public static void main(String[] args) {
        SpringApplication.run(UrlShortener.class, args);
    }
 
    @Autowired private StringRedisTemplate redis;
 
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public void redirect(@PathVariable String id, HttpServletResponse resp) throws Exception {
        final String url = redis.opsForValue().get(id);
        if (url != null)
            resp.sendRedirect(url);
        else
            resp.sendError(HttpServletResponse.SC_NOT_FOUND);
    }
 
    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<String> save(HttpServletRequest req) {
        final String queryParams = (req.getQueryString() != null) ? "?" + req.getQueryString() : "";
        final String url = (req.getRequestURI() + queryParams).substring(1);
        final UrlValidator urlValidator = new UrlValidator(new String[]{"http", "https"});
        if (urlValidator.isValid(url)) {
            final String id = Hashing.murmur3_32().hashString(url, StandardCharsets.UTF_8).toString();
            redis.opsForValue().set(id, url);
            return new ResponseEntity<>("http://mydomain.com/" + id, HttpStatus.OK);
        } else
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }
}

该代码很好地自我描述,并且在功能上等同于Scala中的版本。 我没有试图太挤它以使行数尽可能的短,上面的代码很典型,只包含很少的细节:

  • 我通常不使用通配符导入
  • 我不使用完全限定的类名(我想保存一条import行,我承认)
  • 我用if括号包围if else用括号括起来
  • 我几乎从不使用场注入,这是控制家族反转中最丑陋的兄弟。 相反,我会去让构造函数允许使用模拟的Redis进行测试:
@Autowired
private final StringRedisTemplate redis;
 
public UrlShortener(StringRedisTemplate redis) {
    this.redis = redis;
}

我最苦恼的事情是……获取原始的完整URL。 基本上,我需要.com或port之后的所有内容。 没有流血的方式(既没有servlet,也没有Spring MVC),因此笨拙的getQueryString()摆弄着。 您可以按以下方式使用该服务-创建较短的URL:

$ curl -vX POST localhost:8080/https://www.google.pl/search?q=tomasz+nurkiewicz
 
> POST /https://www.google.pl/search?q=tomasz+nurkiewicz HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: text/plain;charset=ISO-8859-1
< Content-Length: 28
< Date: Sat, 23 Aug 2014 20:47:40 GMT
<
http://mydomain.com/50784f51

通过较短的网址重定向:

$ curl -v localhost:8080/50784f51
 
> GET /50784f51 HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 302 Found
< Server: Apache-Coyote/1.1
< Location: https://www.google.pl/search?q=tomasz+nurkiewicz
< Content-Length: 0
< Date: Sat, 23 Aug 2014 20:48:00 GMT
<

为了完整起见,这是Gradle中的一个构建文件(maven也可以使用),在所有先前的解决方案中都跳过了:

buildscript {
    repositories {
        mavenLocal()
        maven { url "http://repo.spring.io/libs-snapshot" }
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.1.5.RELEASE'
    }
}
 
apply plugin: 'java'
apply plugin: 'spring-boot'
 
sourceCompatibility = '1.8'
 
repositories {
    mavenLocal()
    maven { url 'http://repository.codehaus.org' }
    maven { url 'http://repo.spring.io/milestone' }
    mavenCentral()
}
 
dependencies {
    compile "org.springframework.boot:spring-boot-starter-web:1.1.5.RELEASE"
    compile "org.springframework.boot:spring-boot-starter-redis:1.1.5.RELEASE"
    compile 'com.google.guava:guava:17.0'
    compile 'org.apache.commons:commons-lang3:3.3.2'
    compile 'commons-validator:commons-validator:1.4.0'
    compile 'org.apache.tomcat.embed:tomcat-embed-el:8.0.9'
    compile "org.aspectj:aspectjrt:1.8.1"
 
    runtime "cglib:cglib-nodep:3.1"
}
 
tasks.withType(GroovyCompile) {
    groovyOptions.optimizationOptions.indy = true
}
 
task wrapper(type: Wrapper) {
    gradleVersion = '2.0'
}

实际上也是42行...这就是整个应用程序,没有XML,没有描述符,没有安装。

对于最短,最模糊的工作代码,我不认为此练习只是一个虚拟的代码。 带有Redis后端的URL缩短器Web服务是给定语言和生态系统的语法和功能的有趣展示。 有趣的是,还有很多算法问题,例如在Rosetta代码中发现的。 这也是编写REST服务的一个很好的基本模板。

原始Scala实现的一个重要功能(包括该实现)在所有实现中都以某种方式被默默地忘记了,它是非阻塞的。 HTTP和Redis的访问是事件驱动的( 反应 ,没事,我说),所以我想它可以同时处理客户数以万计。 阻止由Tomcat支持的控制器无法实现这一点。 但是,您仍然必须承认,用Java(甚至不是Java 8!)编写的这种服务简明扼要,易于遵循和简单明了-其他解决方案都不是可读的(当然这是主观的)。

等待别人!

翻译自: https://www.javacodegeeks.com/2014/08/url-shortener-service-in-42-lines-of-code-in-java-spring-boot-redis.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值