Skip to content

Commit

Permalink
Merge pull request #529 from EOxServer/43-custom-error-page-for-wcs-e…
Browse files Browse the repository at this point in the history
…xceptions

43 custom error page for wcs exceptions
  • Loading branch information
constantinius committed Jul 30, 2022
2 parents 21770a7 + 8a83989 commit e3f0e36
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 10 deletions.
77 changes: 76 additions & 1 deletion autotest/autotest_services/tests/wcs/test_v20.py
Original file line number Diff line number Diff line change
Expand Up @@ -1632,4 +1632,79 @@ def getExpectedHTTPStatus(self):
return 404

def getExpectedExceptionCode(self):
return "TilingInvalid"
return "TilingInvalid"


@tag('wcs', 'wcs20')
class WCS20DefaultErrorFormatIsXmlTestCase(testbase.OWSTestCase):
def getRequest(self):
params = "service=WCS&version=2.0.1&request=invalid"
return (params, "kvp")

def testStatus(self):
pass

def testContentTypeIsXml(self):
content_type = self.response.get("Content-Type")
self.assertEqual(content_type, "text/xml")


@tag('wcs', 'wcs20')
class WCS20ErrorFormatIsHtmlOnRequestTestCase(testbase.OWSTestCase):
def getRequest(self):
params = "service=WCS&version=2.0.1&request=invalid&exceptions=text/html"
return (params, "kvp")

def testStatus(self):
pass

def testContentTypeIsHtml(self):
content_type = self.response.get("Content-Type")
self.assertEqual(content_type, "text/html")

def testTemplateContainsErrorMessage(self):
self.assertIn(
"Error: Operation 'INVALID' is not supported",
self.response.content.decode(),
)

@tag('wcs', 'wcs20')
class WCS20PostDefaultErrorFormatIsXmlTestCase(testbase.OWSTestCase):
def getRequest(self):
params = """<ns:invalid updateSequence="u2001" service="WCS"
xmlns:ns="http://www.opengis.net/wcs/2.0"
xmlns:ns1="http://www.opengis.net/ows/2.0">
<ns1:AcceptVersions><ns1:Version>2.0.1</ns1:Version></ns1:AcceptVersions>
</ns:invalid>
"""
return (params, "xml")

def testStatus(self):
pass

def testContentTypeIsHtml(self):
content_type = self.response.get("Content-Type")
self.assertEqual(content_type, "text/xml")


@tag('wcs', 'wcs20')
class WCS20PostErrorFormatIsHtmlOnRequestTestCase(testbase.OWSTestCase):
def getRequest(self):
params = """<ns:invalid updateSequence="u2001" service="WCS"
xmlns:ns="http://www.opengis.net/wcs/2.0"
xmlns:ns1="http://www.opengis.net/ows/2.0"
xmlns:eoxs="http://eoxserver.org/eoxs/1.0">
<ns1:AcceptVersions><ns1:Version>2.0.1</ns1:Version></ns1:AcceptVersions>
<ns:Extension>
<eoxs:exceptions>text/html</eoxs:exceptions>
</ns:Extension>
</ns:invalid>
"""
return (params, "xml")

def testStatus(self):
pass

def testContentTypeIsHtml(self):
content_type = self.response.get("Content-Type")
self.assertEqual(content_type, "text/html")
2 changes: 2 additions & 0 deletions eoxserver/services/opensearch/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
# default ordering (field name) for opensearch queries
DEFAULT_EOXS_OPENSEARCH_DEFAULT_ORDERING = None

# when True, adds exceptions=text/html to all GetCoverage links in opensearch response
EOXS_OPENSEARCH_GETCOVERAGE_HTML_EXCEPTION = False

def get_opensearch_record_model():
class_name = getattr(
Expand Down
16 changes: 10 additions & 6 deletions eoxserver/services/opensearch/formats/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,14 +510,18 @@ def _create_map_link(self, request, item, size):
return None

def _create_coverage_link(self, request, coverage):
options = dict(
service="WCS",
version="2.0.1",
request="GetCoverage",
coverageId=coverage.identifier,
)
if getattr(settings, 'EOXS_OPENSEARCH_GETCOVERAGE_HTML_EXCEPTION', False):
options["exceptions"] = "text/html"

return request.build_absolute_uri(
"%s?%s" % (
reverse("ows"), urlencode(dict(
service="WCS",
version="2.0.1",
request="GetCoverage",
coverageId=coverage.identifier,
))
reverse("ows"), urlencode(options)
)
)

Expand Down
2 changes: 1 addition & 1 deletion eoxserver/services/ows/common/v20/encoders.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def encode_operations_metadata(self, request, service, versions):


class OWS20ExceptionXMLEncoder(XMLEncoder):
def encode_exception(self, message, version, code, locator=None):
def encode_exception(self, message, version, code, locator=None, request=None, exception=None):
exception_attributes = {
"exceptionCode": str(code)
}
Expand Down
2 changes: 2 additions & 0 deletions eoxserver/services/ows/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,5 @@
'eoxserver.services.ows.wcs.v20.exceptionhandler.WCS20ExceptionHandler',
'eoxserver.services.ows.wms.v13.exceptionhandler.WMS13ExceptionHandler',
]

