1

I'm testing API CRUD with browsable web API flask implementation, but the browser seems to send unexpected requests to the API.

Here is the code I'm testing :

from flask import request, url_for
from flask_api import FlaskAPI, status, exceptions

app = FlaskAPI(__name__)


notes = {
    0: 'do the shopping',
    1: 'build the codez',
    2: 'paint the door',
}

def note_repr(key):
    return {
        'url': request.host_url.rstrip('/') + url_for('notes_detail', key=key),
        'text': notes[key]
    }


@app.route("/", methods=['GET', 'POST'])
def notes_list():
    """
    List or create notes.
    """
    if request.method == 'POST':
        note = str(request.data.get('text', ''))
        idx = max(notes.keys()) + 1
        notes[idx] = note
        return note_repr(idx), status.HTTP_201_CREATED

    # request.method == 'GET'
    return [note_repr(idx) for idx in sorted(notes.keys())]


@app.route("/<int:key>/", methods=['GET', 'PUT', 'DELETE'])
def notes_detail(key):
    """
    Retrieve, update or delete note instances.
    """
    if request.method == 'PUT':
        note = str(request.data.get('text', ''))
        notes[key] = note
        return note_repr(key)

    elif request.method == 'DELETE':
        notes.pop(key, None)
        return '', status.HTTP_204_NO_CONTENT

    # request.method == 'GET'
    if key not in notes:
        raise exceptions.NotFound()
    return note_repr(key)


if __name__ == "__main__":
    app.run(host='0.0.0.0', debug=True)

When I tried to delete a specific note given its id (key) from the example notes list, the browser sends a POST method instead of DELETE, which is not supported by the route.

browser request debugging

The base render template is located here where you can see DELETE button statement at line 104.

I edited the library code and move the form method there from POST to DELETE thinking it could solve the problem this way :

base rendering template DELETE block code

104         {% if 'DELETE' in allowed_methods %}
105             <form class="button-form" action="{{ request.url }}" method="DELETE" class="pull-right">
106                 <!-- csrf_token -->
107                 <input type="hidden" name="_method" value="DELETE" />
108                 <button class="btn btn-danger js-tooltip" title="Make a DELETE request on the resource">DELETE</button>
109             </form>
110         {% endif %}

But not, the browser is now sending a GET request with the query string _method=DELETE, instead of DELETE request/method.

browser rendering after code update

Everything is OK when sending request to the API using curl

Can one of you guys with good flask html rendering skills check this out and test on its side?

nixmind
  • 2,060
  • 6
  • 32
  • 54

2 Answers2

1

Never seen form with method=DELETE. MDN documentation says it should be either a GET or POST. Also see this old stackoverflow question that also says it is not supported in forms.

The template you're referencing is for an API so it seems to me that they are supporting direct calls to the API (such as with curl or Postman which would support PUT, DELETE, etc) and calls via forms (which would only be GET or POST)

You should keep your original code where method = POST and add POST as a method for def notes_detail i.e. you should have

    @app.route("/<int:key>/", methods=['GET', 'POST', 'PUT', 'DELETE'])
    def notes_detail(key):
NoCommandLine
  • 5,044
  • 2
  • 4
  • 15
0

I abandoned the effort of making the browser sending DELETE requests, and dealed with form hiden inputs, keeping POST as the form's method

104         {% if 'DELETE' in allowed_methods %}
105             <form class="button-form" action="{{ request.url }}" method="POST" class="pull-right">
106                 <!-- csrf_token -->
107                 <input type="hidden" name="_method" value="DELETE" />
108                 <button class="btn btn-danger js-tooltip" title="Make a DELETE request on the resource">DELETE</button>
109             </form>
110         {% endif %}
nixmind
  • 2,060
  • 6
  • 32
  • 54