]> git.openstreetmap.org Git - nominatim.git/commitdiff
send charset again in content-type when returning json
authorSarah Hoffmann <lonvia@denofr.de>
Wed, 23 Aug 2023 18:55:57 +0000 (20:55 +0200)
committerSarah Hoffmann <lonvia@denofr.de>
Wed, 23 Aug 2023 18:55:57 +0000 (20:55 +0200)
There are quite a few applications out there that will use some local
encoding when the charset is not explicitly given.

nominatim/api/v1/server_glue.py
test/python/api/test_server_glue_v1.py

index cf9bc3af6ff8d804450319119393184052fe237e..95484c5bea8bce16ae10c2eda41759fa903c65b2 100644 (file)
@@ -25,17 +25,18 @@ from nominatim.api.v1.format import dispatch as formatting
 from nominatim.api.v1.format import RawDataList
 from nominatim.api.v1 import helpers
 
 from nominatim.api.v1.format import RawDataList
 from nominatim.api.v1 import helpers
 
-CONTENT_TYPE = {
-  'text': 'text/plain; charset=utf-8',
-  'xml': 'text/xml; charset=utf-8',
-  'debug': 'text/html; charset=utf-8'
-}
+CONTENT_TEXT = 'text/plain; charset=utf-8'
+CONTENT_XML = 'text/xml; charset=utf-8'
+CONTENT_HTML = 'text/html; charset=utf-8'
+CONTENT_JSON = 'application/json; charset=utf-8'
+
+CONTENT_TYPE = {'text': CONTENT_TEXT, 'xml': CONTENT_XML, 'debug': CONTENT_HTML}
 
 class ASGIAdaptor(abc.ABC):
     """ Adapter class for the different ASGI frameworks.
         Wraps functionality over concrete requests and responses.
     """
 
 class ASGIAdaptor(abc.ABC):
     """ Adapter class for the different ASGI frameworks.
         Wraps functionality over concrete requests and responses.
     """
-    content_type: str = 'text/plain; charset=utf-8'
+    content_type: str = CONTENT_TEXT
 
     @abc.abstractmethod
     def get(self, name: str, default: Optional[str] = None) -> Optional[str]:
 
     @abc.abstractmethod
     def get(self, name: str, default: Optional[str] = None) -> Optional[str]:
@@ -85,13 +86,13 @@ class ASGIAdaptor(abc.ABC):
         """ Create a response from the given output. Wraps a JSONP function
             around the response, if necessary.
         """
         """ Create a response from the given output. Wraps a JSONP function
             around the response, if necessary.
         """
-        if self.content_type == 'application/json' and status == 200:
+        if self.content_type == CONTENT_JSON and status == 200:
             jsonp = self.get('json_callback')
             if jsonp is not None:
                 if any(not part.isidentifier() for part in jsonp.split('.')):
                     self.raise_error('Invalid json_callback value')
                 output = f"{jsonp}({output})"
             jsonp = self.get('json_callback')
             if jsonp is not None:
                 if any(not part.isidentifier() for part in jsonp.split('.')):
                     self.raise_error('Invalid json_callback value')
                 output = f"{jsonp}({output})"
-                self.content_type = 'application/javascript'
+                self.content_type = 'application/javascript; charset=utf-8'
 
         return self.create_response(status, output, num_results)
 
 
         return self.create_response(status, output, num_results)
 
@@ -101,16 +102,16 @@ class ASGIAdaptor(abc.ABC):
             message. The message will be formatted according to the
             output format chosen by the request.
         """
             message. The message will be formatted according to the
             output format chosen by the request.
         """
-        if self.content_type == 'text/xml; charset=utf-8':
+        if self.content_type == CONTENT_XML:
             msg = f"""<?xml version="1.0" encoding="UTF-8" ?>
                       <error>
                         <code>{status}</code>
                         <message>{msg}</message>
                       </error>
                    """
             msg = f"""<?xml version="1.0" encoding="UTF-8" ?>
                       <error>
                         <code>{status}</code>
                         <message>{msg}</message>
                       </error>
                    """
