Cloud Functions for Firebase: 'Error: could not handle the request'
Asked Answered
S

5

27

I feel like pulling my hair out; this is either super simple and i'm having brain freeze or it is not that simple.

What I want

I am trying to unshorten a shortened URL using firebase, when a user goes to:
myapp.firebaseappurl.com/url/SHORTENEDLINK
SO wont let me add a shortened URL

I would like the output to be:

{
  "url": "https://mcmap.net/q/535015/-sphinx-search-how-to-use-an-empty-before_match-and-after_match"
}

What I have tried

firebase.json file:

{
  "hosting": {
    "public": "public",
    "rewrites": [ {
    "source": "/url/:item",
      "destination": "/url/:item"
    } ]
  }
}

index.js file:

const functions = require('firebase-functions');

exports.url = functions.https.onRequest((requested, response) => {

    var uri = requested.url;
    request({
        uri: uri,
        followRedirect: true
      },
      function(err, httpResponse) {
        if (err) {
          return console.error(err);
        }
        response.send(httpResponse.headers.location || uri);
      }
    );

});

Result

When I go to myapp.firebaseappurl.com/url/SHORTENEDLINK I get the following:

Error: could not handle the request
Snarl answered 9/8, 2017 at 20:46 Comment(9)
add method : 'POST' to the requestParanoiac
Why? The passed parameter is in the URL, so it's a GET request, right?Snarl
@mohamadrabee - Also, just tried it to see if that was it. Nope. Didnt work.Snarl
sorry my mistakeParanoiac
can you share your package.json? did you have 'request' in the dependenciesParanoiac
request is not on my package.json file but I did npm install request in the functions folder and it is there :)Snarl
You can't use express-style ":wildcard" notation in rewrites. Rewrites can only be paths and ** wildcards. You can create an express app in Cloud Functions, and have that use :wildcard.Accord
@DougStevenson Hi Doug. I am afraid I am a little confused. Please could you show me an example of what I need with code? Thank you for your time. It it greatly appreciated :)Snarl
@Snarl were you able to solve your issue? If so it is recommended to upvote the solution that helped you, or post your answer as the solution here to better help the community.Luck
J
20

You are seeing Error: could not handle the request since there probably was an exception and it timed out.

Check your logs using:

firebase functions:log

Refer docs for more details

Here's how I got URL unshortening to work

const functions = require('firebase-functions');

const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);

const http = require('http');
const urlP = require('url');

const unshorten = (url, cb) => {
  const _r = http.request(
    Object.assign(
      {},
      urlP.parse(url),
      {
        method: 'HEAD',
      }
    ),
    function(response) {
      cb(null, response.headers.location || url);
    }
  );
  _r.on('error', cb);
  _r.end();
};

const resolveShortUrl = (uri, cb) => {
  unshorten(uri, (err, longUrl) => {
    if (longUrl === uri) {
      cb(null, longUrl);
    } else {
      resolveShortUrl(longUrl, cb);
    }
  });
};

exports.url = functions.https.onRequest((requested, response) => {
  var uri = requested.query.url;
  resolveShortUrl(uri, (err, url) => {
    if (err) {
      // handle err
    } else {
      response.send({ url });
    }
  });
});

You can follow the hello world example straight away and use the above code as your function.

Above code uses HEAD requests to peek into 'Location` field of the headers and decides if the url can be further unshortened.

This is lighter as HEAD requests ask for no body (thereby avoiding body parsing). Also, no third party lib required!

Also note that the url passed as a query param. So the request would be

http://<your_firebase_server>/url?url=<short_url>

Saves you the trouble of URL re-writes. Plus semantically makes a little more sense.

Johniejohnna answered 13/8, 2017 at 13:10 Comment(3)
I thought a rewrite is needed to tell my firebase hosting which function to run based on which url?Snarl
Try this us-central1-helloworld-9d223.cloudfunctions.net/… url>Johniejohnna
@Snarl Did you get a chance to try the solution?Johniejohnna
D
3

Did you tried using { source: '/url/**' } syntax?

You can use something like this;

{
  "hosting": {
    "public": "public",
    "rewrites": [ {
    "source": "/url/**",
    "function": "/url"
    }]
  }
}

and then you can parse the url from the request.

 exports.url = functions.https.onRequest((req, res) => { 
   // parse the url from the req and redirect to the correct link
 });
Dempster answered 28/8, 2017 at 9:51 Comment(0)
C
2

You should try this in the firebase.json, its worked for me:

"source": "/**",

I also tried "source": "/url/**" but its not worked.

Cronk answered 16/3, 2019 at 0:21 Comment(0)
A
0

I think your code is fine. What you're doing incorrectly is that you're using Express-js notations in your firebase.json's rewrites node. (the :item part). These don't work in the Firebase Realtime Database.

So, instead of doing that, change your firebase.json to the following :-

 {
  "hosting": {
    "public": "public",
    "rewrites":  {
    "source": "YOUR SHORTENED URL",
    "destination": "YOUR ORIGINAL URL"
  } 
  }
}

This is also the advocated approach in the Cloud Functions for Firebase's URL Shortener sample.

Acquiescence answered 12/8, 2017 at 21:18 Comment(3)
This will not be dynamic. I want to be able to pass to the function the url as a parameter. Any thoughts?Snarl
@Rohan The sample is meant only for goo.gl urls. Besides, url resolution can be done using HEAD requests which are more light weight compared to GETJohniejohnna
@Snarl Yes, it won't be dynamic. But you can simply save the original URL in your specified location whenever you shorten it. And for accessing the original URL, you can just add code inside your Cloud functions file to read it from the specified location during the HTTP trigger and use it as the URL for the your response. This of course won't be dynamic. But, you won't have to unshorten the url on the fly and you also won't have to include a third party application.Acquiescence
S
0

First make sure you are receiving the request properly with the shortened url.

const functions = require('firebase-functions');

const express = require('express');
var express_app = express();
express_app.use(body_parser.text({type: ()=>true}));
express_app.all('*', (req, res) => {
    console.log(req.path);
    res.send(JSON.stringify(req.path));
});
exports.url = functions.https.onRequest(express_app);

Now when you visit myapp.firebaseappurl.com/url/SHORTENEDLINK you should see the SHORTENEDLINK in plain text. When that's working, try the redirect.

const functions = require('firebase-functions');
const express = require('express');
const request = require('request');
var express_app = express();
express_app.use(body_parser.text({type: ()=>true}));
express_app.all('*', (req, res) => {
    var url = req.path;
    request({
        uri: uri,
        followRedirect: true
      },
      function(err, httpResponse) {
        if (err) {
          return console.error(err);
        }
        res.send(httpResponse.headers.location || uri);
      }
    );
});
exports.url = functions.https.onRequest(express_app);

Also it's good practice to npm install with --save so they end up in the packages.json. While firebase copies your node_modules folder, most other SaaS platforms run npm install.

Sparling answered 18/8, 2017 at 12:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.