Short版本:我正在尝试用烧瓶作为后端制作电子应用程序。我要做的部分内容涉及获取会话变量或初始化该会话变量。这是行不通的,因为即使会话变量初始化,也无法识别会话变量,从而导致每次调用路由时都会创建新变量。我怀疑这是我的CORS配置或缺乏的问题,但是我不知道怎么了或如何解决。因此,这篇文章
长版本:我的应用程序采用表单的内容并将其传递给后端,然后将其添加为节点,然后将其附加到链接列表中,然后稍后可以访问。我希望节点的一个字段,细节是唯一的。而且,为此,我想做到这一点,以便用户不能提交“详细信息”已经存在的表格。我试图通过以下JS代码来实现这一目标:
/* INPUT VALIDATION */
async function checkUniqueDetail(detail_to_check){
try{
const response=await fetch(`${SERVER_URL}/check_detail/${detail_to_check}`,{
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
const data=await response.json();
const existing_details=data.detail_list;
console.log("Existing details:", existing_details);
console.log("Checking detail:", detail_to_check);
const result=data.result;
console.log("Result is:",result);
if(result=="True"){
return false;
}else{
console.log("The detail is unique");
return true;
}
}catch(e){
console.error(e);
return false;
}
}
/* FORM SUBMISSION */
function confirmFormSubmit(formData){
if(confirm("Are you sure you want to submit?")){
const qd=formData.get("detail");
console.log("q_detail is:",qd);
/* Form validation */
checkUniqueDetail(qd).then(result=>{
isUniqueDetail=result;
console.log(qd,"unique?:",isUniqueDetail);
/* If validated, send info */
if(isUniqueDetail==true){
sendForm();
sendDetail(qd);
message.innerText="Question added!"
}else{
message.innerText="A question with this detail already exists. Please try again"
}
});
}
}
form.addEventListener("submit", async (event)=>{
event.preventDefault();
const fData = new FormData(form);
confirmFormSubmit(fData);
});
this呼叫看起来像这样的烧瓶应用程序的路线:
# ROUTES
from flask import Flask, request, session
from flask_session import Session
from flask_cors import CORS
import redis, json
#for forcibly clearing session files
import atexit
app = Flask(__name__)
# Configurations
app.config["SECRET_KEY"]="change_later"
app.config["SESSION_TYPE"] = "redis"
app.config["SESSION_PERMANENT"] = False
r = redis.from_url('redis://127.0.0.1:6379')
app.config['SESSION_REDIS'] = r
def test_redis_connection(redis_session):
"""Check that Redis is connected to"""
try:
redis_session.ping() # Check if Redis is alive
print("Redis connection successful!")
except redis.exceptions.ConnectionError as e:
print(f"Redis connection error: {e}")
exit() # Or handle the error appropriately
test_redis_connection(r)
app.config["CORS_HEADERS"] = "Content-Type"
# Initialize Plugins
sess=Session()
sess.init_app(app)
CORS(app,resources={r"/*": {"origins": "http://localhost*"}},)
#check that the server's running and connected
@app.route("/", methods=["GET","POST"])
def check():
return {"result":"Server active!"}
### ERROR!!!: This only works as a Flask app
# It seems like every time this is run,
# it makes a new session variable, rather than getting the old one
@app.route("/add_detail/<detail>",methods=["GET","POST"])
def add_detail_to_list(detail):
"""Add a q_detail to a list of q_details"""
# Initialize the list if it doesn't exist
if 'lst' not in session:
print("Session variable not found. Initializing...")
session['lst'] = json.dumps([])
session.modified = True
# Append to the list
lst:list[str]=json.loads(session['lst'])
print("Before appending:",lst)
lst.append(detail)
print("After appending:",lst)
session['lst'] = json.dumps(lst)
session.modified = True
return {"response":f"{lst}"}
@app.route("/get_all_details",methods=["GET"])
def get_all_details():
details:list[str]=session.get("lst",[])
return {"result":details}
@app.route("/check_detail/<detail>")
def check_detail(detail):
details:list[str]=session.get("lst",[])
if detail in details:
return {"result":"True","detail_list":details}
else:
return {"result":"False","detail_list":details}
## NOTE: Make sure this works in production
### ERROR!!! Doesn't work with Electron app
# Maybe due to how the Flask app is killed?
def clear_redis_sessions(redis_session):
"""Clears all session data from Redis."""
try:
for key in redis_session.keys("session:*"): # Important: Use a pattern to only delete session keys
redis_session.delete(key)
print("Redis sessions cleared.")
except Exception as e:
print(f"Error clearing Redis sessions: {e}")
atexit.register(clear_redis_sessions,redis_session=r) # Register the cleanup function
# print(app.url_map)
if __name__ == "__main__":
app.run(debug=True)
IT在评论中说了这一点,但是错误发生在“/add_detail/”路由中,特别是在初始if语句中。它每次都会触发,创建一个新的会话[“ LST”]变量,而不是获取旧会话。这意味着前端获得的列表始终是空的,因此细节总是标记为唯一的,并且始终允许发送表单。 这是电子应用程序的屏幕截图,显示了这种情况:
以及我的redis-cli的屏幕截图显示了创建的两个会话键:
我怀疑发生这种情况的原因是由于跨站点请求的性质。这是因为如果我独立运行烧瓶应用程序,则不会发生此问题:
但是如果我使用卷发:
我启用了CORS,但我怀疑我没有正确配置它。但是,也许这也与我如何连接到烧瓶应用有关。这是我的主要。JS,以防万一:
const { app, BrowserWindow } = require('electron');
const { exec } = require("child_process");
/**
* Connects to the Flask app
*/
const connectToFlask=function(){
let python;
//test version
python = require('child_process').spawn('py', ['./py/test_routes.py']);
//executable version
//python = require('child_process').execFile("routes.exe");
python.stdout.on('data', function (data) {
console.log("FLASK RUNNING! data:", data.toString('utf8'));
});
python.stderr.on('data', (data) => { // when error
console.error(`stderr: ${data}`);
console.log(`stderr: ${data}`);
});
python.on("close", (code)=>{
console.log(`child process exited with code ${code}`);
});
}
/**
* Create a new BrowserWindow that's connected to Flask with index.html as its UI
*/
const createWindow = () => {
const win = new BrowserWindow({
width: 800,
height: 600
});
connectToFlask();
win.loadFile('index.html');
}
/**
* When all the windows close, quit the app
*/
app.on('window-all-closed', () => {
if (process.platform !== 'darwin'){
/* Kill the Flask app on close too */
exec("taskkill /f /t /im python.exe", (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
console.log(`stdout: ${stdout}`);
console.log(`stderr: ${stderr}`);
console.log('Flask process terminated');
});
app.quit();
}
});
/**
* Wait for the app to be ready, then create a new window
*/
app.whenReady().then(() => {
createWindow();
});
没有人知道我在做什么错以及如何解决?
提前感谢您即使您使用redis保存会话,cookie用于存储会话ID。 因此,有必要允许服务器端上有cookie的请求,并与cookie一起从客户端发送它们。
要随请求发送cookie,请添加credentials: 'include'
因此,也可以在服务器端接收数据,将属性添加到烧瓶cors的初始化中。
fetch(url, { credentials: 'include' })
.then(resp => resp.json())
.then(data => data.result);
这里是基于您的代码而没有Redis的整个简化示例。
supports_credentials=True
CORS(app, supports_credentials=True)