最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

springboot中請求地址轉(zhuǎn)發(fā)的兩種方案

 更新時(shí)間:2023年11月24日 15:29:02   作者:Mr.var  
在開發(fā)過程中,我們經(jīng)常需要將請求從一個(gè)服務(wù)轉(zhuǎn)發(fā)到另一個(gè)服務(wù),以實(shí)現(xiàn)不同服務(wù)之間的協(xié)作,本文主要介紹了springboot中請求地址轉(zhuǎn)發(fā)的兩種方案,感興趣的可以了解一下

一、背景需求

現(xiàn)有一個(gè)平臺,如果在上面發(fā)布軟件,需要在平臺注冊所有的接口,注冊好后平臺會給每一個(gè)接口都提供一個(gè)不同的新地址(所有的請求在平臺注冊后都是類似"http://localhost:8080/{appkey}/{token}"的格式,每個(gè)接口都擁有一個(gè)不同的appkey作為標(biāo)識,token可通過另一個(gè)請求獲?。?,在前端調(diào)用請求的時(shí)候,必須請求平臺提供的地址,然后平臺會替前端轉(zhuǎn)發(fā)到真實(shí)的地址去請求后端。

為了減少注冊和審核的工作量,我們可以只注冊少量接口,然后在這些接口內(nèi)我們自行轉(zhuǎn)發(fā)。

二、方案一

zuul轉(zhuǎn)發(fā):

在平臺注冊增刪改查等若干個(gè)虛擬的接口地址,然后在前端將所有接口封裝成這些虛擬接口,并在請求參數(shù)內(nèi)傳遞真實(shí)的接口地址,通過平臺轉(zhuǎn)發(fā)到后端之后我們通過zuul過濾器再轉(zhuǎn)發(fā)到自己真實(shí)的接口地址上。(注:登錄接口比較特殊,登錄在后端是寫在主服務(wù)內(nèi)的,zuul網(wǎng)關(guān)不會進(jìn)行攔截,這里單獨(dú)注冊;其余接口統(tǒng)一寫在同一個(gè)服務(wù)內(nèi),便于統(tǒng)一轉(zhuǎn)發(fā)配置)

前端代碼演示:

這里是在vue中寫的一個(gè)axios的請求攔截器,統(tǒng)一對真實(shí)接口進(jìn)行封裝
舉個(gè)例:
我們在平臺上注冊一個(gè)虛擬地址:
http://localhost:8080/comSelect/getData
=>
注冊后請求地址變?yōu)椋?br />http://xxx.xxx.xxx:xxxx/appKeySelect123/{token}

axios.interceptors.request.use(
	config => {
		if (!config.url.startsWith("http")) {
			    //模擬一個(gè)token,真實(shí)token可通過平臺提供的另一請求獲取
				let token = "token";
				
				//將接口地址放在covertUrl參數(shù)內(nèi)傳遞給后端
				if (config.method == "post") {
				    //post請求的兩種content-type格式
					if (typeof config.data == "string") {
						//請求參數(shù)表單格式
						//qs可用于格式化參數(shù)
						let conData = qs.parse(config.data);
						conData.covertUrl = config.url;

						config.data = qs.stringify(conData);
					} else {
						//請求體格式
						config.data.covertUrl = config.url;
					}
				} else if (config.method == "get"){
				    //axios中g(shù)et請求可用params指定url傳值
					config.params.covertUrl = config.url;
				}

                //封裝成平臺要求的請求地址,真實(shí)的url存于參數(shù)covertUrl中
				config.url = urlPack(token, config.url);
		}
		return config;
	},
	error => {
		return Promise.reject(error);
	}
);

//接口地址封裝,將所有接口統(tǒng)一分為增刪改查四個(gè)接口
function urlPack(token, url) {
	let appKey;
	//登陸
	let appKeyLogin = "/appKeyLogin123/";
	//增
	let appKeyAdd = "/appKeyAdd123/";
	//刪
	let appKeyDelete = "/appKeyDelete123/";
	//改
	let appKeyUpdate = "/appKeyUpdate123/";
	//查
	let appKeySelect = "/appKeySelect123/";  //http://localhost:8080/comSelect/getData 

    //隨便拿幾個(gè)接口舉例
	switch (url) {
		case "/sysUser/app_login":
			appKey = appKeyLogin;
			break;
		case "/appcommon/appVersion/getVersion":
			appKey = appKeySelect;
			break;
		case "/appcommon/appMenu/getMenu":
			appKey = appKeySelect;
			break;
	}

	return "http://localhost:8080" + appKey + token;
}

后端代碼演示:

