How allow HTTP methods "PUT" and "DELETE" in Flask?

Nincha picture Nincha · Aug 8, 2013 · Viewed 7k times · Source

I'm beginning in Python and I try to do PUT and DELETE methods like this:

import gamerocket
from flask import Flask, request, render_template
app = Flask(__name__)

gamerocket.Configuration.configure(gamerocket.Environment.Development,
                                apiKey = "my_apiKey",
                                secretKey = "my_secretKey")

@app.route("/update_player", methods = ["PUT"])
def update_player():
    result = gamerocket.Player.update(
        "a_player_id",
        {
            "name" : "bob_update",
            "emailHash" : "[email protected]",
            "totalPointsAchievement" : 1
        }
    )

if result.is_success:
    return "<h1>Success! " + result.player.id + " " + result.player.name +   "</h1>"
else:
    return "<h1>Error " + result.error + ": " + result.error_description

if __name__ == '__main__':
app.run(debug=True)

but I get a HTTP 405 error Method Not Allowed

The method is not allowed for the requested URL.

Could you help me?

Edit: Here is how I call the method:

class PlayerGateway(object):
    def update(self, player_id, params={}):
    response = self.config.http().put("/players/" + player_id, params)
    if "player" in response:
        return SuccessfulResult({"player": Player(self.gateway,response["player"])})
    elif "error" in response:
        return ErrorResult(response)
    else:
        pass

Next in Http:

class Http(object):
    def put(self, path, params={}):
        return self.__http_do("PUT", path, params)
    def delete(self, path, params={}):
        return self.__http_do("DELETE", path, params)

    def __http_do(self, http_verb, path, params):

        http_strategy = self.config.http_strategy()

        full_path = self.environment.base_url + "/api/" + self.config.api_version() + path
        params['signature'] = self.config.crypto().sign(http_verb, full_path, params,
                              self.config.secretKey)

        params = self.config.sort_dict(params)

        request_body = urlencode(params) if params !={} else ''

        if http_verb == "GET":
            full_path += "?" + request_body
        elif http_verb == "DELETE":
            full_path += "?" + request_body

        status, response_body = http_strategy.http_do(http_verb, full_path, 
                            self.__headers(),request_body)

        if Http.is_error_status(status):
            Http.raise_exception_from_status(status)
        else:
            if len(response_body.strip()) == 0:
                return {}
            else:
                return json.loads(response_body)

    def __headers(self):
        return {
            "Accept" : "application/json",
            "Content-type" : "application/x-www-form-urlencoded",
            "User-Agent" : "Gamerocket Python " + version.Version,
            "X-ApiVersion" : gamerocket.configuration.Configuration.api_version()
        }

And to determine request strategy:

import requests

class RequestsStrategy(object):
    def __init__(self, config, environment):
        self.config = config
        self.environment = environment

    def http_do(self, http_verb, path, headers, request_body):

        response = self.__request_function(http_verb)(
            path,
            headers = headers,
            data = request_body,
            verify = self.environment.ssl_certificate,
        )

        return [response.status_code, response.text]

    def __request_function(self, method):
        if method == "GET":
            return requests.get
        elif method == "POST":
            return requests.post
        elif method == "PUT":
            return requests.put
        elif method == "DELETE":
            return requests.delete

Answer

avoid3d picture avoid3d · Sep 17, 2013

I have not attempted to run your code but I believe I can see what is going on.

class PlayerGateway(object):
    def update(self, player_id, params={}):
    response = self.config.http().put("/players/" + player_id, params)
    if "player" in response:
        return SuccessfulResult({"player": Player(self.gateway,response["player"])})
    elif "error" in response:
        return ErrorResult(response)
    else:
        pass

You are calling the route base_url/api/version/players/ with that calling code.

However you are registering the route "/update_player" with the PUT method

Without seeing the rest of your Flask app, I cannot tell if this is the problem, but you have to define which methods are allowed for EACH root. :)