Giter VIP home page Giter VIP logo

flask-accept's Introduction

Flask-Accept

image

Description

Custom Accept header routing support for Flask.

Features

Respond differently based on the MIME type accepted

Extend any given endpoint to support any additional media type.

Use custom media types to version your API

Never put a /v1/ in your URI ever again.

Dead-simple API

Yet Another Flask Decorator.

Documentation

Installation

Installing:

$ pip install flask-accept

Quickstart

Below is an example Flask app that only accepts the text/html media type:

When one tries to access the endpoint without a valid Accept header:

$ curl localhost:5000 -I
HTTP/1.0 406 NOT ACCEPTABLE

With the valid header:

$ curl localhost:5000 -I -H "Accept: text/html"
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8

Adding Support for an Existing Endpoint

Given our example from before, we can add support for a different response to an additonal media type as follows:

Now our hello_world endpoint supports JSON:

$ curl localhost:5000 -I -H "Accept: application/json"
HTTP/1.0 200 OK
Content-Type: application/json

Falling Back on a Default Endpoint

If we want to support a specific media type, but have every other request fall back to a default endpoint, we can use accept_fallback as follows:

Our hello_world endpoint still supports JSON, but for any other media type (or if none is specified) it will fall back:

$ curl localhost:5000 -I
HTTP/1.0 200 OK
Content-Type: text/html

$ curl localhost:5000 -I -H "Accept: madeup/mediatype"
HTTP/1.0 200 OK
Content-Type: text/html

Use Cases

Some possible use cases for Flask-Accept.

Versioning your API

Flask-Accept let you accept any possible media type, including custom vendored media types. This is ideal for versioning an API using Accept headers only:

$ curl localhost:5000 -H "Accept: application/vnd.your_vendor.v1"
Hello World!

$ curl localhost:5000 -H "Accept: application/vnd.your_vendor.v2"
Hello World!

$ curl localhost:5000 -H "Accept: application/vnd.your_vendor.v3"
Goodbye cruel world.

Works with Flask-RESTful Resources

The same functionality can be applied to APIs built with Flask-RESTful

$ curl localhost:5000 -H "Accept: application/vnd.your_vendor.v1"
Hello World!

$ curl localhost:5000 -H "Accept: application/vnd.your_vendor.v2"
Hello World!

$ curl localhost:5000 -H "Accept: application/vnd.your_vendor.v3"
Goodbye cruel world.

Works with Flask-RESTPlus Resources

The same functionality can be applied to APIs built with Flask-RESTPlus

$ curl localhost:5000 -H "Accept: application/vnd.your_vendor.v1"
Hello World!

$ curl localhost:5000 -H "Accept: application/vnd.your_vendor.v2"
Hello World!

$ curl localhost:5000 -H "Accept: application/vnd.your_vendor.v3"
Goodbye cruel world.

Testing

To run the tests

python setup.py test

Authors

License

Open source MIT license.

flask-accept's People

Contributors

charlie-hendricks-es avatar di avatar patricksmith avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

flask-accept's Issues

Multiple resources using Flask Restful

Is there an ability use the support decorator on a method from a different Flask Restful resource?

i.e.

class Health(Resource):
    @accept_fallback
    def get(self):
        return {'success': True}

class Health2(Resource):
    @Health.get.support('application/v2')
    def get2(self):
        return {'success': False}

Flask `MethodView` and `flask-smorest`

I have code that looks like the following:

blp = Blueprint(
    "example", "example", url_prefix="/example"
)
@blp.route(
    "/test/<string1>/<string2>",
)
class Test(MethodView):
    @blp.arguments(...)
    @blp.response(...)
    @blp.paginate()
    @accept('application/vnd.your_vendor.v1', 'application/vnd.your_vendor.v2')
    def get(self, args, string1, string2, pagination_parameters):
        ...

    @get.support('application/vnd.your_vendor.v3')
    def get_v3(self, args, string1, string2, pagination_parameters):
        ...

I'm getting the following error when I try to hit either endpoint:

AttributeError: 'function' object has no attribute 'support' // Werkzeug Debugger

Is there support for Flask's MethodView?

Couldn't get it to work

see https://stackoverflow.com/questions/28791613/route-requests-based-on-the-accept-header-in-flask/47838604#47838604

why i tried this.

after having put:

# Content negotiation
# https://pypi.org/project/flask_accept/
flask_accept

in my requirements.txt and install thing with pip the example import
from flask_accept import accept did not work - showed and error in my IDE liclipse that the import was not available.

python --version
Python 3.6.10
...
pip 20.0.2