#這里需要注意,必須在zuul的路由配置里添加平臺轉(zhuǎn)發(fā)之后傳遞過來的虛擬路由,不然zuul會報(bào)出找不到路由的錯(cuò)
zuul:
  #路由添加
  routes:
    #虛擬服務(wù)地址
    comSelect:
      path: /comSelect/**
    #真實(shí)的路由服務(wù),這里的地址是真實(shí)注冊到了eureka的服務(wù)地址,也可以動態(tài)獲取
    appcommon:
      path: /appcommon/**
      serviceId: appcommon
/**
 * 轉(zhuǎn)換成真正的url地址,路由轉(zhuǎn)發(fā)
 */
@Slf4j
@Component
public class ZuulAppRouteFilter extends ZuulFilter {
    /**
     * filterType:返回一個(gè)字符串代表過濾器的類型,在zuul中定義了四種不同生命周期的過濾器類型,具體如下:
     *     pre:路由之前
     *     routing:路由之時(shí)
     *     post: 路由之后
     *     error:發(fā)送錯(cuò)誤調(diào)用
     */
    @Override
    public String filterType() {
        return FilterConstants.ROUTE_TYPE;
    }

    /**
     * 過濾器優(yōu)先級,同一filterType下的過濾器,數(shù)值越大優(yōu)先級越低
     */
    @Override
    public int filterOrder() {
        return 1;
    }

    /**
     * 是否啟用過濾器,這里可以做一些邏輯判斷
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {

        RequestContext ctx = RequestContext.getCurrentContext();

        HttpServletRequest request = ctx.getRequest();
        //真正的接口地址
        String path = "";

        try {
            //請求參數(shù)(url傳值或表單傳值)
            Map<String, String[]> parameterMap = request.getParameterMap();
            //請求參數(shù)(請求體)
            String requestBody = null;
            try {
                requestBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (parameterMap.size() == 0) {
                if (requestBody != null && !"".equals(requestBody)) {
                    try {
                        JSONObject jsonObj = JSONObject.parseObject(requestBody);

                        if (jsonObj.get("covertUrl") != null) {
                            Object covertUrl = jsonObj.get("covertUrl");
                            path = String.valueOf(covertUrl);
                        }
                    } catch (Exception e) {
                        log.error("path[" + path + "]返回的不是json格式數(shù)據(jù),返回信息:" + requestBody);
                    }
                }
            } else {
                if (parameterMap.get("covertUrl") != null) {
                    String[] description = parameterMap.get("covertUrl");
                    path = URLDecoder.decode(description[0]);
                }
            }
            
            //所有的服務(wù)全部指向serviceId為appcommon這個(gè)路由
            //如果需要轉(zhuǎn)發(fā)到其他服務(wù)則通過判斷path來寫判斷
            String serviceId = "";
            if (path.contains("appcommon")) {
                    serviceId = "appcommon";
             } else if (path.contains("sync")) {
                    serviceId = "sync";
            }
            //請求地址轉(zhuǎn)發(fā)到真實(shí)的接口上
            ctx.put(FilterConstants.REQUEST_URI_KEY, path);

        } catch (Exception ignored) {
            log.error(request.getRequestURL().toString() + "解析失敗");
        }

        return null;
    }
}

三、方案二:

java反射:

在平臺注冊增刪改查等若干個(gè)接口地址,并在后端編寫這些接口作為統(tǒng)一分發(fā)接口,然后在前端將所有接口封裝成這些接口,并在請求參數(shù)內(nèi)傳遞接口的類名和對應(yīng)的方法名,通過平臺轉(zhuǎn)發(fā)傳遞到后端之后,后端利用Java的反射機(jī)制調(diào)用真實(shí)的接口地址,轉(zhuǎn)發(fā)到對應(yīng)的接口上。

前端代碼演示:

舉個(gè)例:
我們在平臺上注冊的地址:
http://localhost:8080/appcommon/common/query
=>
注冊后請求地址變?yōu)椋?br />http://localhost:8080/appKeySelect123/{token}

axios.interceptors.request.use(
	config => {
		if (!config.url.startsWith("http")) {
			    //模擬一個(gè)token,真實(shí)token可通過平臺提供的另一請求獲取
				let token = "token";
				
				let req;
				if (config.method == "post") {
					if (typeof config.data == "string") {
						//請求參數(shù)表單格式
						let conData = qs.parse(config.data);

						config.data = qs.stringify(conData);

						req = reqPack(token, config.url, config.data);

						config.url = req.url;
						config.data = req.reqData;
					} else {
						//請求體格式
						req = reqPack(token, config.url, config.data);
						config.url = req.url;
						config.data = req.reqData;
					}
				} else {
					req = reqPack(token, config.url, config.params);
					
					config.url = req.url;
					config.params = req.reqData;
				}

                //封裝成平臺要求的請求地址,真實(shí)的url存于參數(shù)covertUrl中
				config.url = urlPack(token, config.url);
		}
		return config;
	},
	error => {
		return Promise.reject(error);
	}
);

//接口地址封裝,將所有接口統(tǒng)一分為增刪改查四個(gè)接口
function urlPack(token, url, data) {
	//總線所需的key
	let appKey;
	//登陸
	let appKeyLogin = "/appKeyLogin123/";
	//增
	let appKeyAdd = "/appKeyAdd123/";
	//刪
	let appKeyDelete = "/appKeyDelete123/";
	//改
	let appKeyUpdate = "/appKeyUpdate123/";
	//查
	let appKeySelect = "/appKeySelect123/";  //http://localhost:8080/appcommon/common/query

	//請求參數(shù)
	let reqData = {
		//類名
		className: "",
		//方法名
		methodName: "",
		//接口所需參數(shù)
		params: data
	}
	
	//指定不同接口的類名和方法名,用于分發(fā)調(diào)用
	switch (url) {
	    //登錄請求比較特殊,單獨(dú)注冊,參數(shù)不封裝
		case "/sysUser/app_login":
			appKey = appLogin;
			return {
				url: GLOBAL.$RequestBaseUrl1 + appKey,
				reqData: data
			}
			break;
		case "/appcommon/appVersion/getVersion":
			appKey = appKeySelect;
			reqData.className = "AppVersionController";
			reqData.methodName = "getAppVersion";
			break;
		case "/sync/risk/road/getAllRoad":
			appKey = appKeySelect;
			break;
		case "/appcommon/appMenu/getMenu":
			appKey = appKeySelect;
			reqData.className = "AppMenuController";
			reqData.methodName = "getMenu";
			break;
	}

	return {
		url: GLOBAL.$RequestBaseUrl1 + appKey + token,
		reqData: reqData
	};
}

后端代碼演示:

/**
 * 公共接口實(shí)例
 */
@Data
public class CommonObj {
    /**
     * 類名
     */
    private String className;
    /**
     * 方法名
     */
    private String methodName;
    /**
     * 實(shí)際參數(shù)
     */
    private Map<String,Object> params;
}
/**
 * Spring定義的類實(shí)現(xiàn)ApplicationContextAware接口會自動的將應(yīng)用程序上下文加入
 */
@Slf4j
@Component
public class MySpringUtil implements ApplicationContextAware {
    //上下文對象實(shí)例
    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (MySpringUtil.applicationContext == null) {
            MySpringUtil.applicationContext = applicationContext;
        }
    }

