Nginx веб-приложение перенаправляет ошибку при отправке формы (перенаправляет на поддельный URL с% 2 C в нем) - PullRequest
2 голосов
/ 26 марта 2020

Прежде всего, я новичок в большей части этого. Я запускаю приложение Flask на удаленном (Linode) сервере Ubuntu. Приложение поставляется с Gunicorn и Nginx.

После недавнего обновления перенаправления стали странно себя вести при отправке формы.

В частности, URL-адрес, на который я перенаправлен, выглядит следующим образом: mywebsite.com% 2Cmywebsite.com / my / ожидаемый / редирект

(будет ясно, mywebsite.com - это базовый URL. При отправке формы я ожидаю перенаправления на mywebsite.com / my / ожидаемый / редирект , однако меня перенаправляют на mywebsite.com% 2Cmywebsite.com / my / ожидается / перенаправление )

Это происходит только на сервере. На моем локальном хосте этой проблемы нет.

Это происходит только когда я отправляю форму (через формы wtf). После отправки формы и получения фиктивного URL-адреса, если я вручную ввожу ожидаемый URL-адрес, я получаю ожидаемую страницу - т.е. сообщение о подтверждении sh о том, что отправка формы прошла успешно, и база данных содержит отправленные данные fre sh. через форму. Поэтому я думаю, что db.commit () работает.

Не все ссылки не работают, я могу успешно переходить от страницы к странице. например, я могу перейти домой со своей панели навигации по этой ссылке в заголовочном файле html для всего сайта.

href="{{ url_for('home') }}"

, и я могу переходить на другие страницы с помощью этих кнопок (я знаю, что вместо этого я должен использовать url_for жестких ссылок)

<button class="w3-bar-item w3-button w3-black" onclick="window.location.href = '/recipes';">Recipes</button>
<button class="w3-bar-item w3-button w3-yellow" onclick="window.location.href = '/newrecipe';">Add New Recipe</button>

Существует три отдельные формы, которые все вызывают эту ошибку. ниже вы увидите их как NewRecipe, NewBoilAddition, StartBoilTimer

Я попытался добавить _external = True к перенаправлению url_for, однако это не удалось с Внутренняя ошибка сервера

Из исследований я понимаю, что% 2 C - это неэкранированная запятая, поэтому я подозреваю, что список URL-адресов передается на перенаправление, или что есть какая-то другая проблема с кодировкой, которую я пропускаю , Я особенно запутался, так как эта ошибка только появилась, когда весь этот код работал до этого. Я попытался выполнить откат до коммита, в котором, я уверен, этой ошибки не было, и ошибка была воспроизводимой в этом коммите. Это заставляет меня подозревать, что это ответственность за gunicorn / nginx, так как сам код приложения был хорош.

Я очень запутался. Любые идеи приветствуются!

init .py

from flask import Flask, render_template, request, g, redirect, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.hybrid import hybrid_property

from forms import NewRecipe, NewBoilAddition, StartBoilTimer

# import sqlite3
import pandas as pd
import json
import sys
import os
import config

from datetime import datetime, timedelta

from flask_gtts import gtts

app = Flask(__name__)

app.config['SECRET_KEY'] = 'xxx'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'

db = SQLAlchemy(app)

class User(db.Model):

    __tablename__ = 'users'

    id          = db.Column(db.Integer, primary_key=True)
    username    = db.Column(db.String(20), unique=True, nullable=False)
    password    = db.Column(db.String(60), nullable=False)
    bevvys      = db.relationship('Bevvy_list', backref='brewer', lazy=True)

    def __repr__(self):
        return f"User('{self.id}', '{self.username}')"

class Bevvy_list(db.Model):

    __tablename__ = 'bevvy_list'

    id              = db.Column(db.Integer, primary_key=True)
    name            = db.Column(db.String(100), unique=True, nullable=False) #Optimise length of this string
    style           = db.Column(db.String(60), nullable=False) #TODO add in validated field, linking to style guide
    abbreviation    = db.Column(db.String(60), nullable=False)
    iteration       = db.Column(db.Integer, nullable=False)
    iteration_of    = db.Column(db.Integer, nullable=False) #id of parent beer, if iteration = 1 then this equals self.id
    batch_size      = db.Column(db.Integer, nullable=False)
    brewday_date    = db.Column(db.DateTime, nullable=False)
    url             = db.Column(db.String(20), nullable=True)
    user_id         = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    boils           = db.relationship('Boil', backref='bevvy', lazy=True)

    def __repr__(self):
        return f"Bevvy_list('{self.id}', '{self.name}')"

class Boil(db.Model):

    __tablename__ = 'boil'

    id              = db.Column(db.Integer, primary_key=True)
    description     = db.Column(db.String(100), unique=False, nullable=False)
    time            = db.Column(db.Integer, unique=False, nullable=False)
    end_datetime    = db.Column(db.DateTime, unique=False, nullable=True)
    brew_id         = db.Column(db.Integer, db.ForeignKey('bevvy_list.id'), nullable=False)

    def __repr__(self):
        return f"Boil('{self.id}', '{self.description}', '{self.time}', '{self.end_datetime}')"

