mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-06-14 06:29:25 -03:00
fix(api): return JSON error responses for all /api/* routes — prevent JSON.parse crashes on 404/500
This commit is contained in:
@@ -33,6 +33,7 @@ from .utils.example_images_migration import ExampleImagesMigration
|
|||||||
from .services.websocket_manager import ws_manager
|
from .services.websocket_manager import ws_manager
|
||||||
from .services.example_images_cleanup_service import ExampleImagesCleanupService
|
from .services.example_images_cleanup_service import ExampleImagesCleanupService
|
||||||
from .middleware.csp_middleware import relax_csp_for_remote_media
|
from .middleware.csp_middleware import relax_csp_for_remote_media
|
||||||
|
from .middleware.error_middleware import api_json_error
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -76,6 +77,11 @@ class LoraManager:
|
|||||||
"""Initialize and register all routes using the new refactored architecture"""
|
"""Initialize and register all routes using the new refactored architecture"""
|
||||||
app = PromptServer.instance.app
|
app = PromptServer.instance.app
|
||||||
|
|
||||||
|
# Register JSON error middleware for /api/* routes as the outermost
|
||||||
|
# middleware so it catches errors from all other middlewares.
|
||||||
|
if api_json_error not in app.middlewares:
|
||||||
|
app.middlewares.insert(0, api_json_error)
|
||||||
|
|
||||||
if relax_csp_for_remote_media not in app.middlewares:
|
if relax_csp_for_remote_media not in app.middlewares:
|
||||||
# Ensure CSP relaxer executes after ComfyUI's block_external_middleware so it can
|
# Ensure CSP relaxer executes after ComfyUI's block_external_middleware so it can
|
||||||
# see and extend the restrictive header instead of being overwritten by it.
|
# see and extend the restrictive header instead of being overwritten by it.
|
||||||
|
|||||||
71
py/middleware/error_middleware.py
Normal file
71
py/middleware/error_middleware.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
"""JSON error middleware for API routes.
|
||||||
|
|
||||||
|
Ensures all responses to /api/* requests return valid JSON that the
|
||||||
|
browser-extension frontend can JSON.parse() without crashing, even when
|
||||||
|
the route does not exist (404) or the handler raises an exception (500).
|
||||||
|
|
||||||
|
Extension consumers call response.json() unconditionally — an HTML error
|
||||||
|
page causes ``SyntaxError: unexpected end of data`` that leaks into the
|
||||||
|
popup UI as a toast notification.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from typing import Awaitable, Callable
|
||||||
|
|
||||||
|
from aiohttp import web
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@web.middleware
|
||||||
|
async def api_json_error(
|
||||||
|
request: web.Request,
|
||||||
|
handler: Callable[[web.Request], Awaitable[web.Response]],
|
||||||
|
) -> web.Response:
|
||||||
|
"""Return JSON ``{"success": false, "error": "..."}`` for API errors.
|
||||||
|
|
||||||
|
Only intercepts paths starting with ``/api/`` — all other routes
|
||||||
|
(frontend pages, static files, WebSocket upgrades) pass through
|
||||||
|
unchanged.
|
||||||
|
"""
|
||||||
|
if not request.path.startswith("/api/"):
|
||||||
|
return await handler(request)
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = await handler(request)
|
||||||
|
return response
|
||||||
|
except web.HTTPException as exc:
|
||||||
|
# Let redirects (301, 302, 307, 308) propagate — they are not errors.
|
||||||
|
if exc.status < 400:
|
||||||
|
raise
|
||||||
|
|
||||||
|
logger.warning(
|
||||||
|
"API %s %s returned HTTP %d: %s",
|
||||||
|
request.method,
|
||||||
|
request.path,
|
||||||
|
exc.status,
|
||||||
|
exc.reason,
|
||||||
|
)
|
||||||
|
|
||||||
|
return web.json_response(
|
||||||
|
{"success": False, "error": f"{exc.status}: {exc.reason}"},
|
||||||
|
status=exc.status,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error(
|
||||||
|
"API %s %s raised unhandled exception: %s",
|
||||||
|
request.method,
|
||||||
|
request.path,
|
||||||
|
exc,
|
||||||
|
exc_info=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
return web.json_response(
|
||||||
|
{
|
||||||
|
"success": False,
|
||||||
|
"error": f"500: Internal Server Error ({type(exc).__name__})",
|
||||||
|
},
|
||||||
|
status=500,
|
||||||
|
)
|
||||||
@@ -2,6 +2,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
from py.middleware.cache_middleware import cache_control
|
from py.middleware.cache_middleware import cache_control
|
||||||
|
from py.middleware.error_middleware import api_json_error
|
||||||
from py.utils.settings_paths import ensure_settings_file
|
from py.utils.settings_paths import ensure_settings_file
|
||||||
|
|
||||||
# Set environment variable to indicate standalone mode
|
# Set environment variable to indicate standalone mode
|
||||||
@@ -157,7 +158,7 @@ class StandaloneServer:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app = web.Application(
|
self.app = web.Application(
|
||||||
logger=logger,
|
logger=logger,
|
||||||
middlewares=[cache_control],
|
middlewares=[api_json_error, cache_control],
|
||||||
client_max_size=256 * 1024 * 1024,
|
client_max_size=256 * 1024 * 1024,
|
||||||
handler_args={
|
handler_args={
|
||||||
"max_field_size": HEADER_SIZE_LIMIT,
|
"max_field_size": HEADER_SIZE_LIMIT,
|
||||||
|
|||||||
Reference in New Issue
Block a user