    //獲取applicationContext
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    //通過name獲取 Bean.
    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    //通過class獲取Bean.
    public static <T> T getBean(Class<T> clazz) {
        return getApplicationContext().getBean(clazz);
    }

    //通過name,以及Clazz返回指定的Bean
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

}
/**
 * app公共接口調(diào)用,通過反射分發(fā)調(diào)用接口
 *
 * @author xht
 */
@RestController
@RequestMapping("/common")
@Slf4j
public class CommonController {

    /**
     * 利用反射調(diào)用接口
     */
    public Response reflectControl(CommonObj commonObj){
        String className = commonObj.getClassName();
        String methodName = commonObj.getMethodName();
        Map<String, Object> params = commonObj.getParams();

        Response response;
        try {
            //1、獲取spring容器中的Bean
            //類名首字母小寫
            className = StringUtils.uncapitalize(className);
            Object proxyObject = MySpringUtil.getBean(className);
            //2、利用bean獲取class對象,進(jìn)而獲取本類以及父類或者父接口中所有的公共方法(public修飾符修飾的)
            Method[] methods = proxyObject.getClass().getMethods();
            //3、獲取指定的方法
            Method myMethod = null;
            for (Method method : methods) {
                if (method.getName().equalsIgnoreCase(methodName)) {
                    myMethod = method;
                    break;
                }
            }
            //4、封裝方法需要的參數(shù)
            if (myMethod != null) {
                Object resObj;
                resObj = myMethod.invoke(proxyObject, params);

                response = (Response) resObj;
            } else {
                response = Response.error("未找到對應(yīng)方法");
            }
        } catch (Exception e) {
            e.printStackTrace();
            response = Response.error(e.getMessage());
        }
        return response;
    }

    /**
     * 公共新增接口
     */
    @PostMapping("/add")
    public Response commonAdd(@RequestBody CommonObj commonObj) {
        return reflectControl(commonObj);
    }
    /**
     * 公共刪除接口
     */
    @PostMapping("/delete")
    public Response commonDelete(@RequestBody CommonObj commonObj) {
        return reflectControl(commonObj);
    }
    /**
     * 公共修改接口
     */
    @PostMapping("/edit")
    public Response commonEdity(@RequestBody CommonObj commonObj) {
        return reflectControl(commonObj);
    }
    /**
     * 公共查詢接口
     */
    @PostMapping("/query")
    public Response commonQuery(@RequestBody CommonObj commonObj) {
        return reflectControl(commonObj);
    }
}

