HarmonyOS鸿蒙Next中使用webview加载网页跨域问题

发布于 1周前 作者 zlyuanteng 来自 鸿蒙OS

HarmonyOS鸿蒙Next中使用webview加载网页跨域问题 使用uniapp开发鸿蒙应用,web组件加载网页时报跨域问题

3 回复

可参考demo:

import harmonyWebView from '@ohos.web.webview'
import picker from '@ohos.file.picker';
import { BusinessError } from '@ohos.base';
import { abilityAccessCtrl, common } from '@kit.AbilityKit';
import { util } from '@kit.ArkTS';

interface Detail {}

interface Event {
  detail: Detail
}

interface TitleUpdateEventDetail {
  title?: string
}

interface TitleUpdateEvent extends Event {
  detail: TitleUpdateEventDetail
}

@Component
export struct WebView {
  @Prop @Watch('loadSrc') src: string
  onMessage?: (event: Event) => void = undefined
  onTitleUpdate?: (event: TitleUpdateEvent) => void = undefined
  onPostMessageToService?: (event: Event) => void = undefined
  exposeWebViewController?: (controller: harmonyWebView.WebviewController) => void = undefined
  controller = new harmonyWebView.WebviewController()
  url: string = ''

  aboutToAppear(): void {
    this.exposeWebViewController?.(this.controller)
  }

  build() {
    Web({
      src: '',
      controller: this.controller
    })
      .overScrollMode(OverScrollMode.NEVER)
      .keyboardAvoidMode(WebKeyboardAvoidMode.RESIZE_VISUAL)
      .geolocationAccess(true)
      .domStorageAccess(true)
      .imageAccess(true)
      .fileAccess(true)
      .onTitleReceive(event => {
        this.onTitleUpdate?.({
          detail: {
            title: event?.title
          }
        })
      })
      .onConsole(event => {
        if (event) {
          console.log('getMessage: ' + JSON.stringify(event.message.getMessage()))
        }
        return false
      })
      .onErrorReceive(event => {
        if (event) {
          console.error(event.error.getErrorInfo())
        }
      })
      .javaScriptProxy({
        object: {
          postMessage: (data: string) => {
            if (this.onMessage) {
              this.onMessage({ detail: JSON.parse(data) })
            }
          },
          postMessageToService: (data: string) => {
            if (this.onPostMessageToService) {
              this.onPostMessageToService({ detail: JSON.parse(data) })
            }
          }
        },
        name: '__uniapp_x_',
        methodList: ['postMessage', 'postMessageToService'],
        controller: this.controller
      })
      .onGeolocationShow(event => {
        let context = getContext(this) as common.UIAbilityContext;
        let atManager = abilityAccessCtrl.createAtManager();
        atManager.requestPermissionsFromUser(context, ["ohos.permission.APPROXIMATELY_LOCATION"]).then(data => {
          console.info('data authResults:' + data.authResults);
        }).catch(error: BusinessError => {
          console.error(`Failed to request permissions from user. Code is ${error.code}, message is ${error.message}`);
        })
        AlertDialog.show({
          title: '位置权限请求',
          message: '是否允许获取位置信息',
          primaryButton: {
            value: 'cancel',
            action: () => {
              if (event) {
                event.geolocation.invoke(event.origin, false, false);
              }
            }
          },
          secondaryButton: {
            value: 'ok',
            action: () => {
              if (event) {
                event.geolocation.invoke(event.origin, true, true);
              }
            }
          },
          cancel: () => {
            if (event) {
              event.geolocation.invoke(event.origin, false, false);
            }
          }
        })
      })
      .onShowFileSelector(event => {
        const selectOptions = new picker.PhotoSelectOptions();
        const mode = event?.fileSelector.getMode();
        const acceptType = event?.fileSelector.getAcceptType();
        if (mode === 0) {
          selectOptions.maxSelectNumber = 1;
        }
        if (acceptType) {
          let type = "";
          const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp'];
          const containsImage = imageExtensions.some((ext: string): boolean => acceptType.includes(ext));
          const videoExtensions =
            ['.mp4', '.mov', '.avi', '.mkv', '.flv', '.wmv', '.ogg', '.webm', 'mpg', 'mpeg', '.3gp', 'rm', 'rmvb',
              'm4v', 'wma', 'mts'];
          const containsVideo = videoExtensions.some((ext: string): boolean => acceptType.includes(ext));
          if (containsImage && containsVideo) {
            type = "IMAGE_VIDEO_TYPE";
          } else if (containsImage) {
            type = "IMAGE_TYPE";
          } else if (containsVideo) {
            type = "VIDEO_TYPE";
          }
          if (type) {
            selectOptions.MIMEType = picker.PhotoViewMIMETypes[type]
          }
        }
        let filePaths: Array<string> | null = null;
        const viewPicker = new picker.PhotoViewPicker();
        viewPicker.select(selectOptions).then(selectResult => {
          filePaths = selectResult.photoUris;
          if (event) {
            event.result.handleFileList(filePaths);
          }
        }).catch(err: BusinessError => {
          console.error(`Invoke viewPicker.select failed, code is ${err.code}, message is ${err.message}`);
        })
        return true
      })
      .onHttpErrorReceive(event=>{
        console.info('dddddd', JSON.stringify(event.response.getResponseData()), JSON.stringify(event.response.getResponseHeader()), event.request.getRequestUrl())
      })
      .onControllerAttached(()=>{
        // controller和web建联
        this.loadSrc()
      })
  }

