Отправка HttpsException в приложение Android в обратном вызове request.post в облачной функции https.onCall - PullRequest
0 голосов
/ 18 января 2020

Я определил https.onCall Облачную функцию, в которой я вызываю request.post (где request = require('request')). В обратном вызове function(error, response, body) я хочу обнаружить два варианта использования: а) есть ли ошибка? Если да, я должен отправить в приложение Android functions.https.HttpsError; б) нет ошибки вообще? Если да, я должен отправить в приложение Android JSON объект, содержащий сообщение об успехе.

Код, который я написал, показан ниже. Это не работает: Android не выполняет блок ниже условия if(!task.isSuccessful()) (см. Часть "Android код стороны приложения")

/**
 * Verifies a Recaptcha filled by the user in his Android app.
 * 1. Success: returns the JSON response
 * 2. Failure: throws the error
 **/
exports.verifyRecaptcha = functions.https.onCall((data, context) => {

    const user_response_token = data.userResponseToken;
    if(user_response_token === null || user_response_token === '') {
        throw new functions.https.HttpsError('invalid-argument', 'The function must be called with an adequat user response token.');
    }

    const remote_url = 'https://recaptcha.google.com/recaptcha/api/siteverify';  // Original value: 'https://www.google.com/recaptcha/api/siteverify';  #  Moises' value: https://recaptcha.google.com/recaptcha/api/siteverify
    const secret = '<Secret_Key>';
    request2.post({url: remote_url, form:{secret: secret, response: user_response_token}}, function(error, response, body) {
        if(error) {
            console.log("error");
            console.log(error);
            throw new functions.https.HttpsError('unknown', error);
        }

        if(response.statusCode !== 200) {
            console.log("error status code");
            console.log(response.statusCode);
            throw new functions.https.HttpsError('unknown', 'Something went wrong. Status code: ' + response.statusCode + '.');
        }

        body_in_json = JSON.parse(body);
        if(!body_in_json.success) {
            console.log("error body");
            console.log(body);
            throw new functions.https.HttpsError('unknown', 'Unable to verify this captcha.');
        }

        return {code: 'Success', message: 'You are actually a human (this msg is for test purposes).'};
    });
});

Важное примечание: эти 4 строки работают

Четыре первые строки, переписанные ниже, на самом деле работают (если я добавлю условие 1 === 1, исключение действительно будет брошено в приложение Android, которое фактически его ловит, оно работает отлично). Так что проблема напрямую связана с тем, как я реализовал обратный вызов request.post.

const user_response_token = data.userResponseToken;
if(user_response_token === null || user_response_token === '') {
    throw new functions.https.HttpsError('invalid-argument', 'The function must be called with an adequat user response token.');
}

То, что я пытался сделать

  1. Возвращение обещания из request.post, но я знаю, что это обещание не должно быть возвращено, потому что оно делает Cloud Function cra sh с чем-то вроде "максимальная глубина стека вызовов достигнута", нормально.

  2. Возвращая functions.https.HttpsError вместо того, чтобы бросать их, но это не решило проблему

Android код стороны приложения

final SafetyNetApi.RecaptchaTokenResponse response = task.getResult();
assert response != null;
final String userResponseToken = response.getTokenResult();
if (!userResponseToken.isEmpty()) {
    final HashMap<String, String> the_data = new HashMap<>();
    the_data.put("userResponseToken", userResponseToken);
    FirebaseFunctions.getInstance()
            .getHttpsCallable("verifyRecaptcha")
            .call(the_data)
            .continueWith(new Continuation<HttpsCallableResult, Void>() {
                @Override
                public Void then(@NonNull final Task<HttpsCallableResult> task) {
                    if(context.isDestroyed() || context.isFinishing()) {
                        return null;
                    }

                    if(!task.isSuccessful()) {
                        Exception e = task.getException();
                        if (e instanceof FirebaseFunctionsException) {
                            FirebaseFunctionsException ffe = (FirebaseFunctionsException) e;
                            System.out.println(ffe.getMessage());
                        }

                        return null;
                    }

                    callback.onAsking();
                    return null;
                }
            });
} else {
    callback.onFailureUserResponseTokenIsEmpty();
}

1 Ответ

1 голос
/ 18 января 2020

request изначально поддерживает интерфейсы обратного вызова, но не возвращает обещание, что вы должны делать в облачной функции, когда вы имеете дело с асинхронными операциями, такими как вызов API (подробности смотрите в этом официальном видеоролике: https://firebase.google.com/docs/functions/video-series/ (в частности, 3 видео под названием «Learn JavaScript Promises»).

Поэтому вы можете использовать request-promise и метод rp(), который «возвращает обычное обещание Promises / A +, соответствующее обещанию», а затем делает что-то вроде:

  exports.verifyRecaptcha = functions.https.onCall((data, context) => {

      const user_response_token = data.userResponseToken;
      if(user_response_token === null || user_response_token === '') {
          throw new functions.https.HttpsError('invalid-argument', 'The function must be called with an adequat user response token.');
      }

      const remote_url = 'https://recaptcha.google.com/recaptcha/api/siteverify';

   // Original value: 'https://www.google.com/recaptcha/api/siteverify';  #  Moises' value: https://recaptcha.google.com/recaptcha/api/siteverify
      const secret = '<Secret_Key>';

      var options = {
          method: 'POST',
          uri: remote_url,
          body: {secret: secret, response: user_response_token},
          json: true // Automatically stringifies the body to JSON
      };



      return rp(options)
        .then(parsedBody => {
            return {code: 'Success', message: 'You are actually a human (this msg is for test purposes).'};
        })
        .catch(error => {
             throw new functions.https.HttpsError('....', error);
             //To enrich if necessary with differnt cases
        });

  });
...