Search code examples
pythonhtmlbottle

POST method 405 and 404 errors in bottle


I am very very new to Python and Bottle and how server requests/responses work, and there are still quite a few basic and general ideas that I am trying to wrap my head around. That being said, there's some fundamental logic that I am missing out on. In order to familiarize myself with bottle, python, html, etc. I have this very basic project. I have a form written in html (stored in tpl file) with some radios, checkboxes, select menus, and text input fields. I want to store all of those inputs and print them out to a new results page when the user clicks submit. As a sort of base trial to store all of the variables, I started with just the first name variable, and I wanted to print the stored variable to the url ('/fname'). If I can get this working then my plan is to change the post method to route to ('/fanpage/results') and then return all of the information on that page (possibly using another template, but I haven't gotten that far yet)

from bottle import route, run, post, get, request, template, static_file

HOST = 'localhost'

(...)

@post('/fname')
def show_fname():
    fname = request.forms.get('fname')
    return "<p>Your name is {{fname}}.</p>"

(...)

run(host=HOST, port=8080, debug=True)
(...)
<form action="/fanpage/results" class="needs-validated">
(...)
  <div class="row">
    <div class="col>
      <label for="fname" class="form-label">First name:</label>
      <input type="first name" class="form-control" id="fname" name="fname" placeholder="Enter first name" title="Please enter your first name" required>
      <div class="invalid-feedback">Please fill out this field to continue.</div>
    </div>
  </div>
(...)
</form>

I have some route and get methods before the post method to send the tpl files to the server to display the form, which work. When I try to go to localhost:8080/fname, though, I get a 405 error - method not allowed. I feel like I am directly copying what I've seen in the documentation and online and I'm not sure why the url isn't working. If I try to change the route for the post method to the url ('/fanpage/results'), I get a 404 not found error.

Also, a basic question: If I'm working with multiple template files and trying to pull information, such as the first name, from just one of the templates, how does it know which template to pull data from? Could that be the reason for the 405 error?


Solution

  • Also, a basic question: If I'm working with multiple template files and trying to pull information, such as the first name, from just one of the templates, how does it know which template to pull data from? Could that be the reason for the 405 error?

    No data is "pulled" from templates as such. The workflow is like this:

    1. Client sends a request to a server's route. The request is typically a GET or POST request method, and the route is something like localhost:8080/fname. The server has to expose a matching request method and route.
    2. The server takes action on the data sent with the request (if any) and sends a response. The response might be a simple status code (like a 405 Method Not Allowed) or it could have a body data payload along with it. The body could be an HTML page, CSS, JSON or something else. To build an HTML page, the server will want to inject dynamic data into a template. Other times, the HTML is purely static (unchanging).
    3. When the response is received from the server, the client (which could be a web browser or a utility like curl or postman) shows the response to the user in whatever form it's designed to (web browsers render the HTML, for example, which often involves kicking off more requests to get relevant CSS files, scripts or images included in the HTML).

    Now, let's say the response was HTML, possibly the result of rendering a template on the server. If that HTML has a form in it (or JS code that triggers a request asynchronously, avoiding a page refresh), the user can re-submit the form and kick off the process all over again.

    One more thing: when you navigate your browser to, say https://en.wikipedia.org or localhost:8080/fname, that's a GET request, not a POST request, which explains the 405 response code since you told /fname to only respond to POST requests.

    If you want to test a POST route, either use curl, postman or write a simple GET route that responds with a form that looks like <form action="/fname" method="post">.

    Here's a minimal example of all of this wired together:

    server.py

    from bottle import post, request, route, run, template
    
    @route("/")
    def index():
        return template("index.tpl")
    
    @post("/fname")
    def fname():
        return template("fname.tpl", fname=request.forms.get("fname"))
    
    if __name__ == "__main__":
        run(host="localhost", port=8080, debug=True, reloader=True)
    

    index.tpl

    <!DOCTYPE html>
    <html>
    <body>
      <form action="/fname" method="post">
        <label for="fname">First name:</label>
        <input name="fname" placeholder="Enter first name" required>
        <input type="submit">
      </form>
    </body>
    </html>
    

    fname.tpl

    <!DOCTYPE html>
    <html>
    <body>
      <p>Your name is {{fname}}.</p>
      <p><a href="/">Back</a></p>
    </body>
    </html>
    

    You can start a server, then navigate to http://localhost:8080 to fill out the form and see the result, or use a utility like curl on the command line to POST the fname:

    PS C:\Users\me> curl -X POST -F 'fname=foo' http://localhost:8080/fname
    <!DOCTYPE html>
    <html>
    <body>
      <p>Your name is foo.</p>
      <p><a href="/">Back</a></p>
    </body>
    </html>
    

    You can see the server responded with HTML, the result of injecting the fname variable from the POST payload into the fname.tpl template. A browser would render this as a webpage, but on the command line you just see the HTML code. In a JSON API, the response on the command line would be easier to manipulate, but here the server is designed for clients using browsers.