寫在最前面
項目完整源碼(歡迎star):github項目源碼
項目臨時預覽地址(指不定什麼時候就掛了):短網址生成項目預覽
引入主題
前幾天在某論壇看到這樣一篇帖子,說的是“大家第一個項目,都是從網址導航開始?”,瀏覽半天回覆的內容發現都是大佬啊,有做了個瀏覽器插件的,有做了個博客的,這個—>“片段”<—更不錯,手擼出來的多厲害。當然也有不少做的網址導航的,於是我想了想之前搞的一個項目,默默的評論了一句。
翻到上面看到一位老哥說的是短網址項目。
於是我腦瓜一熱,百度了下短網址的原理。發現還是很簡單的,說時遲那時快,我已經打開了IDEA,創建項目手擼開幹。項目架構選擇了springboot,持久層還是用的mybatis,前端依然是bootstrap。
用了不到一個下午的時間項目已經出來了。整個項目中,最浪費時間的還是前端調樣式。前端菜的一批。
實現原理
下面說下實現的原理
首先在用戶輸入一條長鏈接傳到後端的時候,我先生成了數字和字母隨機組成的6位字符,然後把這個字符和長鏈接保存到數據庫。保存成功後,把這6位字符拼上我的網址,返回給用戶,就像是這樣的 https://wjup.top/HtN3Gc
當用戶拿到這個短網址訪問的時候。我在後臺進行獲取這個短網址的6個字符,然後根據這個字符到數據庫查詢原來的鏈接,再進行301永久重定向到原網址就可以了。整體實現非常簡單。當然我還增加了對短網址加密的功能,只有輸入正確的密碼才能訪問原始鏈接
邏輯代碼
下面放出主要的實現代碼。項目完整版可以到github中去克隆島到本地研究
package com.wjup.shorturl.controller;
import com.alibaba.fastjson.JSONObject;
import com.wjup.shorturl.entity.UrlEntity;
import com.wjup.shorturl.service.UrlService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.thymeleaf.util.DateUtils;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.Locale;
import java.util.Random;
import java.util.UUID;
/**
* Create by wjup on 2019/9/29 11:33
* <p>
* 短網址生成項目
*/
@Controller
public class UrlController {
@Autowired
private UrlService urlService;
@RequestMapping("/")
public String index() {
return "index";
}
/**
* 創建短鏈接
*
* @param longUrl 原地址
* @param viewPwd 訪問密碼
* @param request 請求
* @return json
*/
@RequestMapping("/create")
@ResponseBody
public String creatShortUrl(String longUrl, String viewPwd, HttpServletRequest request) {
JSONObject json = new JSONObject();
String[] split = longUrl.split("\n|\r");
StringBuffer msg = new StringBuffer();
for (int i = 0; i < split.length; i++) {
UrlEntity urlEntity = new UrlEntity();
if (!split[i].contains("https://") && !split[i].contains("http://")) {
split[i] = "http://" + split[i];
}
String shortUrlId = getStringRandom(6);
urlEntity.setShortUrlId(shortUrlId);
urlEntity.setUuid(UUID.randomUUID().toString());
urlEntity.setLongUrl(split[i]);
urlEntity.setCreateTime(DateUtils.format(new Date(), "yyyy-MM-dd HH-mm-ss", Locale.SIMPLIFIED_CHINESE));
urlEntity.setViewPwd(viewPwd);
int flag = urlService.createShortUrl(urlEntity);
String toUrl = "/";
int serverPort = request.getServerPort();
if (serverPort == 80 || serverPort == 443) {
toUrl = request.getScheme() + "://" + request.getServerName();
} else {
toUrl = request.getScheme() + "://" + request.getServerName() + ":" + serverPort;
}
if (flag > 0) {
msg.append(toUrl + "/" + shortUrlId + "<br>");
}
}
json.put("shortUrl", msg);
return json.toJSONString();
}
/**
* 訪問短鏈接
*
* @param shortUrlId 短網址id
* @param response 響應
* @param request 請求
* @throws ServletException 異常捕獲
* @throws IOException 異常捕獲
*/
@RequestMapping(value = "/{shortUrlId}")
public void view(@PathVariable("shortUrlId") String shortUrlId, HttpServletResponse response, HttpServletRequest request) throws ServletException, IOException {
UrlEntity urlEntity = urlService.findByShortUrlId(shortUrlId);
if (urlEntity != null) {
if (urlEntity.getViewPwd() != null && !"".equals(urlEntity.getViewPwd())) {
request.setAttribute("shortUrlId", shortUrlId);
request.getRequestDispatcher("/viewPwd").forward(request, response);
} else {
urlService.updateShortUrl(shortUrlId);
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
response.setHeader("Location", urlEntity.getLongUrl());
}
} else {
request.getRequestDispatcher("/noPage").forward(request, response);
}
}
/**
* 沒有該請求跳轉到指定頁面
*
* @return page
*/
@RequestMapping("/noPage")
public String noPage() {
return "noPage";
}
/**
* 有密碼打開輸入密碼頁面
*
* @return html
*/
@RequestMapping("/viewPwd")
public String viewPwd(HttpServletRequest request, Model model) {
String shortUrlId = request.getAttribute("shortUrlId").toString();
model.addAttribute("shortUrlId", shortUrlId);
return "viewPwd";
}
/**
* 驗證密碼是否正確
*
* @param viewPwd 密碼
* @param shortUrlId 短址id
*/
@RequestMapping("/VerifyPwd")
@ResponseBody
public String VerifyPwd(String viewPwd, String shortUrlId) {
UrlEntity urlEntity = urlService.findByPwd(viewPwd, shortUrlId);
JSONObject jsonObject = new JSONObject();
if (urlEntity != null) {
urlService.updateShortUrl(shortUrlId);
jsonObject.put("longUrl", urlEntity.getLongUrl());
jsonObject.put("flag", true);
} else {
jsonObject.put("flag", false);
}
return jsonObject.toJSONString();
}
/**
* 生成隨機數字和字母
*
* @param length 生成長度
* @return shortUrlId
*/
private String getStringRandom(int length) {
String val = "";
Random random = new Random();
//參數length,表示生成幾位隨機數
for (int i = 0; i < length; i++) {
String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
//輸出字母還是數字
if ("char".equalsIgnoreCase(charOrNum)) {
//輸出是大寫字母還是小寫字母
int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
val += (char) (random.nextInt(26) + temp);
} else if ("num".equalsIgnoreCase(charOrNum)) {
val += String.valueOf(random.nextInt(10));
}
}
return val;
}
}