  // 加载页面
  loadSrc() {
    if(!this.src) {
      return
    }
    if(this.src.indexOf('resource://rawfile/') > -1){
      // rawfile中的资源
      let rawfilePath = this.src.split('resource://rawfile/')[1]
      console.info('test')
      // script解析有问题,
      // this.controller.loadUrl(this.src)
      getContext(this).resourceManager.getRawFileContent(rawfilePath, (_err, value) => {
        let decoder = util.TextDecoder.create('utf-8');
        // buffer转化为str
        let str = decoder.decodeWithStream(value);
        this.controller.loadData(str,"text/html", "UTF-8", 'https://*.com')
      });
    }else{
      this.controller.loadUrl(this.src)
    }
  }
}

更多关于HarmonyOS鸿蒙Next中使用webview加载网页跨域问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,WebView组件加载网页时,跨域问题主要是由于浏览器的同源策略限制导致的。同源策略要求网页只能访问与其源(协议、域名、端口)相同的资源,否则会触发跨域限制。

鸿蒙Next中的WebView默认遵循同源策略,因此在加载跨域资源时可能会遇到限制。为解决跨域问题,可以通过以下方式处理:

  1. CORS(跨域资源共享):服务器端可以通过设置HTTP响应头(如Access-Control-Allow-Origin)来允许特定域或所有域访问资源。鸿蒙Next的WebView会遵循这些响应头。

  2. 代理服务器:在鸿蒙应用中设置代理服务器,将跨域请求转发到同源地址,从而绕过浏览器同源策略。

  3. iframe跨域通信:使用postMessage API实现跨域iframe之间的安全通信。鸿蒙Next的WebView支持该API。

  4. WebView设置:鸿蒙Next的WebView提供了相关设置接口,允许开发者自定义跨域行为。例如,可以通过WebSettings类配置是否允许跨域访问。

需要注意的是,跨域问题的处理应遵循安全性原则,避免引入安全漏洞。

在HarmonyOS鸿蒙Next中使用WebView加载网页时,若遇到跨域问题,可以通过以下方式解决:

  1. 配置CORS:确保服务器端正确配置了Access-Control-Allow-Origin头,允许WebView所在的域访问资源。

  2. 使用代理服务器:通过代理服务器转发请求,避免直接跨域访问。

  3. 本地资源加载:将网页资源(如HTML、CSS、JS)打包到应用中,通过file://协议加载,避免跨域限制。

  4. WebView设置:在WebView中启用JavaScript和允许跨域请求:

    WebSettings webSettings = webView.getSettings();
    webSettings.setJavaScriptEnabled(true);
    webSettings.setAllowUniversalAccessFromFileURLs(true);
  5. 安全策略:确保跨域请求符合应用的安全策略,避免引入安全风险。

通过以上方法,可以有效解决HarmonyOS鸿蒙Next中WebView加载网页的跨域问题。

回到顶部
AI 助手
你好,我是IT营的 AI 助手
您可以尝试点击下方的快捷入口开启体验!