import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox
import requests
from requests.exceptions import RequestException
import json
from datetime import datetime
import re
class RequestSimulator(tk.Tk):
def __init__(self):
super().__init__()
self.title("HTTP请求模拟器")
self.geometry("800x650")
self.method_var = tk.StringVar(value="GET")
self.json_tags = {
"key": "#0066cc",
"string": "#009933",
"number": "#ff6600",
"boolean": "#cc00cc",
"null": "#888888",
"brace": "#333333",
"colon": "#333333",
"comma": "#333333"
}
self.create_widgets()
self.init_json_highlight_tags()
def create_widgets(self):
top_frame = ttk.Frame(self, padding="10")
top_frame.pack(fill=tk.X, padx=10, pady=5)
top_frame.columnconfigure(2, weight=1)
method_label = ttk.Label(top_frame, text="请求方法:")
method_label.grid(row=0, column=0, padx=5, pady=5, sticky="w")
methods = ["GET", "POST", "PUT", "DELETE"]
method_menu = ttk.OptionMenu(
top_frame, self.method_var, methods[0], *methods
)
method_menu.grid(row=0, column=1, padx=5, pady=5)
url_label = ttk.Label(top_frame, text="请求URL:")
url_label.grid(row=0, column=2, padx=5, pady=5, sticky="w")
self.url_entry = ttk.Entry(
top_frame, width=100
)
self.url_entry.grid(row=0, column=3, padx=5, pady=5, sticky="ew")
top_frame.columnconfigure(3, weight=2)
send_btn = ttk.Button(
top_frame, text="发送请求", command=self.send_request
)
send_btn.grid(row=0, column=4, padx=10, pady=5)
body_frame = ttk.LabelFrame(self, text="请求体(支持JSON格式化)", padding="10")
body_frame.pack(fill=tk.X, padx=10, pady=5)
body_frame.columnconfigure(0, weight=1)
self.body_text = scrolledtext.ScrolledText(
body_frame, wrap=tk.WORD, height=10
)
self.body_text.grid(row=0, column=0, sticky="nsew")
format_btn = ttk.Button(
body_frame, text="格式化JSON", command=self.format_json
)
format_btn.grid(row=1, column=0, padx=5, pady=5, sticky="w")
response_frame = ttk.LabelFrame(self, text="响应结果", padding="10")
response_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
response_frame.rowconfigure(0, weight=1)
response_frame.columnconfigure(0, weight=1)
self.status_var = tk.StringVar(value="状态: 等待请求")
status_label = ttk.Label(
response_frame, textvariable=self.status_var, foreground="#333"
)
status_label.grid(row=1, column=0, padx=5, pady=2, sticky="w")
self.response_text = scrolledtext.ScrolledText(
response_frame, wrap=tk.WORD, state=tk.DISABLED, font=("Consolas", 10)
)
self.response_text.grid(row=0, column=0, sticky="nsew")
def init_json_highlight_tags(self):
"""初始化高亮标签(仅给响应体文本框添加)"""
for tag_name, color in self.json_tags.items():
self.response_text.tag_configure(tag_name, foreground=color)
def highlight_json(self, text_widget, json_content):
"""通用JSON高亮方法(仅用于响应体)"""
text_widget.tag_remove("all", "1.0", tk.END)
text_widget.delete("1.0", tk.END)
text_widget.insert("1.0", json_content)
try:
parsed = json.loads(json_content)
formatted = json.dumps(parsed, indent=2, ensure_ascii=False)
text_widget.delete("1.0", tk.END)
text_widget.insert("1.0", formatted)
self._tag_json_elements(text_widget, formatted)
except json.JSONDecodeError:
pass
def _tag_json_elements(self, text_widget, json_str):
"""递归标记JSON元素(内部辅助方法)"""
patterns = [
("key", r'"([^"]+)"(?=\s*:)'),
("string", r'"([^"]*)"'),
("number", r'-?\d+(\.\d+)?([eE][+-]?\d+)?'),
("boolean", r'\b(true|false)\b'),
("null", r'\b(null)\b'),
("brace", r'[{}\[\]]'),
("colon", r':'),
("comma", r',')
]
for tag_name, pattern in patterns:
for match in re.finditer(pattern, json_str):
start = "1.0 + {} chars".format(match.start())
end = "1.0 + {} chars".format(match.end())
text_widget.tag_add(tag_name, start, end)
def format_json(self):
"""格式化JSON"""
content = self.body_text.get("1.0", tk.END).strip()
if not content:
messagebox.showwarning("提示", "请求体为空,无需格式化")
return
try:
json_data = json.loads(content)
formatted_json = json.dumps(
json_data, indent=2, ensure_ascii=False, sort_keys=True
)
self.body_text.delete("1.0", tk.END)
self.body_text.insert("1.0", formatted_json)
except json.JSONDecodeError as e:
messagebox.showerror("JSON格式错误", f"无法解析JSON:\n{str(e)}")
def send_request(self):
"""发送HTTP请求"""
url = self.url_entry.get().strip()
method = self.method_var.get()
body = None
if method in ["POST", "PUT"]:
content = self.body_text.get("1.0", tk.END).strip()
if content:
try:
body = json.loads(content)
except json.JSONDecodeError:
body = content
try:
self.status_var.set("状态: 正在请求...")
self.update_idletasks()
start_time = datetime.now()
if method == "GET":
response = requests.get(url, timeout=30)
elif method == "POST":
response = requests.post(
url, json=body if isinstance(body, dict) else None,
data=body if not isinstance(body, dict) else None,
timeout=30
)
elif method == "PUT":
response = requests.put(
url, json=body if isinstance(body, dict) else None,
data=body if not isinstance(body, dict) else None,
timeout=30
)
else:
response = requests.delete(url, timeout=30)
duration = (datetime.now() - start_time).total_seconds()
self.status_var.set(
f"状态: 成功 | 状态码: {response.status_code} | 耗时: {duration:.2f}s"
)
self.show_response(response)
except RequestException as e:
self.status_var.set("状态: 请求失败")
messagebox.showerror("请求错误", f"无法连接到URL:\n{str(e)}")
except Exception as e:
self.status_var.set("状态: 未知错误")
messagebox.showerror("错误", f"发生异常:\n{str(e)}")
def show_response(self, response):
"""显示响应结果(保留JSON高亮)"""
self.response_text.config(state=tk.NORMAL)
self.response_text.delete("1.0", tk.END)
try:
json_data = response.json()
formatted_json = json.dumps(
json_data, indent=2, ensure_ascii=False, sort_keys=True
)
self.highlight_json(self.response_text, formatted_json)
except json.JSONDecodeError:
self.response_text.insert(tk.END, response.text)
self.response_text.config(state=tk.DISABLED)
def destroy(self):
super().destroy()
if __name__ == "__main__":
app = RequestSimulator()
app.mainloop()
