Los protocolos internos
Cómo llega tu frontend a la webview sin servidor, qué esquemas registra el framework, y por qué el que sirve ficheros del disco es una decisión de seguridad y no una comodidad.
Los esquemas
Y una nota de plataforma: la forma exacta de cada esquema difiere entre sistemas —en Windows toman forma de dominio local por compatibilidad con su motor— pero el comportamiento es el mismo. Lo que cambia y sí importa es el origen resultante, que es lo del nivel 10.1.
El de recursos, y por qué tiene ámbito
Es el interesante. Sin él, mostrar una imagen que está en el disco del usuario exige leerla en Rust y mandarla por el puente —con el coste del nivel 15.3—. Con él, se pone la URL en la etiqueta y el motor la carga directamente.
{ "app": { "security": { "assetProtocol": { "enable": true, "scope": ["$APPDATA/imagenes/**", "$APPDATA/portadas/**"] } } }}Y tiene ámbito porque es una vía directa del disco a la webview que no pasa por ningún comando. Sin ámbito, cualquier código en tu frontend podría poner cualquier ruta en una etiqueta de imagen y leer el fichero. Con ámbito, solo lo declarado.
Dos detalles operativos:
- El ámbito vive en la configuración, no en una capability. Es la excepción del nivel 27.3, y la razón es que no acota un comando sino el motor.
- Y la política de contenido tiene que permitirlo. Habilitar el protocolo no basta: si tu
img-srcno incluye el esquema de recursos, el motor bloquea la carga igual. Es el fallo más común al activarlo.
Protocolo o puente
La decisión, que el nivel 15.3 anticipó:
La regla: si es un recurso que se muestra, el protocolo; si es un dato que se procesa o la decisión de servirlo tiene lógica, el puente.
Un esquema propio
La tercera opción, y resuelve el caso que las dos anteriores dejan fuera: servir contenido que generas, con lógica, sin pasarlo por el puente.
tauri::Builder::default() .register_uri_scheme_protocol("miniatura", |ctx, request| { // aquí hay lógica: comprobar, generar, cachear let id = extraer_id(request.uri()); let bytes = generar_miniatura(id); // o servirla de la caché tauri::http::Response::builder() .header("Content-Type", "image/webp") .body(bytes) .unwrap() })<img src="miniatura://proyecto-42" />Con eso, la webview pide una miniatura por su identificador, el core decide qué sirve y comprueba lo que quiera, y los bytes no cruzan el puente. Es lo mejor de las dos columnas, y es la respuesta correcta para galerías, vistas previas y cualquier cosa que se muestre mucho.
Tres cosas que hay que tener en cuenta:
- La política de contenido tiene que permitir tu esquema.
- El manejador corre por cada petición, así que conviene que sea rápido o que cachee.
- Y es una superficie: lo que ese manejador acepte en la URL es lo que la webview puede pedir. Aplica el criterio del nivel 14.4: un identificador de dominio, no una ruta.