-        elif self.content_type == 'application/json':
+        elif self.content_type == CONTENT_JSON:
             msg = f"""{{"error":{{"code":{status},"message":"{msg}"}}}}"""
             msg = f"""{{"error":{{"code":{status},"message":"{msg}"}}}}"""
-        elif self.content_type == 'text/html; charset=utf-8':
+        elif self.content_type == CONTENT_HTML:
             loglib.log().section('Execution error')
             loglib.log().var_dump('Status', status)
             loglib.log().var_dump('Message', msg)
             loglib.log().section('Execution error')
             loglib.log().var_dump('Status', status)
             loglib.log().var_dump('Message', msg)
@@ -204,7 +205,7 @@ class ASGIAdaptor(abc.ABC):
         """
         if self.get_bool('debug', False):
             loglib.set_log_output('html')
         """
         if self.get_bool('debug', False):
             loglib.set_log_output('html')
-            self.content_type = 'text/html; charset=utf-8'
+            self.content_type = CONTENT_HTML
             return True
 
         return False
             return True
 
         return False
@@ -234,7 +235,7 @@ class ASGIAdaptor(abc.ABC):
             self.raise_error("Parameter 'format' must be one of: " +
                               ', '.join(formatting.list_formats(result_type)))
 
             self.raise_error("Parameter 'format' must be one of: " +
                               ', '.join(formatting.list_formats(result_type)))
 
-        self.content_type = CONTENT_TYPE.get(fmt, 'application/json')
+        self.content_type = CONTENT_TYPE.get(fmt, CONTENT_JSON)
         return fmt
 
 
         return fmt
 
 
index fe406c42e9a778838263bd120108f00e95a7bb44..5a7430f48db2b57c668c06ef27f88b80edb704ab 100644 (file)
@@ -67,7 +67,7 @@ def test_adaptor_parse_format_use_configured():
     adaptor = FakeAdaptor(params={'format': 'json'})
 
     assert adaptor.parse_format(napi.StatusResult, 'text') == 'json'
     adaptor = FakeAdaptor(params={'format': 'json'})
 
     assert adaptor.parse_format(napi.StatusResult, 'text') == 'json'
-    assert adaptor.content_type == 'application/json'
+    assert adaptor.content_type == 'application/json; charset=utf-8'
 
 
 def test_adaptor_parse_format_invalid_value():
 
 
 def test_adaptor_parse_format_invalid_value():
@@ -132,7 +132,7 @@ class TestAdaptorRaiseError:
 
 
     def test_json(self):
 
 
     def test_json(self):
-        self.adaptor.content_type = 'application/json'
+        self.adaptor.content_type = 'application/json; charset=utf-8'
 
         err = self.run_raise_error('TEST', 501)
 
 
         err = self.run_raise_error('TEST', 501)
 
@@ -189,7 +189,7 @@ def test_build_response_with_status():
     assert isinstance(resp, FakeResponse)
     assert resp.status == 404
     assert resp.output == 'stuff\nmore stuff'
     assert isinstance(resp, FakeResponse)
     assert resp.status == 404
     assert resp.output == 'stuff\nmore stuff'
-    assert resp.content_type == 'application/json'
+    assert resp.content_type == 'application/json; charset=utf-8'
 
 
 def test_build_response_jsonp_with_json():
 
 
 def test_build_response_jsonp_with_json():
@@ -201,7 +201,7 @@ def test_build_response_jsonp_with_json():
     assert isinstance(resp, FakeResponse)
     assert resp.status == 200
     assert resp.output == 'test.func({})'
     assert isinstance(resp, FakeResponse)
     assert resp.status == 200
     assert resp.output == 'test.func({})'
-    assert resp.content_type == 'application/javascript'
+    assert resp.content_type == 'application/javascript; charset=utf-8'
 
 
 def test_build_response_jsonp_without_json():
 
 
 def test_build_response_jsonp_without_json():
@@ -270,7 +270,7 @@ class TestStatusEndpoint:
 
         assert isinstance(resp, FakeResponse)
         assert resp.status == 200
 
         assert isinstance(resp, FakeResponse)
         assert resp.status == 200
-        assert resp.content_type == 'application/json'
+        assert resp.content_type == 'application/json; charset=utf-8'
 
 
     @pytest.mark.asyncio
 
 
     @pytest.mark.asyncio