1
- import colander
2
1
import logging
3
2
import sys
4
3
from typing import TYPE_CHECKING
5
4
5
+ import colander
6
+ from cornice .renderer import JSONError
6
7
from pyramid .httpexceptions import (
7
8
HTTPException ,
8
9
HTTPInternalServerError ,
@@ -80,26 +81,23 @@ def validate_format(request):
80
81
def http_apply_response_format_tween_factory (handler , registry ): # noqa: F811
81
82
# type: (ViewHandler, Registry) -> Callable[[PyramidRequest], AnyViewResponse]
82
83
"""
83
- Tween factory that applies the response ``Content-Type`` according to the requested format.
84
+ Tween factory that applies the request ``Accept`` header according to the requested format.
84
85
85
- Format can be provided by ``Accept`` header or ``format`` query.
86
+ Format can be provided by ``Accept`` header or ``format`` query. The resulting request will unify the format
87
+ under the ``Accept`` header, such that a single reference can be considered by the following code.
86
88
87
- The target ``Content-Type `` is expected to have been validated by :func:`validate_accept_header_tween` beforehand
88
- to handle not-acceptable errors. If an invalid format is detected at this stage, JSON is used by default.
89
- This can be the case for example for :func:`validate_accept_header_tween` itself that raises the error about
90
- the invalid ``Accept`` header or ``format`` query, but detects these inadequate parameters from incoming request .
89
+ If validation of allowed ``Accept `` header values must be done, it is expected to be validated by
90
+ :func:`cornice.validators.colander_headers_validator` or :func:`cornice.validators.colander_validator`
91
+ with the relevant `validators` definition applied onto the :class:`cornice.service.Service` decorating
92
+ the specific view .
91
93
92
- The tween also ensures that additional request metadata extracted from :func:`get_request_info` is applied to
93
- the response body if not already provided by a previous operation.
94
+ Since browsers will typically inject :term:`HTML`-related media-types in the ``Accept` header, specific
95
+ combinations of browser ``User-Agent`` will ignore those values to provide :term:`JSON` by default, unless
96
+ an explicit ``text/html`` or ``f=html`` is specified. In the case of non-browser ``User-Agent``, headers will
97
+ be interpreted normally.
94
98
"""
95
99
def apply_format (request ):
96
100
# type: (PyramidRequest) -> HTTPException
97
- """
98
- Validates the specified request according to its ``Accept`` header, ignoring UI related routes that request more
99
- content-types than the ones supported by the application for display purposes (styles, images etc.).
100
-
101
- Alternatively, if no ``Accept`` header is found, look for equivalent value provided via query parameter.
102
- """
103
101
content_type = guess_target_format (request )
104
102
# NOTE:
105
103
# enforce the accept header in case it was specified with format query, since some renderer implementations
@@ -123,77 +121,6 @@ def apply_format(request):
123
121
return apply_format
124
122
125
123
126
- # def generate_response_http_format(http_class, http_kwargs, content, content_type=None):
127
- # # type: (Type[HTTPException], Optional[Dict[str, Any]], Union[JSON, str], Optional[str]) -> HTTPException
128
- # """
129
- # Formats the HTTP response content according to desired ``content_type`` using provided HTTP code and content.
130
- #
131
- # :param http_class: `HTTPException` derived class to use for output (code, generic title/explanation, etc.)
132
- # :param http_kwargs: additional keyword arguments to pass to `http_class` when called
133
- # :param content: formatted JSON content or literal string content providing additional details for the response
134
- # :param content_type: One of the supported types by the application.
135
- # :return: `http_class` instance with requested information and content type if creation succeeds
136
- # :raises: `HTTPInternalServerError` instance details about requested information and content type if creation fails
137
- # """
138
- # content = str(content) if not isinstance(content, six.string_types) else content
139
- #
140
- # # adjust additional keyword arguments and try building the http response class with them
141
- # http_kwargs = {} if http_kwargs is None else http_kwargs
142
- # http_headers = http_kwargs.get("headers", {})
143
- # # omit content-type and related headers that we override
144
- # for header in dict(http_headers):
145
- # if header.lower().startswith("content-"):
146
- # http_headers.pop(header, None)
147
- #
148
- # try:
149
- # # Pass down Location if it is provided and should be given as input parameter for this HTTP class.
150
- # # Omitting this step would inject a (possibly extra) empty Location that defaults to the current application.
151
- # # When resolving HTTP redirects, injecting this extra Location when the requested one is not the current
152
- # # application will lead to redirection failures because all locations are appended in the header as CSV list.
153
- # if issubclass(http_class, HTTPRedirection):
154
- # location = get_header("Location", http_headers, pop=True)
155
- # if location and "location" not in http_kwargs:
156
- # http_kwargs["location"] = location
157
- #
158
- # # directly output json
159
- # if content_type == ContentType.APP_JSON:
160
- # content_type = "{}; charset=UTF-8".format(CONTENT_TYPE_JSON)
161
- # http_response = http_class(body=content, content_type=content_type, **http_kwargs)
162
- #
163
- # # otherwise json is contained within the html <body> section
164
- # elif content_type == ContentType.TEXT_HTML:
165
- # if http_class is HTTPOk:
166
- # http_class.explanation = "Operation successful."
167
- # if not http_class.explanation:
168
- # http_class.explanation = http_class.title # some don't have any defined
169
- # # add preformat <pre> section to output as is within the <body> section
170
- # html_status = "Exception" if http_class.code >= 400 else "Response"
171
- # html_header = "{}<br><h2>{} Details</h2>".format(http_class.explanation, html_status)
172
- # html_template = "<pre style='word-wrap: break-word; white-space: pre-wrap;'>{}</pre>"
173
- # content_type = "{}; charset=UTF-8".format(CONTENT_TYPE_HTML)
174
- # if json_content:
175
- # html_body = html_template.format(json.dumps(json_content, indent=True, ensure_ascii=False))
176
- # else:
177
- # html_body = html_template.format(content)
178
- # html_body = html_header + html_body
179
- # http_response = http_class(body_template=html_body, content_type=content_type, **http_kwargs)
180
- #
181
- # elif content_type in [CONTENT_TYPE_APP_XML, CONTENT_TYPE_TXT_XML]:
182
- # xml_body = OutputFormat.convert(json_content, ContentType.APP_XML, item_root="response")
183
- # http_response = http_class(body=xml_body, content_type=CONTENT_TYPE_TXT_XML, **http_kwargs)
184
- #
185
- # # default back to plain text
186
- # else:
187
- # http_response = http_class(body=content, content_type=CONTENT_TYPE_PLAIN, **http_kwargs)
188
- #
189
- # return http_response
190
- # except Exception as exc: # pylint: disable=W0703
191
- # raise HTTPInternalServerError(json={
192
- # "detail": "Failed to build HTTP response",
193
- # "cause": repr(exc),
194
- # "value": str(content_type),
195
- # })
196
-
197
124
# FIXME:
198
125
# https://github.com/crim-ca/weaver/issues/215
199
126
# define common Exception classes that won't require this type of conversion
@@ -208,7 +135,12 @@ def error_repr(http_err):
208
135
if not isinstance (http_err , (HTTPException , OWSException )):
209
136
return f"({ err_type } ) { http_err !s} "
210
137
err_code = getattr (http_err , "code" , getattr (http_err , "status_code" , 500 ))
211
- err_repr = str (http_err )
138
+ # in some cases, cornice will wrap HTTPError incorrectly without 'detail'
139
+ # (see https://github.com/Cornices/cornice/issues/586)
140
+ try :
141
+ err_repr = str (http_err )
142
+ except (AttributeError , NameError ):
143
+ err_repr = err_type
212
144
try :
213
145
# FIXME: handle colander invalid directly in tween (https://github.com/crim-ca/weaver/issues/112)
214
146
# specific cleanup in case of string representation of colander.Invalid to help debug logged errors
@@ -301,7 +233,7 @@ def handle_ows_tween(request):
301
233
# names must differ to avoid conflicting configuration error
302
234
OWS_RESPONSE_EXCVIEW = fully_qualified_name (ows_response_tween_factory_excview )
303
235
OWS_RESPONSE_INGRESS = fully_qualified_name (ows_response_tween_factory_ingress )
304
- HTTP_FORMAT_VALIDATE = fully_qualified_name (http_validate_response_format_tween_factory )
236
+ # HTTP_FORMAT_VALIDATE = fully_qualified_name(http_validate_response_format_tween_factory)
305
237
HTTP_FORMAT_RESPONSE = fully_qualified_name (http_apply_response_format_tween_factory )
306
238
307
239
@@ -312,7 +244,7 @@ def includeme(config):
312
244
config .add_tween (OWS_RESPONSE_INGRESS , under = INGRESS )
313
245
314
246
# intermediate tweens to modify the request/response
315
- config .add_tween (HTTP_FORMAT_VALIDATE , over = MAIN )
247
+ # config.add_tween(HTTP_FORMAT_VALIDATE, over=MAIN)
316
248
config .add_tween (HTTP_FORMAT_RESPONSE , over = OWS_RESPONSE_EXCVIEW )
317
249
318
250
# using 'EXCVIEW' to run over any other 'valid' exception raised to adjust formatting and log
0 commit comments