工作在请求上下文之外的错误 – 如何在普通函数中删除用户会话

huangapple go评论58阅读模式
英文:

Working outside of request context error - how do I delete user session in a normal function

问题

以下是您要翻译的部分:

"我正在尝试在页面关闭足够长时间且用户未重新打开页面的情况下删除用户的会话。
为了测试它,您只需打开网页,然后关闭它,服务器仍然在运行。

以下是演示我的问题的示例代码:"

from flask import Flask, session, render_template, url_for, redirect
from flask_socketio import SocketIO, join_room
from flask_session import Session
from threading import Thread
from time import sleep
app = Flask(__name__)
app.config['SECRET_KEY'] = 'temporary2'
app.config['SESSION_TYPE'] = 'filesystem'
Session(app)
socketio = SocketIO(app, manage_session=False)
deletelist = {}
@app.route('/')
def index():
    return render_template('wait.html')
@app.route('/dir')
def foo():
    if "name" in session:
        name = session['name']
        return f"Hello, {name}!"
    else:
        return redirect(url_for("index"))

@socketio.on('log')
def connection(request):
    if "name" not in session:
        join_room('waiting')
        session['name'] = 'craig'
        session.modified = True
        socketio.emit('redirect', url_for('foo'), room='waiting')
    else:
        deletelist[session['name']]= False #this line stops the user session being deleted if they relog soon enough e.g. reloading the page
        print('relogin')
def clear_session():
    with app.app_context():
        session.clear()
        print('Session cleared')
        
@socketio.on('disconnect')
def disconnect():
    def sessiondelete(sess,t):
        if deletelist[sess]==False:
            print('cancelled')
            del deletelist[sess]
        elif t == 5:#the time would be more than 5 seconds this is just to test
            print('cleared')
            clear_session()
            del deletelist[sess]
        elif deletelist[sess]==True:
            print('poll')
            sleep(1)
            sessiondelete(sess,t+1)
    deletelist[session['name']]= True #error arises if you just store session, as it is not hashable
    print('init')
    worker = Thread(target=sessiondelete, args=(session['name'],0))# I create seperate thread so the rest of the server can still function for other users
    worker.start()
if __name__ == '__main__':  
   socketio.run(app, port=5200, debug=True)

"断开连接和重新登录的功能是有效的,但当它达到删除会话函数中的会话清除时,它将引发错误。我尝试从断开连接函数和app.app_context()传递会话,但无济于事。错误与清除函数没有flask和socket装饰器有关,因此无法处理请求?

如果您想了解控制台日志,下面是示例代码中的一部分:

init
poll
poll
poll
poll
poll
going to clear
File "/Volumes/src/testing/test.py", line 46, in sessiondelete
clear_session(session)
File "/Volumes/src/testing/test.py", line 35, in clear_session
session.clear()
RuntimeError: Working outside of request context.

这通常意味着您尝试使用需要活动HTTP请求的功能。请查阅有关如何避免此问题的测试文档。"

如果您需要HTML部分,请提供HTML代码,我将为您翻译。

英文:

So I am trying to delete the user's session if the page has been closed long enough and the user has not reopened the page again.
So to test it you just open the webpage and then close it with the server still up.

Here is some example code demonstrating my problem:

from flask import Flask, session, render_template, url_for, redirect
from flask_socketio import SocketIO, join_room
from flask_session import Session
from threading import Thread
from time import sleep
app = Flask(__name__)
app.config['SECRET_KEY'] = 'temporary2'
app.config['SESSION_TYPE'] = 'filesystem'
Session(app)
socketio = SocketIO(app, manage_session=False)
deletelist = {}
@app.route('/')
def index():
return render_template('wait.html')
@app.route('/dir')
def foo():
if "name" in session:
name = session['name']
return f"Hello, {name}!"
else:
return redirect(url_for("index"))
@socketio.on('log')
def connection(request):
if "name" not in session:
join_room('waiting')
session['name'] = 'craig'
session.modified = True
socketio.emit('redirect', url_for('foo'), room='waiting')
else:
deletelist[session['name']]= False #this line stops the user session being deleted if they relog soon enough e.g. reloading the page
print('relogin')
def clear_session():
with app.app_context():
session.clear()
print('Session cleared')
@socketio.on('disconnect')
def disconnect():
def sessiondelete(sess,t):
if deletelist[sess]==False:
print('cancelled')
del deletelist[sess]
elif t == 5:#the time would be more than 5 seconds this is just to test
print('cleared')
clear_session()
del deletelist[sess]
elif deletelist[sess]==True:
print('poll')
sleep(1)
sessiondelete(sess,t+1)
deletelist[session['name']]= True #error arises if you just store session, as it is not hashable
print('init')
worker = Thread(target=sessiondelete, args=(session['name'],0))# I create seperate thread so the rest of the server can still function for other users
worker.start()
if __name__ == '__main__':  
socketio.run(app, port=5200, debug=True)

The functioniality of the disconnect and relog works but it will raise an error when it reaches the session clear in the delete session function. I have tried passing the session from the disconnect function and app.app_context() to no avail. The error has something to do that the function clearing the function does not have the flask and socket decorators, so it cannot process the request?

The console logs if you are wondering:
init
poll
poll
poll
poll
poll
going to clear
File "/Volumes/src/testing/test.py", line 46, in sessiondelete
clear_session(session)
File "/Volumes/src/testing/test.py", line 35, in clear_session
session.clear()
RuntimeError: Working outside of request context.

This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem.

And the html if you need

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
Waiting...
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
<script>
const socket = io();
socket.emit("log", { data: "connection" });
socket.on('redirect', (dest) => {
window.location = dest;
});
</script>
</body>
</html>

答案1

得分: 0

会话仅在事件处理程序的上下文中可访问。当您启动后台线程时,与客户端的关联丢失,线程只是一个线程,它不知道客户端是谁,因此无法从该客户端或任何其他客户端访问会话。

我不确定我理解为什么您要使用如此复杂的解决方案。如果您在处理程序中直接运行等待循环,我认为一切都应该正常工作。并且这不应该阻止其他客户端发送或接收事件。

英文:

The session is only accessible in the context of the event handlers. When you start a background thread the association with a client is lost, the thread is just a thread, it does not know who the client is, so it has no way to access the session from that or any other client.

I'm not sure I understand why you use such a complicated solution. If you run your wait loop directly in your handler, I think everything should work. And this should not block other clients from sending or receiving events.

huangapple
  • 本文由 发表于 2023年6月22日 17:07:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/76530266.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定