到此這篇關(guān)于springboot中請求地址轉(zhuǎn)發(fā)的兩種方案的文章就介紹到這了,更多相關(guān)springboot 請求地址轉(zhuǎn)發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • RocketMq同組消費(fèi)者如何自動設(shè)置InstanceName

    RocketMq同組消費(fèi)者如何自動設(shè)置InstanceName

    這篇文章主要介紹了RocketMq同組消費(fèi)者如何自動設(shè)置InstanceName問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • Java中通過ZipOutputStream類如何將多個(gè)文件打成zip

    Java中通過ZipOutputStream類如何將多個(gè)文件打成zip

    ZipOutputStream?是Java中用于創(chuàng)建ZIP文件的類,它是?java.util.zip?包中的一部分,通過使用?ZipOutputStream?,可以將多個(gè)文件壓縮到一個(gè)ZIP文件中,這篇文章主要介紹了Java中(ZipOutputStream)如何將多個(gè)文件打成zip,需要的朋友可以參考下
    2023-09-09
  • Java實(shí)現(xiàn)簡單猜拳游戲

    Java實(shí)現(xiàn)簡單猜拳游戲

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡單猜拳游戲,輸入字符,不輸入數(shù)字,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • 淺談Java8的特性之Optional類

    淺談Java8的特性之Optional類

    這篇文章主要介紹了淺談Java8的特性之Optional類,Optional類是 Java 8 引入的一個(gè)很有趣的特性,Optional 類是一個(gè)可以為null的容器對象,它主要解決的問題是臭名昭著的空指針異常,需要的朋友可以參考下
    2023-08-08
  • java使用rmi傳輸大文件示例分享

    java使用rmi傳輸大文件示例分享

    由于在rmi中無法傳輸文件流,可以先用FileInputStream將文件讀到一個(gè)Byte數(shù)組中,然后把這個(gè)Byte數(shù)組作為參數(shù)傳進(jìn)RMI的方法中,然后在服務(wù)器端將Byte數(shù)組還原為outputStream,這樣就能通過RMI 來傳輸文件了,下面我們來看實(shí)例
    2014-01-01
  • java中Javers?比較兩個(gè)類的差異

    java中Javers?比較兩個(gè)類的差異

    本文主要介紹了Javers?比較兩個(gè)類的差異,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 詳解Spring依賴注入的三種方式使用及優(yōu)缺點(diǎn)

    詳解Spring依賴注入的三種方式使用及優(yōu)缺點(diǎn)

    這篇文章主要介紹了spring依賴注入的三種方式的使用方法,以及優(yōu)缺點(diǎn)的介紹,通過代碼示例介紹的非常詳細(xì),感興趣的小伙伴可以參考一下
    2023-04-04
  • java分析html算法(java網(wǎng)頁蜘蛛算法示例)

    java分析html算法(java網(wǎng)頁蜘蛛算法示例)

    近來有些朋友在做蜘蛛算法,或者在網(wǎng)頁上面做深度的數(shù)據(jù)挖掘,下面使用示例
    2014-03-03
  • Java異常處理3個(gè)避坑神操作總結(jié)(告別崩潰式報(bào)錯(cuò))

    Java異常處理3個(gè)避坑神操作總結(jié)(告別崩潰式報(bào)錯(cuò))

    日常Java開發(fā)中,有些細(xì)節(jié)看似微小,卻能大幅影響代碼質(zhì)量,這篇文章主要介紹了Java異常處理3個(gè)避坑神操作總結(jié)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2026-04-04
  • idea生成類注釋和方法注釋的正確方法(推薦)

    idea生成類注釋和方法注釋的正確方法(推薦)

    這篇文章主要介紹了idea生成類注釋和方法注釋的正確方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11

最新評論

岳西县| 杂多县| 文昌市| 汤原县| 尼木县| 莲花县| 湄潭县| 永清县| 乳山市| 清水县| 武宣县| 奎屯市| 中阳县| 保康县| 德江县| 景泰县| 阜宁县| 连江县| 江永县| 平江县| 高青县| 柘城县| 甘洛县| 三穗县| 威远县| 冷水江市| 阜康市| 崇文区| 太保市| 襄汾县| 高邑县| 扶绥县| 大田县| 平邑县| 军事| 巴中市| 宁都县| 浪卡子县| 固始县| 黄陵县| 长葛市|