DEFAULT_EOXS_WCS_ERROR_HTML_TEMPLATE = "wcs/error_template.html"
66 changes: 64 additions & 2 deletions eoxserver/services/ows/wcs/v20/exceptionhandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,21 @@
# THE SOFTWARE.
#-------------------------------------------------------------------------------

import traceback

from django.conf import settings
from django.template.loader import render_to_string

from eoxserver.core import Component, implements
from eoxserver.core.decoders import kvp, lower, xml
from eoxserver.services.ows.interfaces import ExceptionHandlerInterface
from eoxserver.services.ows.common.v20.encoders import OWS20ExceptionXMLEncoder
from eoxserver.services.ows.config import DEFAULT_EOXS_WCS_ERROR_HTML_TEMPLATE
from eoxserver.services.ows.wcs.v20.util import ns_wcs
from eoxserver.core.decoders import (
DecodingException, MissingParameterException
)
from eoxserver.core.util.xmltools import NameSpace, NameSpaceMap


CODES_404 = frozenset((
Expand All @@ -46,13 +54,67 @@
))


class WCS20ExceptionHandlerKVPDecoder(kvp.Decoder):
exceptions = kvp.Parameter(num="?", type=lower, default="application/xml")


class WCS20ExceptionHandlerXMLDecoder(xml.Decoder):
namespaces = NameSpaceMap(
ns_wcs, NameSpace("http://eoxserver.org/eoxs/1.0", "eoxs")
)
exceptions = xml.Parameter("wcs:Extension/eoxs:exceptions/text()", num="?", type=lower, default="application/xml")


class OWS20ExceptionHTMLEncoder(object):
@property
def content_type(self):
return "text/html"

def serialize(self, message):
# content is already str
return message

def encode_exception(self, message, version, code, locator=None, request=None, exception=None):
template_name = getattr(
settings,
'EOXS_ERROR_HTML_TEMPLATE',
DEFAULT_EOXS_WCS_ERROR_HTML_TEMPLATE,
)
# pass in original traceback and debug to allow usage in template
debug = getattr(settings, 'DEBUG', False)
stack_trace = traceback.format_exc()

template_params = {
"message": message,
"exception": exception,
"debug": debug,
"stack_trace": stack_trace,
}
return render_to_string(
template_name,
context=template_params,
request=request
)


class WCS20ExceptionHandler(Component):
implements(ExceptionHandlerInterface)

service = "WCS"
versions = ("2.0.0", "2.0.1")
request = None

def get_encoder(self, request):
if request.method == "GET":
decoder = WCS20ExceptionHandlerKVPDecoder(request.GET)
elif request.method == "POST":
decoder = WCS20ExceptionHandlerXMLDecoder(request.body)

if decoder.exceptions == "text/html":
return OWS20ExceptionHTMLEncoder()
else:
return OWS20ExceptionXMLEncoder()

def handle_exception(self, request, exception):
message = str(exception)
code = getattr(exception, "code", None)
Expand All @@ -72,9 +134,9 @@ def handle_exception(self, request, exception):
elif code in ("OperationNotSupported", "OptionNotSupported"):
status = 501

encoder = OWS20ExceptionXMLEncoder()
encoder = self.get_encoder(request)
xml = encoder.serialize(
encoder.encode_exception(message, "2.0.1", code, locator)
encoder.encode_exception(message, "2.0.1", code, locator, request=request, exception=exception)
)

return (xml, encoder.content_type, status)
17 changes: 17 additions & 0 deletions eoxserver/services/templates/wcs/error_template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<html>
<head>
<title>Error: {{ message }}</title>
</head>
<body>
<h1>Error: {{ message }}</h1>
{% if exception.code == 404 %}
<p>Not found.</p>
{% endif %}
{% if debug %}
<h2>DEBUG: Full stack trace</h2>
<p>
{{ stack_trace }}
</p>
{% endif %}
</body>
</html>

0 comments on commit e3f0e36

Please sign in to comment.