diff --git a/web/client/components/map/openlayers/plugins/WMSLayer.js b/web/client/components/map/openlayers/plugins/WMSLayer.js index 4a232890b6..6188f66458 100644 --- a/web/client/components/map/openlayers/plugins/WMSLayer.js +++ b/web/client/components/map/openlayers/plugins/WMSLayer.js @@ -29,7 +29,7 @@ import VectorTileSource from 'ol/source/VectorTile'; import VectorTileLayer from 'ol/layer/VectorTile'; import { isVectorFormat } from '../../../../utils/VectorTileUtils'; -import { isValidResponse } from '../../../../utils/WMSUtils'; +import { isValidResponse, parseOGCException } from '../../../../utils/WMSUtils'; import { OL_VECTOR_FORMATS, applyStyle } from '../../../../utils/openlayers/VectorTileUtils'; import { proxySource, getWMSURLs, wmsToOpenlayersOptions, toOLAttributions, generateTileGrid } from '../../../../utils/openlayers/WMSUtils'; @@ -66,26 +66,25 @@ const loadFunction = (options, headers) => function(image, src) { } } }).catch(e => { + image.setState(3); console.error(e); }); } else { - if (headers) { + if (headers) { // case of custom headers is setted in localConfig, example requestsConfigurationRules axios.get(newSrc, { headers, responseType: 'blob' - }).then(response => { - if (isValidResponse(response)) { + }).then(async response => { + if (isValidResponse(response)) { // not contains OGC exception image.getImage().src = URL.createObjectURL(response.data); } else { - // #10701 this is needed to trigger the imageloaderror event - // in ol otherwise this event is not triggered if you assign - // the xml content of the exception to the src attribute - image.getImage().src = null; - console.error("error: " + response.data); + const exception = await parseOGCException(response); + throw new Error('response exception: ' + exception); } }).catch(e => { - image.getImage().src = null; - console.error(e); + image.getImage().src = null; // needed to trigger the MS imageloaderror event in Map.onLayerError + image.setState(3); // set error state for tile and removed from the queue and will block, prevent reloading loops + console.error(e); // show ogc exception in console for debugging }); } else { img.src = newSrc; diff --git a/web/client/utils/WMSUtils.js b/web/client/utils/WMSUtils.js index 44d2e664a6..4900edae1a 100644 --- a/web/client/utils/WMSUtils.js +++ b/web/client/utils/WMSUtils.js @@ -59,6 +59,35 @@ export const isValidResponse = (response) => { return response?.status === 200 && response?.data && response?.data?.type !== "text/xml"; }; +/** + * parse the xml exception message and code + * @param {Object} response is axios response object containing the exception in the data property is a Blob of type text/xml + * @returns {string} parsed exception with code and message + */ +export const parseOGCException = (response) => { + return new Promise((resolve, reject) => { + const textDecoder = new TextDecoder(); + const reader = response.data.stream().getReader(); + reader.read().then(({ done, value }) => { + if (done) { + reject("No data available"); + return; + } + const responseText = textDecoder.decode(value); + const parser = new DOMParser(); + const xmlDoc = parser.parseFromString(responseText, "text/xml"); + const serviceException = xmlDoc.getElementsByTagName("ServiceException")[0]; + if (serviceException) { + const code = serviceException.getAttribute("code") || "unknown"; + const message = serviceException.textContent || "unknown"; + resolve(`code: ${code}, message: ${message}`); + } else { + resolve(`code: unknown, message: ${responseText}`); + } + }).catch(reject); + }); +}; + /** * Parses layer info from capabilities object * @param {object} capabilities capabilities section of the layer as an object from xml2js parsing of the WMS capabilities diff --git a/web/client/utils/__tests__/WMSUtils-test.js b/web/client/utils/__tests__/WMSUtils-test.js index 06eff09e99..b63da88424 100644 --- a/web/client/utils/__tests__/WMSUtils-test.js +++ b/web/client/utils/__tests__/WMSUtils-test.js @@ -13,7 +13,8 @@ import { getLayerOptions, getTileGridFromLayerOptions, getCustomTileGridProperties, - isValidResponse + isValidResponse, + parseOGCException } from '../WMSUtils'; describe('Test the WMSUtils', () => { @@ -132,4 +133,16 @@ describe('Test the WMSUtils', () => { // valid responses expect(isValidResponse({data: {type: "blob"}, status: 200})).toBeTruthy(); }); + it('test parseOGCException', () => { + const response = { + data: new Blob([`Rendering process failed. Layers: road_issue:Road`], { type: 'text/xml' }), + status: 200 + }; + expect(parseOGCException(response).then((result) => { + expect(result).toEqual({ + code: 'internalError', + message: 'Rendering process failed. Layers: road_issue:Road' + }); + })); + }); });