I am facing an issue with a react native app in production. Some request fails and other go through. The issue happens to a lot of users and it's random. Only happens to our ios app and not our android app. The device has internet because we have #react-native-community/netinfo logging every time the network changes and in almost all the cases is not logging any change (there are some times that the user losses internet in which case it's fine the network error).
We use sentry to log app errors. In the image below you can see the device makes a post request to our API and very quickly it goes to response interceptor error.
In the tags image you can see that the problem only occurs on release 5.3.3 but I don't know what is causing this from this release. I have not changed anything from axios request.
Since I don't have a reputation of 10 I cannot post images but here are the links.
Network Issue Report: https://i.postimg.cc/PJWjvCvt/Screen-Shot-2022-06-16-at-11-25-06-AM.png
Image of sentry tags: https://i.postimg.cc/Y0NSBr7s/Screen-Shot-2022-06-16-at-11-43-37-AM.png
This is our axios interceptor:
const axiosInstance = axios.create({
baseURL: ENV.API_URL,
headers: {
Accept: "application/json",
"Content-Type": "application/json"
}
});
axiosInstance.interceptors.request.use(async function (config) {
config.headers['device-app-version'] = DeviceInfo.getVersion();
try {
const token = await AsyncStorage.getItem('token');
if (token) {
config.headers['authorization'] = "JWT " + token;
}
} catch (error) {
console.log("ERROR GET ASYNCSTORAGE DATA FAILED ON TOKEN", error);
}
const httpMetric = firebase.perf().newHttpMetric(config.url, config.method.toUpperCase());
config.metadata = { httpMetric };
await httpMetric.start();
return config;
}, function (error) {
console.log("Request interceptor error");
console.log(error);
return Promise.reject(error);
});
axiosInstance.interceptors.response.use(async function (response) {
const { httpMetric } = response.config.metadata;
if (response.status != 200) {
Sentry.withScope(function(scope) {
scope.setTag('API', response.config.url);
scope.setExtra('API', response.config.url);
scope.setFingerprint(['API', response.config.url]);
Sentry.captureException(response);
});
}
httpMetric.setHttpResponseCode(response.status);
httpMetric.setResponseContentType(response.headers['content-type']);
await httpMetric.stop();
return response;
}, async function (error) {
const { httpMetric } = error.config.metadata;
console.log("Response interceptor error");
console.log(error);
console.log("error.response");
console.log(error.response);
const scope = new Sentry.Scope();
if (error.response) {
// Request made and server responded
console.log(error.response.data);
console.log(error.response.headers);
console.log("Status:", error.response.status);
if (error.response.status == 403) {
return Promise.reject(error);
}
scope.setTag('API', error.response.config.url);
scope.setExtra('API', error.response.config.url);
scope.setFingerprint(['API', error.response.config.url]);
Sentry.captureException(error.response, scope);
} else if (error.request) {
// The request was made but no response was received
console.log(error.request);
Sentry.captureException(error.request, scope);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
Sentry.captureException(error.message, scope);
}
httpMetric.setHttpResponseCode(error.response.status);
httpMetric.setResponseContentType(error.response.headers['content-type']);
await httpMetric.stop();
if (!error.response) {
error.response = {
data: [],
status: 401,
message: 'Error ocurred making request. Try again'
}
}
return Promise.reject(error);
},
);
Package.json (affected packages)
"axios": "^0.26.1",
"react": "17.0.2",
"react-native": "0.66.1",
Related
I'm using Expo's bare-workflow for my react-native project. I'm using expo's push notification service. I keep getting the following error whenever I try to get my expo push token:
[expo-notifications] Error encountered while updating the device push token with the server: {"error":"invalid_token","error_description":"The bearer token is invalid"}
I'm running the app directly on my device so should be able to get notifications.
I'm using basically the same registerForPushNotificationsAsync() that is provided in the documentation.
import Constants from 'expo-constants';
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
export const registerForPushNotificationsAsync = async () => {
try {
if (Constants.isDevice) {
const experienceId = '#{username}/{slug}';
const {
status: existingStatus
} = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
return;
}
const token = (
await Notifications.getExpoPushTokenAsync({ experienceId })
).data;
console.log(' 🏷 🏷 Token :', token);
return token;
} else {
alert('Must use physical device for Push Notifications');
}
if (Platform.OS === 'android') {
Notifications.setNotificationChannelAsync('default', {
name: 'default',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C'
});
}
return undefined;
} catch (error) {
console.error('Error in registerForPushNotificationsAsync()', error);
return undefined;
}
};
Expo packages in package.json
"expo": "~40.0.0",
"expo-analytics": "^1.0.16",
"expo-app-loading": "^1.0.1",
"expo-font": "~8.4.0",
"expo-image-manipulator": "~8.4.0",
"expo-image-picker": "~9.2.0",
"expo-intent-launcher": "~8.4.0",
"expo-notifications": "~0.8.2",
"expo-splash-screen": "~0.8.0",
"expo-updates": "~0.4.0",
I can't see anything about setting a bearer token, so I'm unsure what it could be after or where to even set it assuming I was able to determine what bearer token it is after.
Does anyone know what might be causing the problem?
So we figured it out.
We were using fetch-token-intercept which was adding our bearer token to calls going to expo, which meant validation was failing.
We modified the function to exclude calls to expo from including our bearer token and now the token is being retrieved successfully.
I want to ask about how to send an event using firebase & electron.js. A friend of mine has a problem when using firebase analytics and electron that it seems the electron doesn't send any event to the debugger console. When I see the network it seems the function doesn't send anything but the text successfully go in console. can someone help me to figure it? any workaround way will do, since he said he try to implement the solution in this topic
firebase-analytics-log-event-not-working-in-production-build-of-electron
electron-google-analytics
this is the error I got when Try to use A solution in Point 2
For information, my friend used this for the boiler plate electron-react-boilerplate
The solution above still failed. Can someone help me to solve this?
EDIT 1:
As you can see in the image above, the first image is my friend's code when you run it, it will give a very basic example like in the image 2 with a button to send an event.
ah just for information He used this firebase package :
https://www.npmjs.com/package/firebase
You can intercept HTTP protocol and handle your static content though the provided methods, it would allow you to use http:// protocol for the content URLs. What should make Firebase Analytics work as provided in the first question.
References
Protocol interception documentation.
Example
This is an example of how you can serve local app as loaded by HTTP protocol and simulate regular browser work to use http protocol with bundled web application. This will allow you to add Firebase Analytics. It supports poorly HTTP data upload, but you can do it on your own depending on the goals.
index.js
const {app, BrowserWindow, protocol} = require('electron')
const http = require('http')
const {createReadStream, promises: fs} = require('fs')
const path = require('path')
const {PassThrough} = require('stream')
const mime = require('mime')
const MY_HOST = 'somehostname.example'
app.whenReady()
.then(async () => {
await protocol.interceptStreamProtocol('http', (request, callback) => {
const url = new URL(request.url)
const {hostname} = url
const isLocal = hostname === MY_HOST
if (isLocal) {
serveLocalSite({...request, url}, callback)
}
else {
serveRegularSite({...request, url}, callback)
}
})
const win = new BrowserWindow()
win.loadURL(`http://${MY_HOST}/index.html`)
})
.catch((error) => {
console.error(error)
app.exit(1)
})
async function serveLocalSite(request, callback) {
try {
const {pathname} = request.url
const filepath = path.join(__dirname, path.resolve('/', pathname))
const stat = await fs.stat(filepath)
if (stat.isFile() !== true) {
throw new Error('Not a file')
}
callback(
createResponse(
200,
{
'content-type': mime.getType(path.extname(pathname)),
'content-length': stat.size,
},
createReadStream(filepath)
)
)
}
catch (err) {
callback(
errorResponse(err)
)
}
}
function serveRegularSite(request, callback) {
try {
console.log(request)
const req = http.request({
url: request.url,
host: request.url.host,
port: request.url.port,
method: request.method,
headers: request.headers,
})
if (req.uploadData) {
req.write(request.uploadData.bytes)
}
req.on('error', (error) => {
callback(
errorResponse(error)
)
})
req.on('response', (res) => {
console.log(res.statusCode, res.headers)
callback(
createResponse(
res.statusCode,
res.headers,
res,
)
)
})
req.end()
}
catch (err) {
callback(
errorResponse(err)
)
}
}
function toStream(body) {
const stream = new PassThrough()
stream.write(body)
stream.end()
return stream
}
function errorResponse(error) {
return createResponse(
500,
{
'content-type': 'text/plain;charset=utf8',
},
error.stack
)
}
function createResponse(statusCode, headers, body) {
if ('content-length' in headers === false) {
headers['content-length'] = Buffer.byteLength(body)
}
return {
statusCode,
headers,
data: typeof body === 'object' ? body : toStream(body),
}
}
MY_HOST is any non-existent host (like something.example) or host that is controlled by admin (in my case it could be electron-app.rumk.in). This host will serve as replacement for localhost.
index.html
<html>
<body>
Hello
</body>
</html>
I'm trying to my expo/react-native project to send push notifications to my server. It works on standalone Android, but not stand alone iPhone.
The standalone iPhone app never sends the token.
Since the app sends nothing without error, I tried removing:
if (finalStatus !== 'granted') { return; }
This didn't work either.
export async function registerForPushNotificationsAsync(token) {
const { status: existingStatus } = await Permissions.getAsync(
Permissions.NOTIFICATIONS
);
let finalStatus = existingStatus;
// Only ask if permissions have not already been determined, for iOS.
if (existingStatus !== 'granted') {
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
finalStatus = status;
}
// Stop here if the user did not grant permissions
if (finalStatus !== 'granted') {
return;
}
// Get the push token that uniquely identifies this device
let expoToken = await Notifications.getExpoPushTokenAsync();
// Post new push token to backend for user
return axios({
method: 'POST',
url: `${str.ROOT_URL}/account/push/`,
headers: {
Authorization: `Token ${token}`
},
data: {
"token": expoToken,
"status": finalStatus
}
});
}
I expected the token to get sent to the backend, but nothing is sent on the standalone iOS app.
Please let me know if you know a workaround or had this issue before. Thanks!
I think it's too late to give an answer, but I spent 2 days to resolve it... I hope it helps somebody.
Instead this:
import * as Notifications from "expo-notifications";
Try this:
import { Notifications } from "expo";
I am using cordova-plugin-advanced-http plugin for API calling and all JSON enabled API working fine but I have one XML embedded API which is working fine in Postman but while I call it from ionic its param not getting at the server end.
Below is my code for XML API:
Type 1:
let headers = {
"Content-type": 'text/xml; charset=utf-8',
"Authorization": token,
};
let xmlBody =
'<ServiceRequest>' +
'<CaseNumber>' + caseNumber +
'</CaseNumber>' +
'</ServiceRequest>'
this.httpPlugin.setDataSerializer('utf8');
this.httpPlugin.post('https://test.com/Service', xmlBody, headers).then((response) => {
console.log("XML Response : ", JSON.stringify(response.data));
xml2js.parseString(response.data, function (err, result) {
console.log("XML parser success:", result);
console.log("XML parser error:", err);
if (result) {
resolve(result);
} else {
reject(err);
}
});
}).catch(error => {
if (error.status == 403) {
console.log("Token expired : " + JSON.stringify(error));
} else {
console.log("Error : " + error.error);
console.log("Error " + JSON.stringify(error));
reject(error);
}
});
Type 2:
let xmlBody = '<ServiceRequest>' +
'<CaseNumber>' + caseNumber +
'</CaseNumber>' +
'</ServiceRequest>';
console.log("XML Body", xmlBody)
// this.httpPlugin.setRequestTimeout(60);
this.httpPlugin.setDataSerializer('utf8');
this.httpPlugin.setHeader('*', 'authorization', token);
this.httpPlugin.setHeader('*', 'Content-Type', "application/x-www-form-urlencoded");
this.httpPlugin.post('https://test.com/Service', xmlBody, {}).then((response) => {
console.log("XML Response : ", JSON.stringify(response.data));
xml2js.parseString(response.data, function (err, result) {
console.log("XML parser success:", result);
console.log("XML parser error:", err);
if (result) {
resolve(result);
} else {
reject(err);
}
});
}).catch(error => {
if (error.status == 403) {
console.log("Token expired : " + JSON.stringify(error));
} else {
console.log("Error : " + error.error);
console.log("Error " + JSON.stringify(error));
reject(error);
}
});
All the time it's throwing errors from the server and with the same request, I am able to get data in postman as well as Native iOS code.
I referred this issue on GitHub but still no success.
Something I am missing though it's not able to get data on the server.
Help me to solve this issue.
After struggling a lot on this issue I found a solution to clean my request cookies.
In the HTTP Advanced plugin, there is one method to clear my cookies.
clearCookies()
Clear all cookies.
Use this method before calling any API.
So what it will do clear all my cookies and my issue related to old cookies will be solved in this way.
constructor(
public storage: Storage,
public httpPlugin: HTTP,
private platform: Platform
) {
// enable SSL pinning
httpPlugin.setSSLCertMode("pinned");
//Clear old cookies
httpPlugin.clearCookies();
}
The above code solves my issue.
Thanks all for your quick guidance and suggestions.
comment on this if this is not the right way to clear my old request data.
My Dart code send a form via post method to my GAE python server. I would like to see all headers return by the server in the response. but I can't perform that. Could you help?
void _httpRequest(url, datas){
HttpRequest request = new HttpRequest()
..open("POST", url, async: true)
..setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
..responseType = "";
// add an event handler that is called when the request finishes
request.onReadyStateChange.listen((_) {
if (request.status == 200) {
_onSuccess(request.responseText);
}else{
_handleTheError(request.responseText);
}
});
request.send(datas);
}
void _onSuccess(msg){
print("success : $msg");
}
void _handleTheError(msg){
print("error : $msg");
}
request.responseHeaders.forEach((k, v) {
print('Header: $k, value: $v');
});