How to catch UrlFetchApp.fetch exception - google-apps-script

Is there any way to catch the exception from UrlFetchApp.fetch?
I thought I can use response.getResponseCode() to check the response code, but I'm not able to, for e.g when there is 404 error, the script not continue and just stop at UrlFetchApp.fetch

Edit: This parameter is now documented here.
You can use the undocumented advanced option "muteHttpExceptions" to disable exceptions when a non-200 status code is returned, and then inspect the status code of the response. More information and an example is available on this issue.

The trick is passing the muteHttpExceptions param of UrlFetchApp.fetch().
Here an example (untested):
var payload = {"value": "key"}
var response = UrlFetchApp.fetch(
url,
{
method: "PUT",
contentType: "application/json",
payload: JSON.stringify(payload),
muteHttpExceptions: true,
}
);
var responseCode = response.getResponseCode()
var responseBody = response.getContentText()
if (responseCode === 200) {
var responseJson = JSON.parse(responseBody)
// ...
} else {
Logger.log(Utilities.formatString("Request failed. Expected 200, got %d: %s", responseCode, responseBody))
// ...
}
For some reason if the URL is not available (e.g. the service you're trying to use is down) it still looks like is throwing an error so you may still need to use a try/catch block.

why don't you use try catch and handle the error in catch block
try{
//Your original code, UrlFetch etc
}
catch(e){
// Logger.log(e);
//Handle error e here
// Parse e to get the response code
}

You can manually parse the caught error, but it's not recommended. When catching the exception (that is being thrown in case muteHttpExceptions is turned off), the error object would be in the following format:
{
"message": "Request failed for ___ returned code___. Truncated server response: {___SERVER_RESPONSE_OBJECT___} (use muteHttpExceptions option to examine full response)",
"name": "Exception",
"fileName": "___FILE_NAME___",
"lineNumber": ___LINE_NUMBER___,
"stack": "___STACK_DETAILS___"
}
If you for some reason prefer not using muteHttpExceptions, you could catch the exception e, look at e.message, substring the text between "Truncated server response: " and " (use muteHttpExceptions option to examine full response)", JSON.parse() it, and the returned object would be the error returned from the api call.
I wouldn't suggest it over muteHttpExceptions, just wanted to show the best way to get the error object this way.
Anyways, try-catch your UrlFetchApp.fetch() call to make sure you catch the unhandled exceptions, like 404.

Related

Is there a way to have Nest use the ExceptionsHandler error message as the API response message when an exception is thrown?

When an error is thrown within my Nest API, often times the error that is thrown is not an HttpException, and therefore the standard Nest API response that I see from the client's side is:
{
"statusCode": 500,
"message": "Internal server error"
}
But when I look at the console log, there is a much more descriptive error message, for example:
[Nest] 18476 - 06/23/2022, 2:28:07 PM ERROR [ExceptionsHandler] Cannot perform update query
because update values are not defined.
UpdateValuesMissingError: Cannot perform update query because update values are not defined.
Is there a way I can route this message to be the in the response body of the response given back to the API client?
You can use Global Filter in NestJs to catch error and then throw a good error.
For exemple, you can create a Exception Filter
// UpdateValuesMissingError.exception-filter.ts
import { ArgumentsHost, Catch, ExceptionFilter } from '#nestjs/common';
import { Request, Response } from 'express';
#Catch(UpdateValuesMissingError)
export class UpdateValuesMissingErrorFilter implements ExceptionFilter {
catch(exception: UpdateValuesMissingError, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
console.log(exception);
response.status(400).json({
statusCode: 400,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
and in your main.ts add this
app.useGlobalFilters(new HttpExceptionFilter());
And now in response.status(400).json({}) you can add everything you want in your body. Like the error message of Axios.

getResponseCode() method doesn't return expected response code

I have 2 questions about Google App Script Services getResponseCode() method.
1) "Unexpected Error"
When I run the getResponseCode() method, I got "Unexpected Error...".
https://developers.google.com/apps-script/reference/url-fetch/http-response#getResponseCode()
var response = UrlFetchApp.fetch(url, {muteHttpExceptions: true});
responseCode = response.getResponseCode();
Unexpected error: https://www.example.com/
※I can't tell the url for business reasons.
HTTP response status codes don't include "Unexpected Error".
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
Please tell me what response codes actually return, when this error occurs?
2) getResponseCode() method didn't work as expected
When I run the code below, I got "200".
I expected "301" in response to the "http://google.com/" request.
function myFunction() {
var response = UrlFetchApp.fetch("http://google.com/");
 Logger.log(response.getResponseCode());
}
I think getResponseCode() method doesn't return actual http status codes.
Please tell me why I got "200" instead of "301".
get 301 response in browser
get 200 response instead of 301
This happens because the request is following the redirect. Take a look at the available parameters in the UrlFetchApp.fetch() method. You'll see followRedirects, which defaults to true.
Make this small change and you'll get the expected 301.
function myFunction() {
var response = UrlFetchApp.fetch("http://google.com/", { followRedirects: false });
 Logger.log(response.getResponseCode());
}

HTTP failure callback for UrlFetchApp.fetch() in gmail addon

I would like to write a callback when a HTTP requests fails. How do I chain it to UrlFetchApp.fetch()?
Please refer to the HTTP request below.
// Make a GET request.
UrlFetchApp.fetch('http://www.google.com/');
Please note that the fetch function is synchronous. It does not provide a callback parameter and does not return a promise.
An approach to catching exceptions is possible through the UrlFetchApp.fetch(url, params) function. You can pass the muteHttpExceptions parameter into the params location of the function call. That way you can inspect the response code yourself and respond appropriately. Documentation: https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app#fetchurl-params
UrlFetchApp.fetch('http://www.google.com/', {muteHttpExceptions: true});
muteHttpExceptions (Boolean) if this is set to true, the fetch will not
throw an exception if the response code indicates failure, and will
instead return the HTTPResponse (default: false)
An alternative would be a simple try/catch statement. I imagine you could log the error or respond appropriately.
try {
UrlFetchApp.fetch('http://www.google.com/');
}
catch(e) {
// e contains error response
}

NodeJS request-json setting header breaks

I'm using request-json, found on npm, to query my API server. My server requires that an auth token be passed in the header, but using request-json's method of setting headers produces the following error:
Error: "name" and "value" are required for setHeader().
at ClientRequest.OutgoingMessage.setHeader (_http_outgoing.js:333:11)
at new ClientRequest (_http_client.js:101:14)
at Object.exports.request (http.js:49:10)
at Request.start (C:\Users\Michael\Desktop\FrescoWeb\node_modules\request-js
on\node_modules\request\request.js:904:30)
at Request.write (C:\Users\Michael\Desktop\FrescoWeb\node_modules\request-js
on\node_modules\request\request.js:1625:10)
at end (C:\Users\Michael\Desktop\FrescoWeb\node_modules\request-json\node_mo
dules\request\request.js:666:16)
at Immediate._onImmediate (C:\Users\Michael\Desktop\FrescoWeb\node_modules\r
equest-json\node_modules\request\request.js:690:7)
at processImmediate [as _immediateCallback] (timers.js:358:17)
I'm setting the header and hitting the endpoint in the following code:
var api = requestJson.createClient(config.API_URL);
api.headers['authtoken'] = req.session.token;
api.post(
'/v1/outlet/update',
params,
function(error, response, body){
if (error)
return res.json({err: error}).end();
if (!body)
return res.json({err: 'ERR_MISSING_BODY'}).end();
if (body.err)
return res.json({err: body.err}).end();
req.session.user.outlet = body.data;
req.session.save(function(){
res.json({}).end();
});
}
);
When I comment api.headers['authtoken'] = req.session.token; out, the call doesn't crash. Is there something that I am doing wrong, or do I have to migrate to request for http requests?
req.session.token should be req.session.user.token

Uncaught SyntaxError: Unexpected token B on live but not local server

So i am making some ajax post and it seems to work fine on the localhost, but when I publish it to ec2 server on amazon, I get Uncaught SyntaxError: Unexpected token B. Which seems to point to JSON parsing failure. Exact same database, same browser, and same methods being called. Why would it work on local and not on the server.
$.ajax({
url: '#Url.Action("Action")',
type: "POST",
data: ko.toJSON(viewModel),
dataType: "json",
contentType: "application/json; charset:utf-8",
success: function (result) {
},
error: function (xhr, textStatus, errorThrown) {
var errorData = $.parseJSON(xhr.responseText);
var errorMessages = [];
for (var key in errorData)
{
errorMessages.push(errorData[key]);
}
toastr.error(errorMessages.join("<br />"), 'Uh oh');
}
});
Here is the basic layout on the server side:
[HttpPost]
public JsonResult Action(ViewModel model)
{
try
{
Response.StatusCode = (int)HttpStatusCode.OK;
return Json("Successfull");
}
catch (Exception ex)
{
logger.Log(LogLevel.Error, string.Format("{0} \n {1}", ex.Message, ex.StackTrace));
Response.StatusCode = (int)HttpStatusCode.BadRequest;
List<string> errors = new List<string>();
errors.Add(ex.Message);
return Json(errors);
}
}
Within the try statement, I do a couple of queries to the database and post some calculations on Authorize.Net (https://api.authorize.net/soap/v1/Service.asmx)
If there are any error with Authorize.net web service calls then I return errors like this:
if (profile.resultCode == MessageTypeEnum.Error)
{
logger.Log(LogLevel.Error, string.Join(",", profile.messages.Select(x => x.text)));
Response.StatusCode = (int)HttpStatusCode.BadRequest;
List<string> errors = new List<string>();
profile.messages.ToList().ForEach(x => errors.Add(x.text));
db.SaveChanges();
return Json(errors);
}
This error that I am logging:
A public action method 'AddPromoCode' was not found on controller 'Flazingo.Controllers.PositionController'. at
System.Web.Mvc.Controller.HandleUnknownAction(String actionName) at
System.Web.Mvc.Controller.ExecuteCore() at
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) at
System.Web.Mvc.MvcHandler.<>c__DisplayClass6.<>c__DisplayClassb.b__5() at
System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass1.b__0() at
System.Web.Mvc.MvcHandler.<>c__DisplayClasse.b__d() at
System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean&
completedSynchronously)
You have another post at can't find action only on live server, works fine in local server, so I'm guessing that this post is specifically related to the javascript pieces, not the server-side pieces.
It sounds like something bad happens on the server, the server sends back some type of error, and the your error handler (in javascript) dies when trying to handle that response.
I get Uncaught SyntaxError: Unexpected token B. Which seems to point
to JSON parsing failure.
That sounds quite reasonable. Let's look at the code:
.ajax({
...
error: function (xhr, textStatus, errorThrown) {
var errorData = $.parseJSON(xhr.responseText);
var errorMessages = [];
...
},
...
});
I would highly recommend taking a look at what xhr.responseText is. My guess it that it does not contain valid JSON, so the parseJSON method throws the 'Unexpected token B' error.
To look at this value, you could put console.log(xhr.responseText); or you could use a tool like the javascript debugger in your web browser or fiddler to see what is there.
My guess is that the server is sending back a string with something like There was an error on the server instead of JSON like you are expecting. I see that you have error handling built in - my guess is that there is an error within your error handling, and there is nothing to catch it. I would recommend doing debugging on the server side to see if there is an error somewhere that you are not expecting.
Perhaps profile.messages is something that can only be enumerated once, and when you try to do it again it throws an error. Or maybe DB.SaveChanges is throwing an error for some reason. Either of these would result in the logged message that you see with the behavior you see on the client side.
You are attempting to return a 400 response (Bad Request) with your own custom response content.
I think that IIS by default doesn't allow you to do this, and as CodeThug mentioned, may be replacing your custom JSON content with a server message.
But it appears that you can override this behaviour:
http://develoq.net/2011/returning-a-body-content-with-400-http-status-code/
<system.webServer>
<httpErrors existingResponse="PassThrough"></httpErrors>
</system.webServer>
I have received similar mysterious errors in the past when using ASP.NET script bundling on knockout and bootstrap, especially when including the already-minified versions in a bundle.
If you are running in DEBUG mode on localhost, then ASP.NET will not be minifying the javascript libraries. However, once you deploy, you are presumably no longer in DEBUG mode and now minifying/bundling the scripts. Sometimes the bundling/minification of these scripts can result in syntax errors similar to the one you posted.
If so, you may be able to load knockout from a CDN to avoid the need for bundling.
It seems JSON sending as the response from the server is badly generated
ex: if a value in the database is hi "my" friends
JSON file will be generated as text:"hi "my" friends"
so value for property text is badly generated.
double check values in production/development server for such values.
best practice is replace quotes with escape character
ex: text:"hi \"my\" friends"