##########################################
#          Build app
##########################################

# home navigation
@app.route("/")
def index():
    return render_template("home.html")

@app.route("/recipes")
def recipes():
    bevs_data = Bevvy_list.query.all()
    return render_template("recipes.html", bevs_data=bevs_data)

@app.route("/edit/<int:recipe_id>", methods=['GET', 'POST'])
def edit(recipe_id):
    form = NewBoilAddition(brew_id=recipe_id)
    if form.validate_on_submit():
        boil_addition = Boil(description=form.description.data, \
                            time=form.time.data, \
                            brew_id=recipe_id,\
                            end_datetime=None)
        db.session.add(boil_addition)
        db.session.commit()
        flash(f'Boil Addition {form.description.data} successfully added ', 'success_boil')
        return redirect(url_for('edit', recipe_id=recipe_id))

    start_timer_form = StartBoilTimer()
    boil_additions = Boil.query.filter(Boil.brew_id == recipe_id).all()
    if start_timer_form.validate_on_submit():
        end_all_timers = datetime.now() + timedelta(minutes=max_value(Boil.query.filter(Boil.brew_id == recipe_id).with_entities(Boil.time).all()))
        for addition in boil_additions:
            addition_end_datetime = end_all_timers - timedelta(minutes=addition.time)
            db.session.query(Boil).filter(Boil.id == addition.id).update({'end_datetime':addition_end_datetime})
            db.session.commit()
        return redirect(url_for('edit', recipe_id=recipe_id))

    recipe = Bevvy_list.query.filter(Bevvy_list.id == recipe_id).all()

    return render_template("edit.html", recipe=recipe[0], form=form, \
        boil_additions=boil_additions, start_timer_form=start_timer_form)

# add a new recipe
@app.route("/newrecipe", methods=['GET', 'POST'])
def new_recipe():
    form = NewRecipe()
    recipe_list = Bevvy_list.query.all()
    list_recipes =[]
    for recipe in recipe_list:
        row = [recipe.id, recipe.name, recipe.brewday_date.strftime("%-d %b %y")]
        list_recipes.append(row)
    if form.validate_on_submit():
        recipe = Bevvy_list(name=form.name.data, style=form.style.data, abbreviation=form.abbreviation.data, iteration=form.iteration.data, \
            iteration_of=form.iteration_of.data, batch_size=form.batch_size.data, brewday_date=form.brewday_date.data, user_id=form.user_id.data)
        db.session.add(recipe)
        href = "/edit/" + str(db.session.query(Bevvy_list).order_by(Bevvy_list.id.desc()).first().id)
        db.session.query(Bevvy_list).order_by(Bevvy_list.id.desc()).first().url = href
        db.session.commit()
        flash(f'Recipe for {form.name.data} successfully added', 'success')
        return redirect(url_for('home'), _external=True)

    return render_template("new_recipe.html", title="New Recipe", form=form, modal=list_recipes)

if __name__ == "__main__":
    app.run()

new_recipe. html

<!DOCTYPE html>

{% extends "layouts.html" %}

{% block content %}

  <!-- Modal -->  
<div class="modal fade" id="exampleModalLong" tabindex="-1" role="dialog" aria-labelledby="exampleModalLongTitle" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLongTitle">All Recipes</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <table class="table table-striped table-hover table-sm">
          <thead class="thead-dark">
            <tr>
              <th scope="col">ID</th>
              <th scope="col">Name</th>
              <th scope="col">Brewday Date</th>
            </tr>
          </thead>
          <tbody>
            {% for list in modal %}
              <tr>
              {% for item in list %}
                <td>{{ item }}</td>
              {% endfor %}
              </tr>
            {% endfor %}
          </tbody>
        </table>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>

