def render_with(template=None, json=False, jsonp=False):
    """
    Decorator to render the wrapped function with the given template (or dictionary
    of mimetype keys to templates, where the template is a string name of a template
    file or a callable that returns a Response). The function's return value must be
    a dictionary and is passed to the template as parameters. Callable templates get
    a single parameter with the function's return value. Usage::

        @app.route('/myview')
        @render_with('myview.html')
        def myview():
            return {'data': 'value'}

        @app.route('/myview_with_json')
        @render_with('myview.html', json=True)
        def myview_no_json():
            return {'data': 'value'}

        @app.route('/otherview')
        @render_with({
            'text/html': 'otherview.html',
            'text/xml': 'otherview.xml'})
        def otherview():
            return {'data': 'value'}

        @app.route('/404view')
        @render_with('myview.html')
        def myview():
            return {'error': '404 Not Found'}, 404

        @app.route('/headerview')
        @render_with('myview.html')
        def myview():
            return {'data': 'value'}, 200, {'X-Header': 'Header value'}

    When a mimetype is specified and the template is not a callable, the response is
    returned with the same mimetype. Callable templates must return Response objects
    to ensure the correct mimetype is set.

    If a dictionary of templates is provided and does not include a handler for ``*/*``,
    render_with will attempt to use the handler for (in order) ``text/html``, ``text/plain``
    and the various JSON types, falling back to rendering the value into a unicode string.

    If the method is called outside a request context, the wrapped method's original
    return value is returned. This is meant to facilitate testing and should not be
    used to call the method from within another view handler as the presence of a
    request context will trigger template rendering.

    Rendering may also be suspended by calling the view handler with ``_render=False``.

    render_with provides JSON and JSONP handlers for the ``application/json``,
    ``text/json`` and ``text/x-json`` mimetypes if ``json`` or ``jsonp`` is True
    (default is False).

    :param template: Single template, or dictionary of MIME type to templates. If the
        template is a callable, it is called with the output of the wrapped function
    :param json: Helper to add a JSON handler (default is False)
    :param jsonp: Helper to add a JSONP handler (if True, also provides JSON, default is False)
    """
    if jsonp:
        templates = {
            'application/json': dict_jsonp,
            'application/javascript': dict_jsonp,
        }
    elif json:
        templates = {
            'application/json': dict_jsonify,
        }
    else:
        templates = {}
    if isinstance(template, six.string_types):
        templates['text/html'] = template
    elif isinstance(template, dict):
        templates.update(template)
    elif template is None and (json or jsonp):
        pass
    else:  # pragma: no cover
        raise ValueError("Expected string or dict for template")

    default_mimetype = '*/*'
    if '*/*' not in templates:
        templates['*/*'] = six.text_type
        default_mimetype = 'text/plain'
        for mimetype in ('text/html', 'text/plain', 'application/json'):
            if mimetype in templates:
                templates['*/*'] = templates[mimetype]
                default_mimetype = mimetype  # Remember which mimetype's handler is serving for */*
                break

    template_mimetypes = list(templates.keys())
    template_mimetypes.remove('*/*')  # */* messes up matching, so supply it only as last resort

    def inner(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            # Check if we need to bypass rendering
            render = kwargs.pop('_render', True)

            # Get the result
            result = f(*args, **kwargs)

            # Is the result a Response object? Don't attempt rendering
            if isinstance(result, (Response, WerkzeugResponse, current_app.response_class)):
                return result

            # Did the result include status code and headers?
            if isinstance(result, tuple):
                resultset = result
                result = resultset[0]
                if len(resultset) > 1:
                    status_code = resultset[1]
                else:
                    status_code = None
                if len(resultset) > 2:
                    headers = Headers(resultset[2])
                else:
                    headers = Headers()
            else:
                status_code = None
                headers = Headers()

            if len(templates) > 1:  # If we have more than one template handler
                if 'Vary' in headers:
                    vary_values = [item.strip() for item in headers['Vary'].split(',')]
                    if 'Accept' not in vary_values:
                        vary_values.append('Accept')
                    headers['Vary'] = ', '.join(vary_values)
                else:
                    headers['Vary'] = 'Accept'

            # Find a matching mimetype between Accept headers and available templates
            use_mimetype = None
            if render and request:
                # We do not use request.accept_mimetypes.best_match because it turns out to
                # be buggy: it returns the least match instead of the best match.
                # use_mimetype = request.accept_mimetypes.best_match(template_mimetypes, '*/*')
                use_mimetype = _best_mimetype_match(template_mimetypes, request.accept_mimetypes, '*/*')

            # Now render the result with the template for the mimetype
            if use_mimetype is not None:
                if callable(templates[use_mimetype]):
                    rendered = templates[use_mimetype](result)
                    if isinstance(rendered, Response):
                        if status_code is not None:
                            rendered.status_code = status_code
                        if headers is not None:
                            rendered.headers.extend(headers)
                    else:
                        rendered = current_app.response_class(
                            rendered,
                            status=status_code,
                            headers=headers,
                            mimetype=default_mimetype if use_mimetype == '*/*' else use_mimetype)
                else:  # Not a callable mimetype. Render as a jinja2 template
                    rendered = current_app.response_class(
                        render_template(templates[use_mimetype], **result),
                        status=status_code or 200, headers=headers,
                        mimetype=default_mimetype if use_mimetype == '*/*' else use_mimetype)
                return rendered
            else:
                return result
        return decorated_function
    return inner