在TypeScript中重置Google reCaptcha

时间:2016-12-15 13:56:56

标签: angular recaptcha typescript2.0

我正在开发Angular 2 + TypeScript应用。 如果表单无效,我需要重置reCaptcha。 但是当我写grecaptcha.reset();时,在我的打字稿代码中,我收到错误:Can not resolve symbol 'grecaptcha'

如何重置reCaptcha或解析/声明grecaptcha变量以及在哪里?

2 个答案:

答案 0 :(得分:2)

对于Typescript,声明grecaptcha

declare var grecaptcha: any;

这假设是reCAPTCHA api has been included

<script src='https://www.google.com/recaptcha/api.js?render=explicit'></script>

或者,grecaptcha可以输入如下:

declare var grecaptcha: ReCAPTCHA;

ReCAPTCHA界面

import { ElementRef } from '@angular/core';

/**
 * Interface for Google's reCAPTCHA JavaScript API. 
 * 
 * Display API
 * @see {@link https://developers.google.com/recaptcha/docs/display}
 * 
 * Invisible API
 * @see {@link https://developers.google.com/recaptcha/docs/invisible}
 */
export interface ReCAPTCHA {

  /**
   * Programatically invoke the reCAPTCHA check. Used if the invisible reCAPTCHA is on a div 
   * instead of a button.
   * 
   * @param {string} opt_widget_id Optional widget ID, defaults to the first widget created if 
   *     unspecified.
   */
  execute(opt_widget_id?: string): void;

  /** 
   * Renders the container as a reCAPTCHA widget and returns the ID of the newly created widget.
   * 
   * @param {ElementRef|string} container The HTML element to render the reCAPTCHA widget.  Specify 
   *    either the ID of the container (string) or the DOM element itself. 
   * @param {Object} parameters An object containing parameters as key=value pairs, for example,
   *    {"sitekey": "your_site_key", "theme": "light"}.
   */
  render(container: ElementRef|string, parameters: {[key: string]: string}): void;

  /** 
   * Resets the reCAPTCHA widget.
   * 
   * @param {string} opt_widget_id Optional widget ID, defaults to the first widget created if 
   *     unspecified.
   */
  reset(opt_widget_id?: string): void;

  /** 
   * Gets the response for the reCAPTCHA widget. Returns a null if reCaptcha is not validated. 
   * 
   * @param {string} opt_widget_id Optional widget ID, defaults to the first widget created if 
   *     unspecified.
   */
  getResponse(opt_widget_id?: string): string;
}

实施例

以下代码是大纲,不显示服务器端ReCAPTCHA验证。

ReCAPTCHA服务

import { Injectable, OnInit } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs';

export interface ReCAPTCHAResponse {
  success: boolean; 
  status_code: number,
  error_codes?: Array<string>;
}

@Injectable()
export class ReCAPTCHAService {
  public recaptchaResponse$: Observable<ReCAPTCHAResponse>;

  public constructor(private http: Http) {}

  public verifyUserResponse(userResponseToken: string): Observable<ReCAPTCHAResponse> {
    let headers = new Headers({ 'Content-Type': 'application/json' });
    let options = new RequestOptions({ headers: headers });

    return this.http.post('/auth/captcha', {'g-recaptcha-response': userResponseToken}, options)
        .map( (res: Response) => this.extractData(res))
        .catch( (error: Response | any) => this.handleError(error));
  }


  private extractData(res: Response): ReCAPTCHAResponse {
    const recaptchaResponse: ReCAPTCHAResponse = res.json();
    return recaptchaResponse;
  }


  private handleError (error: Response | any): Observable<ReCAPTCHAResponse> {
    let errMsg: string;
    if (error instanceof Response) {
      let body = error.json() || '';
      let err = body.error || JSON.stringify(body);
      errMsg = error.status + ' - ' + (error.statusText || '') + ': ' + err;
    } else {
      errMsg = error.message ? error.message : error.toString();
    }

    return Observable.throw({ success: false, status_code: 0, error_codes: [errMsg]});
  }
}

组件

import { Component, OnDestroy, OnInit } from '@angular/core';
import { ReCAPTCHA } from './recaptcha';
import { ReCAPTCHAResponse, ReCAPTCHAService } from './recaptcha.service';

declare var grecaptcha: ReCAPTCHA;
declare var window: any;

@Component ({
  moduleId: module.id,
  selector: 'create-account',
  templateUrl: 'create-account.component.html'
})
export class CreateAccountComponent implements OnDestroy, OnInit {

  public constructor(private recaptcha: ReCAPTCHAService, private formBuilder: FormBuilder) {}

  public ngOnInit(): void {
    this.buildForms();

    // Export reCAPTCHACallback to global scope.
    window['reCAPTCHACallback'] = this.reCAPTCHACallback.bind(this);

    grecaptcha.render('create-account-captcha', {
      'sitekey': 'your-site-key',
      'size': 'invisible',        // Optional (see docs)
      'callback': 'reCAPTCHACallback'
    });
  }

  /**
   * Verify reCAPTCHA response on server. 
   */
  public reCAPTCHACallback(token: string) {

    if (token == null /* checks for undefined or null */ || token.length === 0) {
      grecaptcha.reset();
      // TODO: report that captcha was invalid
    } else {
      let response$: Observable<ReCAPTCHAResponse>; 
      response$ = this.recaptcha.verifyUserResponse(token);
      response$.subscribe( r => {
        if (r.success) {
          // TODO: create the new user account
        } else {
          grecaptcha.reset();
          // TODO: report that captcha was invalid
        }
      },
      (error: any) => {
        // TODO: handle server error
      });
    }
  }

  private buildForms(): void {
    // TODO
  }
}

答案 1 :(得分:0)

对于一个非常简单的问题,上面的答案有些冗长。

如上面的答案所示,与其声明一个全局变量,不如在窗口对象(它实际驻留的位置)上定义它更干净:

在您的根文件(例如index.tsx)中,执行以下操作:

declare global {
  interface Window {
    grecaptcha: any
  }
}

开始在其他代码中将其用作window.grecaptcha