vue3使用iframe嵌入ureport2設(shè)計(jì)器,解決預(yù)覽時(shí)NullPointerException異常問題
1. 后端準(zhǔn)備
關(guān)于SpringBoot集成UReport2的文章網(wǎng)絡(luò)上有很多,這里只記錄2個(gè)容易踩坑的地方:
配置類
很多文章在寫后端集成的時(shí)候,都在配置類中使用@Bean
注解創(chuàng)建ServletRegistrationBean
和UReportPropertyPlaceholderConfigurer
對象。
但在實(shí)際使用過程中發(fā)現(xiàn),如果要將SpringBoot創(chuàng)建的DataSource作為UReport2的內(nèi)置數(shù)據(jù)源,則需要將@Bean
注解放到配置類以外的地方,否則DataSource無法注入。
@Configuration public class ReportBean { //添加 report 的servlet @Bean public ServletRegistrationBean<Servlet> ureport2Servlet() { return new ServletRegistrationBean<>(new UReportServlet(), "/ureport/*"); } //這一步省略了創(chuàng)建配置文件 @Bean public UReportPropertyPlaceholderConfigurer UReportPropertyPlaceholderConfigurer() { UReportPropertyPlaceholderConfigurer propertyConfigurer = new UReportPropertyPlaceholderConfigurer(); propertyConfigurer.setIgnoreUnresolvablePlaceholders(true); ClassPathResource pathResource = new ClassPathResource("config/config.properties"); propertyConfigurer.setLocation(pathResource); return propertyConfigurer; } }
@Configuration //導(dǎo)入ureport-console-context.xml文件 @ImportResource("classpath:config/context.xml") @Slf4j public class ReportConfig implements BuildinDatasource { @Autowired private DataSource dataSource; /** * 數(shù)據(jù)源名稱 **/ @Override public String name() { return "ReportSource"; } /** * 獲取連接 **/ @Override public Connection getConnection() { try { return dataSource.getConnection(); } catch (SQLException e) { log.error("Ureport 數(shù)據(jù)源 獲取連接失??!"); e.printStackTrace(); } return null; } }
report.fileStoreDir配置
在ureport的配置文件中,ureport.fileStoreDir
表示報(bào)表的xml文件在磁盤上的存儲(chǔ)目錄。
配置完這個(gè)目錄,必須手動(dòng)創(chuàng)建,否則在設(shè)計(jì)器中保存報(bào)表時(shí)會(huì)報(bào)錯(cuò)。
2. 前端嵌入
假設(shè)后端集成完畢后,報(bào)表設(shè)計(jì)器的訪問路徑在http://localhost:9000/ureport/designer
<template> <div v-loading="loading" class="h-full"> <iframe :src="src" frameborder="no" class="wh-full" scrolling="auto" /> </div> </template>
<script setup lang="tsx"> import { ref } from 'vue'; const src = ref<string>('/ureport/designer'); const loading = ref<boolean>(true); </script>
可以看到,我們沒有直接將iframe的src指向http://localhost:9000/ureport/designer
,否則會(huì)由于跨域問題而無法顯示。
這里我們配了一個(gè)報(bào)表設(shè)計(jì)器的相對路徑,并將“ureport”進(jìn)行前端代理。
'/ureport': { target: envConfig.secondUrl, changeOrigin: true, rewrite: path => path.replace(new RegExp(`^${envConfig.urlPattern}`), '/ureport') }
這里我們讓代理攔截前綴為"/ureport"的請求。
由于報(bào)表后端路徑也是以"/ureport"開頭,所以rewrite中不需要將"/ureport"刪掉,也可以不寫rewrite。
3. 預(yù)覽問題
假設(shè)我們的前端工程以8000端口啟動(dòng)。
將ureport報(bào)表設(shè)計(jì)器嵌入到iframe后,點(diǎn)擊左上角的預(yù)覽,瀏覽器將打開一個(gè)新的tab頁,其URL地址為http://localhost:8000/ureport/preview?_u=p
。
其中,參數(shù)_u
表示要預(yù)覽的報(bào)表文件名稱,在預(yù)覽時(shí)約定文件名為p。此時(shí)頁面無法正常打開,只顯示NullPointerException。
經(jīng)過跟蹤ureport2源碼,發(fā)現(xiàn)_u
參數(shù)沒有發(fā)送到后端。
4. 問題排查與解決
1.嘗試使用后端端口直接訪問http://localhost:9000/ureport/preview?_u=p發(fā)現(xiàn)可以訪問,但無法預(yù)覽。閱讀源碼發(fā)現(xiàn)ureport預(yù)覽報(bào)表時(shí),報(bào)表內(nèi)容隨session傳遞。因此前后端端口不一致時(shí)找不到報(bào)表內(nèi)容,無法預(yù)覽。
2.嘗試新建一個(gè)vue頁面,來進(jìn)行請求轉(zhuǎn)發(fā)發(fā)現(xiàn)行不通。直接轉(zhuǎn)發(fā)到后端依然存在跨域問題,通過前端代理進(jìn)行轉(zhuǎn)發(fā),參數(shù)還是傳不到后端。
3.使用postman調(diào)用前端地址(注意,調(diào)用8000端口)http://localhost:8000/ureport/preview?_u=p,發(fā)現(xiàn)參數(shù)可以傳遞到后端。
到這一步就比較尷尬了。同一地址,使用postman請求,前端代理可以把URL完整傳遞到后端。使用瀏覽器請求,前端代理不發(fā)送URL參數(shù)到后端。
經(jīng)過比對和測試,發(fā)現(xiàn)瀏覽器發(fā)送請求時(shí),其header頭的Accept內(nèi)容為“text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7”。當(dāng)使用postman發(fā)送請求時(shí),其header頭的Accept內(nèi)容為“*/*”
跟蹤了代理的rewrite方法,發(fā)現(xiàn)使用瀏覽器訪問時(shí),傳入的path是不帶參數(shù)的。閱讀vite的部分源碼,并結(jié)合查詢到的一些資料,發(fā)現(xiàn)在配置代理時(shí),除了常規(guī)的target、rewrite以外,還可以配置
configure和bypass
源碼如下:
export declare interface ProxyOptions extends HttpProxy.ServerOptions { /** * rewrite path */ rewrite?: (path: string) => string; /** * configure the proxy server (e.g. listen to events) */ configure?: (proxy: HttpProxy.Server, options: ProxyOptions) => void; /** * webpack-dev-server style bypass function */ bypass?: (req: http.IncomingMessage, res: http.ServerResponse, options: ProxyOptions) => void | null | undefined | false | string; }
可以看到,configure和bypass方法,都可以取得一個(gè)req變量,其類型是http.IncomeingMessage
。
跟蹤req對象,發(fā)現(xiàn)其存在兩個(gè)屬性:
- req.client.originalUrl,其屬性值為
/ureport/preview?_u=p
- req.url,其屬性值為
/ureport/preview
到這里,終于有了些許思路。我們最終的目的就是利用前端代理來訪問ureport的預(yù)覽界面,只要解決了前端代理的參數(shù)傳遞問題,就可以直接預(yù)覽了。
因此,目前的思路就是讓代理直接轉(zhuǎn)發(fā)req.client.originalUrl
就好。
因此我們改造一下定義代理的配置語句,增加bypass配置:
'/ureport': { target: envConfig.secondUrl, changeOrigin: true, rewrite: path => path.replace(new RegExp(`^${envConfig.urlPattern}`), '/ureport'), bypass: (req, res, proxyOption) => { req.url = req.originalUrl; } }
在這里,我們在代理轉(zhuǎn)發(fā)請求之前,將他本來要轉(zhuǎn)發(fā)的地址強(qiáng)行替換為原url地址。
此時(shí)請求的參數(shù)可以轉(zhuǎn)發(fā)到后端,預(yù)覽頁面成功打開。
5. 總結(jié)
1.雖然在分析過程中,知道了瀏覽器請求和postman請求時(shí),由于Header中的Accept不同導(dǎo)致了不同的結(jié)果,但最終還是沒有精力去找具體是哪段代碼導(dǎo)致了這個(gè)差異。換句話說,vite用到的http-proxy組件在不同情況下對URL地址進(jìn)行了不同的處理,如果有知道原理的大神可以留個(gè)言。
2.vue2下沒測出來類似的問題,等有時(shí)間要查查vue2前端代理用的組件和vue3有什么不同。
3.nginx的代理可以完美轉(zhuǎn)發(fā)請求,不存在丟參數(shù)的情況。
4.我們最終在bypass中強(qiáng)行修改了代理轉(zhuǎn)發(fā)的url地址,并成功打開了頁面,但這種做法有違http-proxy的設(shè)計(jì)原理。目前不清楚這種做法會(huì)造成什么其他未知后果,不過想來問題不大,因?yàn)閎ypass只對"/ureport"開頭的請求進(jìn)行了代理,不影響系統(tǒng)的其他請求。
5.這個(gè)小問題排查了兩三天,看見很多關(guān)于ureport2集成的文章下面都有人問這個(gè)預(yù)覽的問題如何解決。所以就臨時(shí)整理思路寫了這篇文章,問題排查部分寫的可能比較亂,湊合看吧。以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
vue實(shí)現(xiàn)移動(dòng)端適方案的完整步驟
現(xiàn)在的手機(jī)五花八門,造就了移動(dòng)端窗口分辨率繁多的局面,在不同分辨率的屏幕下保持與UI圖一致的效果,就成了讓前端不得不頭疼的問題,下面這篇文章主要給大家介紹了vue實(shí)現(xiàn)移動(dòng)端適方案的相關(guān)資料,需要的朋友可以參考下2022-10-10詳解在vue-cli中引用jQuery、bootstrap以及使用sass、less編寫css
這篇文章主要介紹了詳解在vue-cli中引用jQuery、bootstrap以及使用sass、less編寫css,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11vue v-for循環(huán)出來的數(shù)據(jù)動(dòng)態(tài)綁定值問題
這篇文章主要介紹了vue v-for循環(huán)出來的數(shù)據(jù)動(dòng)態(tài)綁定值問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04詳解如何在Vue3+TS的項(xiàng)目中使用NProgress進(jìn)度條
本文主要介紹了詳解如何在Vue3+TS的項(xiàng)目中使用NProgress進(jìn)度條,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06使用vue實(shí)現(xiàn)點(diǎn)擊按鈕滑出面板的實(shí)現(xiàn)代碼
這篇文章主要介紹了使用vue實(shí)現(xiàn)點(diǎn)擊按鈕滑出面板的實(shí)現(xiàn)代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-01-01Vue基于iview table展示圖片實(shí)現(xiàn)點(diǎn)擊放大
這篇文章主要介紹了Vue基于iview table展示圖片實(shí)現(xiàn)點(diǎn)擊放大,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-08-08