pip uninstall flask_accept Found existing installation: flask-accept 0.0.6 Uninstalling flask-accept-0.0.6: Would remove: /Users/wf/Library/Python/3.7/lib/python/site-packages/flask_accept-0.0.6.dist-info/* /Users/wf/Library/Python/3.7/lib/python/site-packages/flask_accept/* Proceed (y/n)? y Successfully uninstalled flask-accept-0.0.6

will downvote the answer now and try another option until it's clear what is going on

Error generating Swagger with flask-restx 0.3.0

With version 0.3.0 of flask-restx it's not working anymore (see Full Stacktrace for details), i downgraded to version 0.2.0 and it's working.
Do someone know how to solve this issue?
Is this project still manteined? ( i did not see commits in a while )

Example Source code

@dummy_namespace.route('/ciao', doc={'description': 'funziona'})
class DummyEndpoint(Resource):
    @responds('DummyData', description='Api', schema=DummyReturnData, status_code=200, api=dummy_namespace)
    def get(self):
        return {
            'ciao': 'HELLO MOTO',
            'banana': 'mela',
            'valore': 12,
        }

Full stacktrace

 * Serving Flask app "application/webserver.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 947-995-830
127.0.0.1 - - [09/Apr/2021 12:09:52] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [09/Apr/2021 12:09:52] "GET /swaggerui/swagger-ui-bundle.js HTTP/1.1" 200 -
127.0.0.1 - - [09/Apr/2021 12:09:52] "GET /swaggerui/swagger-ui-standalone-preset.js HTTP/1.1" 200 -
127.0.0.1 - - [09/Apr/2021 12:09:52] "GET /swagger.json HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask/app.py", line 2464, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask/app.py", line 2450, in wsgi_app
    response = self.handle_exception(e)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask_restx/api.py", line 651, in error_router
    return original_handler(f)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask/app.py", line 1867, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask_restx/api.py", line 649, in error_router
    return self.handle_error(e)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask_restx/api.py", line 651, in error_router
    return original_handler(f)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask_restx/api.py", line 649, in error_router
    return self.handle_error(e)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask_restx/api.py", line 392, in wrapper
    return self.make_response(data, code, headers=headers)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask_restx/api.py", line 415, in make_response
    resp = self.representations[mediatype](data, *args, **kwargs)
  File "/home/buglil/Desktop/FlaskFrontoffice/venv/lib/python3.8/site-packages/flask_restx/representations.py", line 25, in output_json
    dumped = dumps(data, **settings) + "\n"
  File "/usr/lib/python3.8/json/__init__.py", line 234, in dumps
    return cls(
  File "/usr/lib/python3.8/json/encoder.py", line 201, in encode
    chunks = list(chunks)
  File "/usr/lib/python3.8/json/encoder.py", line 431, in _iterencode
    yield from _iterencode_dict(o, _current_indent_level)
  File "/usr/lib/python3.8/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.8/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.8/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.8/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.8/json/encoder.py", line 405, in _iterencode_dict
    yield from chunks
  File "/usr/lib/python3.8/json/encoder.py", line 438, in _iterencode
    o = _default(o)
  File "/usr/lib/python3.8/json/encoder.py", line 179, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '

Support Python 3

Add any (preferably all) to .travis.yml:

  - "3.2"
  - "3.3"
  - "3.4"
  - "3.5"
  - "3.5-dev" # 3.5 development branch
  - "nightly" # currently points to 3.6-dev

Flask-REST Marshmallow Two-way Nesting Schemas

TL;DR : Is there a way to accept string Nested marshmallow fields ?

In order to avoid circular import in a flask/marshmallow project, it's possible to reference the Nested field by using its name (as described here https://marshmallow.readthedocs.io/en/latest/nesting.html#two-way-nesting)

Unfortunately, flask_accept doesn't support it :

` File env/lib/python3.8/site-packages/flask_accepts/decorators/decorators.py", line 110, in decorator
body = for_swagger(
File "env/lib/python3.8/site-packages/flask_accepts/utils.py", line 63, in for_swagger
fields = {
File "env/lib/python3.8/site-packages/flask_accepts/utils.py", line 64, in
k: map_type(v, api, model_name, operation)
File "env/lib/python3.8/site-packages/flask_accepts/utils.py", line 182, in map_type
return type_map[value_type](val, api, model_name, operation)
File "env/lib/python3.8/site-packages/flask_accepts/utils.py", line 19, in unpack_nested
model_name = get_default_model_name(val.nested)
File "env/lib/python3.8/site-packages/flask_accepts/utils.py", line 152, in get_default_model_name
return "".join(schema.name.rsplit("Schema", 1))

AttributeError: 'str' object has no attribute 'name'
`

Can you please add the possibility to support this configuration ?

Unable to decorate routes within a Flask_RESTful Resource class

.../env/lib/python3.5/site-packages/flask_accept/__init__.py", line 39, in __call__
    return self.accept_handlers[mimetype](*args, **kwargs)
TypeError: get() missing 1 required positional argument: 'self'

A much better description of the actual issue can be found in this Stack Overflow article

In short, since we are decorating an instance method, we to set the decorating class up as a descriptor class such that it knows about the instance of the class the function is bound to.

How to return the default response with mime 'application/json' instead of 'text/html'

The response in every case is always 'text/html' like this

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>406 Not Acceptable</title>
<h1>Not Acceptable</h1>
<p>The resource identified by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request. Supported entities are: application/json</p>

Is there any way to send a response in 'application/json' format automatically?

Wildcard media type handling

Hi,

I noticed that Flask-Accept is generating 406 Not Acceptable for wildcard media types.

@app.route('/')
@accept('application/json')
def index():
    return 'OK'

The example above generates 406 for Accept: application/*, Accept: */*, and when no Accept header is present, which essentially means Accept: */*.

According to RFC 2616, */* means all media types are acceptable and application/* means all subtypes of application are acceptable, both accepts application/json. So it might be a bug for Flask-Accept to generate 406.

p.s. There's a test case that requires a 406 for */* which I believe is wrong.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.