相关文章:
java之net.http.HttpClient详解
Java Uri和Url及Uri解析过程
什么是短链长链:
长链接:
长链是完整的URI,包含协议、域名、路径和查询参数等信息。例如,https://www.example.com/page?param1=value1¶m2=value2
就是一个长链。
短链接:
短链是对长链进行简化的一种形式,通常由一个较短的字符串表示。短链可以通过将长链映射到一个短字符串来实现,这个映射关系存储在数据库或者其他数据结构中。用户在访问短链时,系统会将其转换为对应的长链并进行跳转。短链的好处是更易于记忆和分享。
短链场景:
- 微博评论→ 存在字符数限制,使用短链节省空间
- 短链生成二维码更加简单,提高设备识别率
原理
- 请求短链:向服务器发送一个HTTP请求,其中包含短链的URL。
- 服务器根据短链接key获取对应长链接
- 通过HTTP 301/302请求转发到对应长链接
难点:
- 异常场景:
- url如何解析,传入的一定是合法?不合法又是怎么解析,怎么判断是域名导致,是什么导致
- url过期是怎么做的,不能直接报错
- 短链服务问题,无法解析,dns刷不到,找不到域名,(并不局限于此,正式场景一次请求即可判断)
为什么要使用GET?
"GET"是HTTP协议中的一种请求方式,主要用于获取资源。在这个场景中,你正在请求一个URL,所以"GET"是合适的请求方法。 不显示设置请求方法,HttpURLConnection默认的请求方法也为”GET”;
为什么重定向能获取到长链
如果在一个HTTP请求的响应中,服务器返回了一个重定向的HTTP状态码(例如301, 302, 303等),并且提供了一个新的URL,那么**HttpClient.Redirect.NORMAL
**策略会让HttpClient自动发起一个新的请求到这个新的URL,从而获取新URL的响应。
长短链代码优化:
- HttpClient的重复创建:在**
create
和shortUnCompress
方法中,都创建了新的HttpClient
对象。由于HttpClient
对象可以安全地在多个线程之间共享,因此通常建议将其作为单例来使用。你可以将其初始化为类的静态成员,这样就只会创建一次HttpClient
**,并在所有的请求中复用。注意!!这是因为他线程安全 - 异常处理:在**
create
方法中,你捕获了IllegalArgumentException
并抛出了自定义的Kxxxxxxx
,这很好,因为它使异常信息更加清晰。然而,这个方法还可能抛出其他的未检查异常,例如IOException
和InterruptedException
。你可能需要考虑这些异常的处理策略,比如是直接抛出,还是转换为Kxxxxx
**。 - 正则表达式的编译:在**
getabc
**方法中,你在每次调用时都编译了同一个正则表达式。编译正则表达式是一个相对昂贵的操作,如果正则表达式是不变的,你可以将其作为类的静态常量,只编译一次。
优化后代码如下,但是只提供一种思路并不适合正式场景,如请求次数过多影响性能。
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ShortLinkUtil {
private static final String URL_AUTHORITY = "x";
private static final int PATH_NUM = 8;
private static final int SUCCESS_RESPONSE = 200;
private static final Pattern ABC_PATTERN = Pattern.compile("abc=([^&]*)(&|$)");
private static final HttpClient CLIENT = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build();
private ShortLinkUtil() {
throw new IllegalStateException("Utility class");
}
/**
* 获取abc
*
* @param url
* @throws IOException
* @throws InterruptedException
*/
public static String getabc(String url) throws IOException, InterruptedException, xxxxxxxxx {
String paramInfo = shortUnCompress(url).getQuery();
Matcher matcher = abc_PATTERN.matcher(paramInfo);
if (!matcher.find()) {
throw new xxxxxxxx("xxxxxxxx不存在,请检查url果然是");
}
return matcher.group(1);
}
/**
* 获取longUrl
*
* @param shortUrl
* @return
* @throws IOException
* @throws InterruptedException
*/
@Deprecated(forRemoval = false)
public static URI shortUnCompress(String shortUrl) throws IOException, InterruptedException, xxxxxxxxxxxx {
URI uri = create(shortUrl);
HttpResponse<String> response = sendHttpRequest(uri);
URI resultURL = response.uri();
if (uri.equals(resultURL)) {
throw new xxxxxxxxxxxx("链接失效" + shortUrl);
}
return resultURL;
}
/**
* 查验格式、可用性 & 创建url
* @param str
* @return
* @throws xxxxxxx
*/
private static URI create(String str) throws xxxxxxxx {
URI uri;
try {
uri = URI.create(str); //基本格式验证
} catch (IllegalArgumentException e) {
throw new xxxxxxxxxxx("URL格式不合法" + str, e);
}
String scheme = uri.getScheme();
if (scheme == null) {
throw new xxxxxx("URL格式不合法" + str);
}
scheme = scheme.toLowerCase(Locale.US);
if (!(scheme.equals("https") || scheme.equals("http"))) {
throw new Kxxxxx("");
}
try {
InetAddress.getByName(uri.getHost()); //判断dns,,不需要
HttpResponse<String> response = sendHttpRequest(uri);
if (response.statusCode() != SUCCESS_RESPONSE) {
throw new xxxxxxx("解析失败" + str);
}
} catch (UnknownHostException e) {
throw new xxxxx("解析DNS失败" + uri.getHost());
} catch (IOException | InterruptedException e) {
throw new xxxxx("请求失败");
}
return uri;
}
/**
* 发送请求
* @param uri
* @return
* @throws IOException
* @throws InterruptedException
*/
private static HttpResponse<String> sendHttpRequest(URI uri) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(uri)
.GET() // This is the default
.build();
return CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
}
}