Mercurial > p > roundup > code
comparison roundup/rest.py @ 6311:be8d5a8e090a
Fix uncaught error when parsing rest headers, document
Started this work as better docs for rest response format. But I found
406 error response was not being tested. Also there was no error for
bad Content-Type.
In rest.py fix uncaught exceptions due to invalid Accept or
Content-Type headers. If Content-type is valid but not
application/json return code 415.
Document use of accept header (was only shown in examples) and support
for q parameter. Describe using .xml and .json extensions to select
return format for testing from browser (where setting accept header is
a problem). Document 406 error code return. Document 415 error code
return and acceptable content types. Previously only doc was in
examples.
Set up tests for 406 and 415 error codes.
| author | John Rouillard <rouilj@ieee.org> |
|---|---|
| date | Fri, 01 Jan 2021 14:14:34 -0500 |
| parents | 5b66c480f71f |
| children | c1a672b1ad85 |
comparison
equal
deleted
inserted
replaced
| 6310:68d83479747b | 6311:be8d5a8e090a |
|---|---|
| 221 parts = media_range.split(";") | 221 parts = media_range.split(";") |
| 222 media_type = parts.pop(0).strip() | 222 media_type = parts.pop(0).strip() |
| 223 media_params = [] | 223 media_params = [] |
| 224 # convert vendor-specific content types into something useful (see | 224 # convert vendor-specific content types into something useful (see |
| 225 # docstring) | 225 # docstring) |
| 226 typ, subtyp = media_type.split('/') | 226 try: |
| 227 typ, subtyp = media_type.split('/') | |
| 228 except ValueError: | |
| 229 raise UsageError("Invalid media type: %s"%media_type) | |
| 227 # check for a + in the sub-type | 230 # check for a + in the sub-type |
| 228 if '+' in subtyp: | 231 if '+' in subtyp: |
| 229 # if it exists, determine if the subtype is a vendor-specific type | 232 # if it exists, determine if the subtype is a vendor-specific type |
| 230 vnd, sep, extra = subtyp.partition('+') | 233 vnd, sep, extra = subtyp.partition('+') |
| 231 if vnd.startswith('vnd'): | 234 if vnd.startswith('vnd'): |
| 244 # and re-write media_type to something like application/json so | 247 # and re-write media_type to something like application/json so |
| 245 # it can be used usefully when looking up emitters | 248 # it can be used usefully when looking up emitters |
| 246 media_type = '{}/{}'.format(typ, extra) | 249 media_type = '{}/{}'.format(typ, extra) |
| 247 q = 1.0 | 250 q = 1.0 |
| 248 for part in parts: | 251 for part in parts: |
| 249 (key, value) = part.lstrip().split("=", 1) | 252 try: |
| 253 (key, value) = part.lstrip().split("=", 1) | |
| 254 except ValueError: | |
| 255 raise UsageError("Invalid param: %s"%part.lstrip()) | |
| 250 key = key.strip() | 256 key = key.strip() |
| 251 value = value.strip() | 257 value = value.strip() |
| 252 if key == "q": | 258 if key == "q": |
| 253 q = float(value) | 259 q = float(value) |
| 254 if q > 1.0: | 260 if q > 1.0: |
| 1871 method.upper(), uri) | 1877 method.upper(), uri) |
| 1872 | 1878 |
| 1873 # parse Accept header and get the content type | 1879 # parse Accept header and get the content type |
| 1874 # Acceptable types ordered with preferred one first | 1880 # Acceptable types ordered with preferred one first |
| 1875 # in list. | 1881 # in list. |
| 1876 accept_header = parse_accept_header(headers.get('Accept')) | 1882 try: |
| 1883 accept_header = parse_accept_header(headers.get('Accept')) | |
| 1884 except UsageError as e: | |
| 1885 output = self.error_obj(406, _("Unable to parse Accept Header. %(error)s. " | |
| 1886 "Acceptable types: %(acceptable_types)s") % { | |
| 1887 'error': e.args[0], | |
| 1888 'acceptable_types': " ".join(sorted(self.__accepted_content_type.keys()))}) | |
| 1889 accept_header = [] | |
| 1890 | |
| 1877 if not accept_header: | 1891 if not accept_header: |
| 1878 accept_type = self.__default_accept_type | 1892 accept_type = self.__default_accept_type |
| 1879 else: | 1893 else: |
| 1880 accept_type = None | 1894 accept_type = None |
| 1881 for part in accept_header: | 1895 for part in accept_header: |
| 1911 # get the request format for response | 1925 # get the request format for response |
| 1912 # priority : extension from uri (/rest/data/issue.json), | 1926 # priority : extension from uri (/rest/data/issue.json), |
| 1913 # header (Accept: application/json, application/xml) | 1927 # header (Accept: application/json, application/xml) |
| 1914 # default (application/json) | 1928 # default (application/json) |
| 1915 ext_type = os.path.splitext(urlparse(uri).path)[1][1:] | 1929 ext_type = os.path.splitext(urlparse(uri).path)[1][1:] |
| 1916 data_type = ext_type or accept_type or "invalid" | 1930 |
| 1931 # headers.get('Accept') is never empty if called here. | |
| 1932 # accept_type will be set to json if there is no Accept header | |
| 1933 # accept_type wil be empty only if there is an Accept header | |
| 1934 # with invalid values. | |
| 1935 data_type = ext_type or accept_type or headers.get('Accept') or "invalid" | |
| 1917 | 1936 |
| 1918 if (ext_type): | 1937 if (ext_type): |
| 1919 # strip extension so uri make sense | 1938 # strip extension so uri make sense |
| 1920 # .../issue.json -> .../issue | 1939 # .../issue.json -> .../issue |
| 1921 uri = uri[:-(len(ext_type) + 1)] | 1940 uri = uri[:-(len(ext_type) + 1)] |
| 1954 if content_type_header.lower() == "application/json": | 1973 if content_type_header.lower() == "application/json": |
| 1955 try: | 1974 try: |
| 1956 input = SimulateFieldStorageFromJson(b2s(input.value)) | 1975 input = SimulateFieldStorageFromJson(b2s(input.value)) |
| 1957 except ValueError as msg: | 1976 except ValueError as msg: |
| 1958 output = self.error_obj(400, msg) | 1977 output = self.error_obj(400, msg) |
| 1978 else: | |
| 1979 output = self.error_obj(415, | |
| 1980 "Unable to process input of type %s" % | |
| 1981 content_type_header) | |
| 1959 | 1982 |
| 1960 # check for pretty print | 1983 # check for pretty print |
| 1961 try: | 1984 try: |
| 1962 pretty_output = not input['@pretty'].value.lower() == "false" | 1985 pretty_output = not input['@pretty'].value.lower() == "false" |
| 1963 # Can also return a TypeError ("not indexable") | 1986 # Can also return a TypeError ("not indexable") |
