0

I'm doing a simple shopping website with a order confirmation page but I'm finding that there are duplicate POST requests to my /confim-order route. I have a home page that redirects on POST:

@views.route('/', methods=['GET', 'POST'])
@login_required
def home():
    if request.method == 'POST':
        # save information from the form for later use
        session['text'] = request.form.get('samples')
        session['note'] = request.form.get('note')

        return redirect(url_for('views.confirm_order'))

    return render_template("home.html", user=current_user)

My order confirmation function:

@views.route('/confirm-order', methods=['GET', 'POST'])
@login_required
def confirm_order():
    if request.method == 'POST':
        text = session['text']
        note = session['note']

        session.pop('text', None)
        session.pop('note', None)

        create_order(current_user, text, note)

        return redirect(url_for('views.home'))
    elif request.method == 'GET':
        text = session['text']
        note = session['note']

        sample_list = get_samples(text, note)

        return render_template("confirm.html", user=current_user, sample_list=sample_list)

There's no JavaScript in the HTML template. What's in confirm.html is essentially:

  <form method="POST">
    <div class="form-group">
      <label for="cc">Credit Card</label>
      <input type="text" class="form-control" id="cc" name="cc" placeholder="Credit Card Number" />
    </div>
    Click button to place order:
    <p></p>
    <div align="right">
      <button type="submit" id="submit_btn" class="btn btn-success">Place Order</button>
    </div>
  </form>

This is what I see: sometimes, clicking submit works fine. Most times clicking submit results in two POST requests to confirm_order() and then I get a "This site can’t be reached" message in my browser at http://localhost:5000/confirm-order. I've been at this for almost an entire day. I put some print statements that seem to suggest the first POST to /confirm-order is initiated correctly from the template rendered we reach /confirm-order by GET from home: /. The second POST to /confirm-order came immediately after first the POST from within POST of /confirm-order. When that happens, I get the "This site can't be reached" message and I find that duplicate orders have been created.

I've searched online and most people that have duplicate POST issues are using JavaScript along with the form submission button. But my page doesn't use Javascript. If someone sees what's wrong, any help is greatly appreciated. Thank you.

EDIT: Here's the create_order() function in case something in there is causing the problem:

def create_order(user, text, note):
    new_order = Order(user_id=current_user.id, text=text,
        status='submitted', note=note)

    db.session.add(new_order)
    db.session.commit()
Rainie
  • 1
  • 2
  • What does your create_order function do and where is it accessing the current_user variable from? – osint_alex Aug 30 '21 at 00:44
  • @osint_alex `create_order()` basically adds an entry to the orders table in the database. It is in the the same `views.py` file as the code snippets above. It uses `current_user.id` to associate the order with the submitting user. I've added the contents of the function in the main question. Thank you. Any help is greatly appreciated. I'm pulling my hair out over this strange bug. – Rainie Aug 30 '21 at 02:10

1 Answers1

0

I'm not 100% sure this is the issue but it's the most likely thing I can think of.

I think there are some issues in your html since you don't specify the url for action. I would also use an input tag rather than button for submit.

More generally, I would also recommend following a few of the things mentioned here. So use the data in request.form rather than in the session object since you can control and validate that more explicitly within Flask (for instance if this is going into production you may want to implement WTF Forms for security reasons to prevent CSRF).

<form action="/confirm-order" method="post">
    <div class="form-group">
      <label for="po">Purchase Order</label>
    <div class="form-group">
      <label for="cc">Credit Card</label>
      <input type="text" class="form-control" id="cc" name="cc" placeholder="Credit Card Number" />
    </div>
    Click button to place order:
    <p></p>
    <div align="right">
      <input type="submit" id="submit_btn" class="btn btn-success">Place Order</input>
    </div>
  </form>
osint_alex
  • 952
  • 3
  • 16
  • thank you for the WTF forms suggestion, I will look into how to use that. As for the extra action in the `
    `. I added it but unfortunately that didn't fix the double POST then "site not found" issue. The reason I didn't use `request.form` is that the POST comes from the confirmation page itself and that page doesn't have any forms. The forms are in the `home()` page (you can see that I do use `request.form` in that function). I had to save the information in `session` so I can have it available when process the confirmation page.
    – Rainie Aug 30 '21 at 20:01