【问题标题】:How to embed third party javascript widgets from Cloudinary dynamically in Angular?如何在 Angular 中动态嵌入来自 Cloudinary 的第三方 javascript 小部件?
【发布时间】:2020-01-13 19:09:26
【问题描述】:

当我尝试通过 Angular HttpClient 下载和使用 Cloudinary 小部件 javascript 资源时遇到 CORS 错误。

发生了什么事?我对 HttpClient 或 CORS 并不陌生,但从未见过。

从源“http://127.0.0.1:4200”访问“https://widget.cloudinary.com/v2.0/global/all.js”处的 XMLHttpRequest 已被 CORS 策略阻止:请求的资源上不存在“Access-Control-Allow-Origin”标头。

  1. 服务器CORS与这个问题无关。它是公开可用的代码,可以在任何浏览器中轻松检索。

  2. 请求的脚本确实到达在我的 Chrome 开发工具/网络选项卡 XHR 部分。于是服务器发送出去,Chrome 愉快地收到了。

  3. 我在 Angular 开发环境中。

  4. 问题出在 Angular 上,我认为是 HttpClient。它认为存在不存在的 CORS 问题。

  5. 开发工具中的请求标头:Sec-Fetch-Mode: cors

我查看了一堆其他 SO 帖子,包括一个看起来相似但没有帮助的帖子。

我的代码。

export class CloudinaryComponent implements OnInit {

  private url = 'https://widget.cloudinary.com/v2.0/global/all.js';

  constructor(
    private http: HttpClient
  ) {}

  ngOnInit() {
    this.loadWidget;
  }


  // Load the Cloudinary Upload Widget code, not the widget GUI.
  private loadWidget() {
    return this.http.get(this.url);
  };

  // Button click calls the Upload Widget GUI.

  private callPopup() {
    this.loadWidget().subscribe( result => {
        // this.uploadWidget.open();
    });
  }
}

如何正确嵌入 Cloudinary 小部件?

【问题讨论】:

    标签: angular angular-httpclient cloudinary


    【解决方案1】:

    问题

    不要忽略错误消息。 CORS 问题与服务器未提供正确的标头以允许从您的网站 javascript 代码进行跨源访问有关!正如错误所说

    请求的资源上不存在“Access-Control-Allow-Origin”标头。

    问题不在于您的客户端。 Angular 不会产生这个错误。出于安全原因,您的浏览器正在阻止该请求。服务器刚刚决定不允许此类请求。您可以直接从浏览器访问资源这一事实可能会欺骗您,但在这种情况下,您以不同的方式访问资源(即不是从 javascript XMLHttpRequest)。

    如果您想完全理解 CORS,请阅读它:

    https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

    XMLHttpRequest cannot load XXX No 'Access-Control-Allow-Origin' header

    解决方案

    使用 HttpClient 下载 javascript 文件不会使您能够使用小部件!您必须将 cloudinary js 文件嵌入到您的 html 中才能使用它。

    动态嵌入和创建 javascript 小部件的服务可能如下所示:

    Demo

    import { Injectable, RendererFactory2, Renderer2 } from '@angular/core';
    import { Observable, of, fromEvent } from 'rxjs';
    import { map } from 'rxjs/operators';
    declare let cloudinary: any; // declare js widget variable
    
    const widgetUrl = 'https://widget.cloudinary.com/v2.0/global/all.js';
    
    @Injectable({
      providedIn: 'root'
    })
    export class CloudinaryService {
      private renderer: Renderer2;
    
      constructor(rendererFactory: RendererFactory2) {
        this.renderer = rendererFactory.createRenderer(null, null);
      }
    
      // create the upload widget
      createUploadWidget(data: any, callback: (error: any, result: any) => void): Observable<any> {
        return this.skriptExists(widgetUrl)
          // js is embeded -> call js function directly
          ? of(cloudinary.createUploadWidget(data, callback))
          // js isn't embeded -> embed js file and wait for it to load
          : fromEvent(this.addJsToElement(widgetUrl), 'load').pipe(
            // map to call of js function
            map(e => cloudinary.createUploadWidget(data, callback))
          );
      }
    
      // check if js file is already embeded
      private skriptExists(jsUrl: string): boolean {
        return document.querySelector(`script[src="${jsUrl}"]`) ? true : false;
      }
    
      // embed external js file in html
      private addJsToElement(jsUrl: string): HTMLScriptElement {
        const script = document.createElement('script');
        script.type = 'text/javascript';
        script.src = jsUrl;
        this.renderer.appendChild(document.body, script);
        return script;
      }
    }
    

    使用组件中的服务来创建小部件:

    export class AppComponent implements OnInit {
    
      widget: any;
    
      constructor(private cloudinary: CloudinaryService) { }
    
      ngOnInit() {
         this.cloudinary.createUploadWidget(
          {
            cloudName: 'my_cloud_name',
            uploadPreset: 'my_preset'
          },
          (error, result) => {
            if (!error && result && result.event === "success") {
              console.log('Done! Here is the image info: ', result.info);
            }
          }
        ).subscribe(widget => this.widget = widget);
      }
    
      openWidget() {
        if (this.widget) {
          console.log('open')
          this.widget.open();
        }
      }
    }
    

    【讨论】:

    • 谢谢!我以前与 CORS 合作过,并认为我了解所有这些。他们的服务器似乎允许有限的请求。我正在通过 html 脚本设置获取信息,但这对我来说似乎很古怪。看来我应该只用脚本创建一个对象,然后用代码从那里开始。误导的是脚本下载,我可以在开发工具中看到它,所以服务器似乎没有问题。我可以看到我的实现是。
    • @Preston 如果您的整个应用程序都在使用 Cloudinary 小部件,并且您不介意在启动时加载 js,您显然可以将&lt;script&gt; 标签添加到您的index.html。我假设您希望在初始化特定组件时根据特定请求动态加载脚本,这就是我回答中的 Service 所做的。
    • 是的,你是对的。只有几个组件需要小部件。它是应用程序的一小部分。再次感谢您,我相信这个 Angular / Cloudinary 代码将来会对许多编码人员有用。网上没有这样的东西。
    猜你喜欢
    • 1970-01-01
    • 2011-04-21
    • 2014-11-07
    • 1970-01-01
    • 2022-01-20
    • 2013-09-15
    • 2016-04-07
    • 2021-12-18
    • 1970-01-01
    相关资源
    最近更新 更多