![](/img/trans.png)
[英]How to send multiple images from FastAPI backend to JavaScript frontend using StreamingResponse?
[英]How to replace hyperlinks in StreamingResponse?
是否可以替换StreamingResponse中的超链接?
我正在使用下面的代码来 stream HTML 内容。
from starlette.requests import Request
from starlette.responses import StreamingResponse
from starlette.background import BackgroundTask
import httpx
client = httpx.AsyncClient(base_url="http://containername:7800/")
async def _reverse_proxy(request: Request):
url = httpx.URL(path=request.url.path, query=request.url.query.encode("utf-8"))
rp_req = client.build_request(
request.method, url, headers=request.headers.raw, content=await request.body()
)
rp_resp = await client.send(rp_req, stream=True)
return StreamingResponse(
rp_resp.aiter_raw(),
status_code=rp_resp.status_code,
headers=rp_resp.headers,
background=BackgroundTask(rp_resp.aclose),
)
app.add_route("/titles/{path:path}", _reverse_proxy, ["GET", "POST"])
它工作正常,但我想替换a href
链接。
那可能吗?
我已经厌倦了像下面这样包装生成器:
async def adjust_response(iterable):
# Adjust hyperlinks in response.
async for element in iterable.aiter_raw():
yield element.decode("utf-8").replace("/admin", "/gateway/admins/SERVICE_A").encode("utf-8")
但这导致了该错误:
h11._util.LocalProtocolError: Too much data for declared Content-Length
一种解决方案显然是从原始响应生成器中读取(如上面评论部分所述),修改每个href
链接,然后生成修改后的内容。
另一种解决方案是使用 JavaScript 查找 HTML 文档中的所有链接并相应地修改它们。 如果您有权访问外部服务的 HTML 文件,则只需添加一个脚本来修改所有href
链接,前提是Window.location
未指向服务的主机(例如, if (window.location.host:= "containername.7800" ) {...}
)。 即使您无权访问外部 HTML 文件,您仍然可以在服务器端执行此操作。 您可以创建一个StaticFiles
实例来提供replace.js
脚本文件,然后只需在 HTML 页面的<head>
部分中使用<script>
标记注入该脚本(注意:如果未提供<head>
标记,则查找<html>
标签并在其中创建<head></head>
和<script>
)。 您可以使用window.onload
事件在整个页面加载后运行脚本,或者最好在初始 HTML 文档已完全加载和解析(无需等待样式表、图像等完成加载)时运行DOMContentLoaded
事件。 使用这种方法,您不必通过每个块 go 来修改服务器端的每个href
链接,而是注入脚本然后在客户端进行替换。 例子:
# ...
from fastapi.staticfiles import StaticFiles
app = FastAPI()
app.mount("/static-js", StaticFiles(directory="static-js"), name="static-js")
client = httpx.AsyncClient(base_url="http://containername:7800/")
async def iter_content(r):
found = False
async for chunk in r.aiter_raw():
if not found:
idx = chunk.find(bytes('<head>', 'utf-8'))
if idx != -1:
found = True
b_arr = bytearray(chunk)
b_arr[idx+6:] = bytes('<script src="/static-js/replace.js"></script>', 'utf-8') + b_arr[idx+6:]
chunk = bytes(b_arr)
yield chunk
async def _reverse_proxy(request: Request):
url = httpx.URL(path=request.url.path, query=request.url.query.encode("utf-8"))
rp_req = client.build_request(
request.method, url, headers=request.headers.raw, content=await request.body()
)
rp_resp = await client.send(rp_req, stream=True)
return StreamingResponse(
iter_content(rp_resp),
status_code=rp_resp.status_code,
headers=rp_resp.headers,
background=BackgroundTask(rp_resp.aclose),
)
app.add_route("/titles/{path:path}", _reverse_proxy, ["GET", "POST"])
JS 脚本( replace.js
):
document.addEventListener('DOMContentLoaded', (event) => {
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++) {
// ...
let path = anchors[i].pathname.replace('/admin', '/admins/SERVICE_A')
anchors[i].href = path + anchors[i].search
}
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.