Обработка ошибок при обработке данных с помощью значимых журналов
В последнее время я много работал над API и столкнулся с несколькими ошибками, когда искаженные данные, полученные из внешнего источника, вызывали ошибки в Node при попытке преобразовать данные на стороне сервера.
Задача: я хочу получить список местоположений на основе местоположения пользователя. Однако я хочу вернуть только подмножество на основе некоторых фильтров. Скажем, я получаю некоторые данные из источника данных, который я не контролирую (или не имею возможности применять значимые проверки). Например.
const retrievedLocations = [ { name: "Teahupo'o", coordinates: { latitude: 17.8471, longitude: 149.2664 } }, { name: "Pipeline", // coordinates are missing } ]
Теперь предположим, что я хочу отфильтровать эти данные только для местоположений, которые (ради этого примера) находятся в пределах 10 градусов от текущей широты/долготы моего пользователя. У меня может быть что-то вроде этого:
// Node Controller LocationsController.fetchLocations = async (req, res) => { const { userLat, userLong } = req.body; // first get the responses from our external service let locations; try { locations = await externalLocationsService.fetch(); } catch (e) { ... } // now filter that data, assume a function that does my check const filteredLocations = locations.filter(loc => { const userIsInLongRange = locationProximityCheck( userLong, loc.coordinates.longitude ); const userIsInLatRange = locationProximityCheck( userLat, loc.coordinates.latitude ); return userIsInLongRange && userIsInLatRange; }) res.status(200).json(filteredLocations) }
Что происходит, когда этот код запускается? Местоположение, в котором отсутствует объект координат, выдает ошибку типа can't read property longitude of undefined
, и мой сервер падает. Как я могу справиться с этим?
Решение. Опять же, предполагая, что я не могу выполнить проверку данных в другом месте, я могу легко добавить try/catch внутри filter
, чтобы просто пропустить любые искаженные данные.
const filteredLocations = locations.filter(loc => { try { const userIsInLongRange = locationProximityCheck( userLong, loc.coordinates.longitude ); const userIsInLatRange = locationProximityCheck( userLat, loc.coordinates.latitude ); return userIsInLongRange && userIsInLatRange; } catch (error) { // log the error so that bad data gets noticed errorLogger(`Error Filtering Locations at ${loc.name}`); // return false from the filter function so the malformed // options is skipped return false; } })
Теперь любые проблемные данные удобно игнорировать, и я делаю уведомление, чтобы знать, в каком месте может потребоваться исправление данных.
Сделать то же самое в функции .map
немного сложнее, потому что даже если вы ошибетесь в дескрипторе внутри функции .map
, вы все равно получите элемент в результирующем массиве. Однако отфильтровать их довольно просто, если вы возвращаете значение, которое не соответствует ни одному из ваших ожидаемых результатов (например, null
при возврате массива объектов).
const mappedData = someData.map(d => { try { ... some code that might throw errors before you return return whateverIsToBeReturned; } catch (e) { // log the error so that bad data gets noticed errorLogger(`Error mapping data at ${d.property}`); // return null return null; } }).filter(item => !!item); // <= filter out anything falsy
Примечание. Как всегда, если у кого-то есть лучший подход, оставьте его в комментариях.