基于aiohttp+jinja2实现Python本地服务处理前端表单数据
完全可以不依赖Django、Flask,基于你选定的aiohttp+jinja2技术栈就能完成前后端表单对接,以下是可直接运行的实现步骤:
1. 安装依赖
执行以下命令安装所需包,不需要额外安装其他重量级Web框架:
pip install aiohttp aiohttp-jinja2
2. 目录结构准备
在工作目录下创建两个对象:
- 主程序文件
app.py - 模板文件夹
templates,后续调整完成的前端页面就存在这个文件夹里,命名为index.html
3. 前端页面调整
你原有的前端代码需要做三处小修改才能正常对接后端:
- 给两个输入框加参数标识,方便后端取值
- 表单提交时阻止默认的页面跳转行为,改用异步请求把用户输入发给后端
- 拿到后端返回的扫描结果后,动态渲染到页面的消息容器中
调整后的完整index.html代码如下(原有样式完全保留):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Port Scanner</title> <style> html { font-family: Verdana, "sans-serif"; } body { min-height: 100vh; background: black no-repeat; } h1 { text-align: center; color: white; text-shadow: 0 3px 5px rgba(150, 150, 150, 0.8); } #root { display: flex; flex-direction: column; align-items: center; } #messages { width: 70%; } .message { background-color: white; width: 100%; padding: 20px; border-radius: 10px; -webkit-transition: 0.3s; -moz-transition: 0.3s; transition: 0.3s; -webkit-box-shadow: 0px 5px 5px 0px rgba(0, 0, 0, 0.1); -moz-box-shadow: 0px 5px 5px 0px rgba(0, 0, 0, 0.1); box-shadow: 0px 5px 5px 0px rgba(0, 0, 0, 0.1); box-sizing: border-box; margin-bottom: 10px; } .message:hover { -webkit-transition: 0.3s; -moz-transition: 0.3s; transition: 0.3s; -webkit-box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.5); -moz-box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.5); box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.5); } .message__created { font-size: 12px; color: darkgrey; text-align: right; } .message__text { font-size: 20px; color: black; margin-top: 5px; } .message__ratings { display: flex; justify-content: space-between; font-size: 16px; font-weight: 400; margin-top: 5px; } .form { width: 70%; margin-bottom: 20px; display: flex; flex-direction: column; gap: 60px; } .form__button { text-align: center; margin-top: 150px; background-color: white; cursor: pointer; } .form__input, .form__button { box-sizing: border-box; width: 100%; font-size: 18px; outline: none; border: none; padding: 20px; border-radius: 15px; -webkit-transition: 0.3s; -moz-transition: 0.3s; transition: 0.3s; -webkit-box-shadow: 0px 5px 5px 0px rgba(0, 0, 0, 0.1); -moz-box-shadow: 0px 5px 5px 0px rgba(0, 0, 0, 0.1); box-shadow: 0px 5px 5px 0px rgba(0, 0, 0, 0.1); } .form__input:focus, .form__button:hover { -webkit-transition: 0.3s; -moz-transition: 0.3s; transition: 0.3s; -webkit-box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.5); -moz-box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.5); box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.5); } </style> </head> <body> <div> <h1>Port Scanner</h1> <div id="root"> <form class="form" onsubmit="sendMessage(event)"> <input id="message_text" name="ip" class="form__input" type="text" placeholder="IP adress" required/> <input id="message_text1" name="port_range" class="form__input" type="text" placeholder="Range of ports (eg: 1-1000)" required/> <button class="form__button" type="submit">Scan</button> </form> <div id="messages"> </div> </div> </div> </body> <script type="application/javascript"> const sendMessage = async (e) => { e.preventDefault(); alert('Please wait for the scanning!'); const ip = document.getElementById('message_text').value; const portRange = document.getElementById('message_text1').value; const resp = await fetch('/scan', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ip: ip, port_range: portRange}) }); const result = await resp.json(); const msgContainer = document.getElementById('messages'); const msgEl = document.createElement('div'); msgEl.className = 'message'; let openPortsHtml = result.open_ports.length > 0 ? result.open_ports.join(', ') : 'No open ports'; msgEl.innerHTML = ` <div class="message__created">Scan time: ${new Date().toLocaleString()}</div> <div class="message__text">Target IP: ${result.ip}</div> <div class="message__text">Port range: ${result.port_range}</div> <div class="message__text">Open ports: ${openPortsHtml}</div> `; msgContainer.prepend(msgEl); document.getElementById('message_text').value = ''; document.getElementById('message_text1').value = ''; } </script> </html>
4. 服务端逻辑编写
app.py中实现三个核心模块:aiohttp服务初始化、jinja2模板配置、路由处理(首页返回前端页面、/scan接口接收参数执行扫描返回结果),内置异步端口扫描逻辑,不需要额外依赖第三方扫描库,完整代码如下:
import asyncio from aiohttp import web import aiohttp_jinja2 import jinja2 # 异步单端口扫描逻辑 async def scan_port(ip: str, port: int, timeout: float = 1.0): try: fut = asyncio.open_connection(ip, port) reader, writer = await asyncio.wait_for(fut, timeout=timeout) writer.close() await writer.wait_closed() return port except: return None # 批量端口扫描 async def run_scan(ip: str, port_range: str) -> list: if '-' in port_range: start_port, end_port = map(int, port_range.split('-')) else: start_port = end_port = int(port_range) tasks = [scan_port(ip, port) for port in range(start_port, end_port + 1)] results = await asyncio.gather(*tasks) return sorted([p for p in results if p is not None]) # 首页路由,返回前端页面 @aiohttp_jinja2.template('index.html') async def index(request): return {} # 扫描接口,处理前端提交的参数 async def scan_handler(request): try: data = await request.json() ip = data.get('ip', '').strip() port_range = data.get('port_range', '').strip() if not ip or not port_range: return web.json_response({'code': 400, 'msg': 'Missing parameters'}, status=400) open_ports = await run_scan(ip, port_range) return web.json_response({ 'code': 200, 'ip': ip, 'port_range': port_range, 'open_ports': open_ports }) except Exception as e: return web.json_response({'code': 500, 'msg': f'Scan error: {str(e)}'}, status=500) # 初始化应用 def create_app(): app = web.Application() aiohttp_jinja2.setup(app, loader=jinja2.FileSystemLoader('templates')) app.add_routes([ web.get('/', index), web.post('/scan', scan_handler) ]) return app if __name__ == '__main__': app = create_app() web.run_app(app, host='127.0.0.1', port=8080)
5. 运行验证
执行命令python app.py启动服务,打开浏览器访问http://127.0.0.1:8080即可使用,输入IP和端口范围点击Scan按钮,等待扫描完成后结果会自动展示在页面上。
提示:端口扫描属于敏感操作,该服务仅建议在本地localhost环境运行,请勿暴露到公网;如果需要提升可用性,可以自行在接口层增加IP格式校验、端口范围合法性校验、扫描频率限制逻辑。
内容的提问来源于stack exchange,提问作者Parcurcik
相关产品推荐
相关产品推荐