<div class="content-section">
  <form method="POST" action="">
    {{  form.hidden_tag() }}
    <fieldset class="form-group">
      <legend class="border-bottom mb-4">Add New Recipe</legend>
      <div class="form-group"> 
        {{ form.name.label(class="form-control-label") }}
        {% if form.name.errors %}
          {{ form.name(class="form-control form-control-lg is-invalid") }}
          <div class="invalid-feedback">
            {% for error in form.name.errors%}
              <span>{{ error }}</span>
            {% endfor %}
          </div>
        {% else %}
          {{ form.name(class="form-control form-control-lg") }}
        {% endif %}

      </div>
      <div class="form-group"> 
        {{ form.style.label(class="form-control-label") }}
        {% if form.style.errors %}
          {{ form.style(class="form-control form-control-lg is-invalid") }}
          <div class="invalid-feedback">
            {% for error in form.style.errors%}
              <span>{{ error }}</span>
            {% endfor %}
          </div>
        {% else %}
          {{ form.style(class="form-control form-control-lg") }}
        {% endif %}
      </div>
      <div class="form-group"> 
        {{ form.abbreviation.label(class="form-control-label") }}
        {% if form.abbreviation.errors %}
          {{ form.abbreviation(class="form-control form-control-lg is-invalid") }}
          <div class="invalid-feedback">
            {% for error in form.abbreviation.errors%}
              <span>{{ error }}</span>
            {% endfor %}
          </div>
        {% else %}
          {{ form.abbreviation(class="form-control form-control-lg") }}
        {% endif %}
      </div>
      <div class="form-group"> 
        {{ form.iteration.label(class="form-control-label") }}
        {% if form.iteration.errors %}
          {{ form.iteration(class="form-control form-control-lg is-invalid") }}
          <div class="invalid-feedback">
            {% for error in form.iteration.errors%}
              <span>{{ error }}</span>
            {% endfor %}
          </div>
        {% else %}
          {{ form.iteration(class="form-control form-control-lg") }}
        {% endif %}
      </div>
      <div class="form-group"> 
        {{ form.iteration_of.label(class="form-control-label") }}
        {% if form.iteration_of.errors %}
          {{ form.iteration_of(class="form-control form-control-lg is-invalid") }}
          <div class="invalid-feedback">
            {% for error in form.iteration_of.errors%}
              <span>{{ error }}</span>
            {% endfor %}
          </div>
        {% else %}
          {{ form.iteration_of(class="form-control form-control-lg") }}
        {% endif %}
        <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModalLong">
          Show recipe list for reference
        </button>
      </div>
      <div class="form-group"> 
        {{ form.batch_size.label(class="form-control-label") }}
        {% if form.batch_size.errors %}
          {{ form.batch_size(class="form-control form-control-lg is-invalid") }}
          <div class="invalid-feedback">
            {% for error in form.batch_size.errors%}
              <span>{{ error }}</span>
            {% endfor %}
          </div>
        {% else %}
          {{ form.batch_size(class="form-control form-control-lg") }}
        {% endif %} 
      </div>
      <div class="form-group"> 
        {{ form.brewday_date.label(class="form-control-label") }}
        {% if form.brewday_date.errors %}
          {{ form.brewday_date(class="form-control form-control-lg is-invalid") }}
          <div class="invalid-feedback">
            {% for error in form.brewday_date.errors%}
              <span>{{ error }}</span>
            {% endfor %}
          </div>
        {% else %}
          {{ form.brewday_date(class="form-control form-control-lg") }}
        {% endif %}
      </div>
      <div class="form-check"> 
        {% if form.user_id.errors %}
          {% for subfield in form.user_id %}
          <table>
            <td>
                <tr>{{ subfield(class="form-check-input is-invalid") }}</tr>
                <tr>{{ subfield.label(class="form-check-label") }}</tr><br>
            </td>
          </table>
          {% endfor %}
          <div class="invalid-feedback">
            {% for error in form.user_id.errors%}
              <span>{{ error }}</span>
            {% endfor %}
          </div>
        {% else %}
          {% for subfield in form.user_id %}
            <table>
              <td>
                  <tr>{{ subfield(class="form-check-input") }}</tr>
                  <tr>{{ subfield.label(class="form-check-label") }}</tr><br>
              </td>
            </table>
          {% endfor %}
        {% endif %}
      </div>
    </fieldset>
    <div class="form-group"> 
      {{ form.submit(class="btn btn-outline-info")  }}
    </div>
  </form>
</div>

{% endblock content %}

РЕДАКТИРОВАТЬ:

/etc/systemd/system/MYAPP.service

[Unit]
Description=Gunicorn instance to serve zym_app
After=network.target

[Service]
User=root
Group=www-data
WorkingDirectory=/home/lph/zym_app/zym
Environment="PATH=/home/lph/zym_app/zym/zym_app_env/bin"
ExecStart=/home/lph/zym_app/zym/zym_app_env/bin/gunicorn --workers 3 --bind unix:zym_app.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target


и / etc / nginx / sites-available / MYAPP

server {
    listen 80;
    server_name mywebsite.com www.mywebsite.com;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/lph/zym_app/zym/zym_app.sock;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}



РЕДАКТИРОВАТЬ 2: похоже, я пропустил 2 и пошел прямо 3 ..

РЕДАКТИРОВАТЬ 3: После устранения этой проблемы, я теперь считаю, что это проблема с конфигурацией nginx. Я протестировал приложение flask на сервере с

gunicorn --bind 0.0.0.0:5000 wsgi:app

Это решило проблему с перенаправлением, приложение работало, как и ожидалось. Поэтому части flask и gunicorn работают нормально, оставляя только nginx.

. Я также удалил линии перенаправления из условных выражений .validate_on_submit (), что также решило проблему при запуске через nginx , Данные формы Fre sh были успешно отправлены в базу данных, однако мне пришлось вручную обновить страницу sh после отправки из-за отсутствия перенаправлений.

Это мой файл конфигурации nginx. / etc / nginx / sites-available / zym_app

server {
    listen 80;    
    server_name mywebsite.com www.mywebsite.com;

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/ZYM/zym/zym_app.sock;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_headers_hash_max_size 512;
        proxy_headers_hash_bucket_size 128;
    }
}

РЕДАКТИРОВАТЬ 4: Попробовав много изменений в параметре nginx config, я закомментировал строки перенаправления, вызывающие сбой, и теперь проблема возникает, когда я нажимаю на моя навигационная ссылка на дом. Вот html в этой ссылке.

href="{{ url_for('home') }}"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...