Pode
Pode copied to clipboard
Static Route doesn't resolve correctly link that start with ./
Describe the Bug
I have this webpage
<!DOCTYPE html>
<!-- HTML for static distribution bundle build -->
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger Editor</title>
<style>
* {
box-sizing: border-box;
}
body {
font-family: Roboto,sans-serif;
font-size: 9px;
line-height: 1.42857143;
color: #444;
margin: 0px;
}
#swagger-editor {
font-size: 1.3em;
}
.container {
height: 100%;
max-width: 880px;
margin-left: auto;
margin-right: auto;
}
#editor-wrapper {
height: 100%;
border:1em solid #000;
border:none;
}
.Pane2 {
overflow-y: scroll;
}
</style>
<link href="./swagger-editor-dist/swagger-editor.css" rel="stylesheet">
<link rel="icon" type="image/png" href="./swagger-editor-dist/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./swagger-editor-dist/favicon-16x16.png" sizes="16x16" />
</head>
<body>
<div id="swagger-editor"></div>
<script src="./swagger-editor-dist/swagger-editor-bundle.js"> </script>
<script src="./swagger-editor-dist/swagger-editor-standalone-preset.js"> </script>
<script>
window.onload = function() {
// Build a system
// info here https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md#core
const editor = SwaggerEditorBundle({
dom_id: '#swagger-editor',
layout: 'StandaloneLayout',
presets: [
SwaggerEditorStandalonePreset
],
queryConfigEnabled: false,
url: "$($data.OpenApi)",
requestSnippetsEnabled: true
})
window.editor = editor
window.editor = editor
}
</script>
</body>
</html>
this page is published using
# set a default path
$Path = Protect-PodeValue -Value $Path -Default "/docs/swagger-editor"
if ([string]::IsNullOrWhiteSpace($Title)) {
throw "No route path supplied for $($Type) page"
}
if (Test-OpenAPIVersion -Version 3.1 -DefinitionTag $DefinitionTag) {
throw "This version on Swagger-Editor doesn't support OpenAPI 3.1"
}
# setup meta info
$meta = @{
Title = $Title
OpenApi = $OpenApiUrl
DarkMode = $DarkMode
DefinitionTag = $DefinitionTag
SwaggerEditorDist = "$Path/swagger-editor-dist"
}
$route = Add-PodeRoute -Method Get -Path $Path `
-Middleware $Middleware -ArgumentList $meta -EndpointName $EndpointName -PassThru -ScriptBlock {
param($meta)
$Data = @{
Title = $meta.Title
OpenApi = $meta.OpenApi
SwaggerEditorDist = $meta.SwaggerEditorDist
}
$podeRoot = Get-PodeModuleMiscPath
Write-PodeFileResponse -Path ([System.IO.Path]::Combine($podeRoot, 'default-swagger-editor.html.pode')) -Data $Data
}
$swaggerEditorPath = Join-Path -Path $(Get-PodeModuleMiscPath) -ChildPath 'swagger-editor-dist'
Add-PodeStaticRoute -Path $meta.SwaggerEditorDist -Source $swaggerEditorPath -EndpointName $EndpointName
$PodeContext.Server.OpenAPI[$DefinitionTag].hiddenComponents.viewer['editor'] = $Path
all static files are under the swagger-editor-dist folder
Problem
when I open the page I get errors that all links are not valid I checked the content of $WebEvent and this is the result
{
"AcceptEncoding": "",
"Path": "/docs/swagger-editor-dist/favicon-16x16.png",
"Response": {
"StatusCode": 200,
"SendChunked": false,
"Headers": {
"Count": 5,
"Keys": "Access-Control-Allow-Origin Access-Control-Allow-Credentials Access-Control-Max-Age Access-Control-Allow-Headers Access-Control-Allow-Methods"
},
"OutputStream": {
"CanRead": true,
"CanSeek": true,
"CanWrite": true,
"Capacity": 0,
"Length": 0,
"Position": 0,
"CanTimeout": false,
"ReadTimeout": null,
"WriteTimeout": null
},
"Sent": false,
"IsDisposed": false,
"StatusDescription": "OK",
"ContentLength64": 0,
"ContentType": "",
"HttpResponseLine": "HTTP/1.1 200 OK\r\n"
},
"Streamed": true,
"Data": null,
"Cookies": {},
"Request": {
"HttpMethod": "GET",
"QueryString": null,
"Protocol": "HTTP/1.1",
"ProtocolVersion": "1.1",
"ContentType": "",
"ContentLength": 0,
"ContentEncoding": {
"Preamble": null,
"BodyName": "utf-8",
"EncodingName": "Unicode (UTF-8)",
"HeaderName": "utf-8",
"WebName": "utf-8",
"WindowsCodePage": 1200,
"IsBrowserDisplay": true,
"IsBrowserSave": true,
"IsMailNewsDisplay": true,
"IsMailNewsSave": true,
"IsSingleByte": false,
"EncoderFallback": "System.Text.EncoderReplacementFallback",
"DecoderFallback": "System.Text.DecoderReplacementFallback",
"IsReadOnly": true,
"CodePage": 65001
},
"TransferEncoding": "",
"UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"UrlReferrer": "http://localhost:8081/docs/swagger-editor",
"Url": "http://localhost:8081/docs/swagger-editor-dist/favicon-16x16.png",
"Headers": {
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Dest": "image",
"Referer": "http://localhost:8081/docs/swagger-editor",
"Sec-GPC": "1",
"sec-ch-ua-platform": "\"Windows\"",
"Cookie": "pode.sid=s:ef35f34c-673a-4b49-9ca5-0c8912c566d0.LnTLYqmm6jNkNDs2bi5OYUAjyMCgZdyBPMU72Rmafhg=",
"Accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
"Connection": "keep-alive",
"sec-ch-ua-mobile": "?0",
"Host": "localhost:8081",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en",
"Accept-Encoding": "gzip, deflate, br",
"sec-ch-ua": "\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Brave\";v=\"120\"",
"Sec-Fetch-Site": "same-origin"
},
"RawBody": [],
"Host": "localhost:8081",
"AwaitingBody": false,
"Form": null,
"Body": "",
"CloseImmediately": false,
"IsProcessable": true,
"RemoteEndPoint": {
"AddressFamily": 2,
"Address": "127.0.0.1",
"Port": 63863
},
"LocalEndPoint": {
"AddressFamily": 2,
"Address": "127.0.0.1",
"Port": 8081
},
"IsSsl": false,
"SslUpgraded": false,
"IsKeepAlive": true,
"InputStream": {
"Socket": "System.Net.Sockets.Socket",
"CanRead": true,
"CanSeek": false,
"CanWrite": true,
"CanTimeout": true,
"ReadTimeout": 100,
"WriteTimeout": -1,
"DataAvailable": false,
"Length": null,
"Position": null
},
"Certificate": null,
"AllowClientCertificate": false,
"TlsMode": 0,
"ClientCertificate": null,
"ClientCertificateErrors": 0,
"Protocols": 3120,
"Error": null,
"IsAborted": false,
"IsDisposed": false,
"Address": "127.0.0.1:8081",
"Scheme": "Http",
"Type": 1,
"IsHttp": true,
"IsWebSocket": false,
"IsSmtp": false,
"IsTcp": false,
"IsUnknown": false
},
"Timestamp": "2024-01-05T03:07:52.1728449Z",
"ContentType": "",
"Files": null,
"Parameters": null,
"Auth": {},
"StaticContent": null,
"Lockable": {},
"PendingCookies": {},
"TransferEncoding": "",
"Ranges": null,
"OnEnd": [],
"Endpoint": {
"Address": "localhost:8081",
"Protocol": "http",
"Name": null
},
"Method": "get",
"Route": null,
"Query": null,
"ErrorType": null
}
"Path" is /docs/swagger-editor-dist/favicon-16x16.png instead of /docs/swagger-editor/swagger-editor-dist/favicon-16x16.png
Hi @mdaneri,
The translation is done at the HTML level client side. If I remember rightly relative paths in HTML are based on the current "folder" which in this case would be /docs as /swagger-editor is the document you're viewing rather than a folder (as far as the client side is concerned). So you'd need to add ./swagger-editor to all the URLs - which which work if someone was using a custom URL for the editor.
You do have the static content path available in $data.SwaggerEditorDist, so dynamic generating the href URLs could work:
<link rel="icon" type="image/png" href="$($data.SwaggerEditorDist)/favicon-32x32.png" sizes="32x32" />
Yes but this is a workaround I have to use to make it works.
If I use any other webserver (IIS, Apache or anything else) and my page is http://localhost/rootpage/test/index.html ./swagger-editor-dist/swagger-editor.cssinsideindex.htmlredirects tohttp://localhost/rootpage/test/swagger-editor/swagger-editor-dist/swagger-editor.cssnot tohttp://localhost/rootpage/swagger-editor/swagger-editor-dist/swagger-editor.css`
But the correct option is what's occurring no?
Your page is at http://localhost/docs/swagger-editor and your href is ./swagger-editor-dist/swagger-editor.css, therefore it would load the content from http://localhost/docs/swagger-editor-dist/swagger-editor.css
If the page was at http://localhost/docs/swagger-editor/index.html as it was loading the css from http://localhost/docs/swagger-editor-dist/swagger-editor.css I'd agree, but in the former example /swagger-editor is the document rather than a folder 🤔
I created a sample to replicate the issue. https://github.com/mdaneri/Pode/tree/develop/examples/SwaggerEditor. this is the way I'm publishing the editor
# STATIC asset folder route
Add-PodeStaticRoute -Path '/editor' -Source './www' -Defaults @('index.html')
Add-PodeStaticRoute -Path '/editor/swagger-editor-dist' -Source './swagger-editor-dist'
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
Move-PodeResponseUrl -Url '/editor'
}
It should work, but it's not.
Looks like the issue is not related to the translation but the static routes order.
I changed the code and now more or less is working
# STATIC asset folder route
Add-PodeStaticRoute -Path '/editor/swagger-editor-dist' -Source './swagger-editor-dist'
Add-PodeStaticRoute -Path '/editor' -Source './www' -Defaults @('index.html')
Add-PodeRoute -Method Get -Path '/' -ScriptBlock {
Move-PodeResponseUrl -Url '/editor'
}
What is not working is the -Defaults @('index.html')
I have to specify http://localhost:8085/editor/index.html to access the page
This looks like a bug. Can you please confirm it?
Ah yes, the ordering that hierarchical routes like that are created in does matter.
It's been reported before that the -Defaults doesn't work, but I could never reproduce it; I'll try again just in case.
Hi @mdaneri,
I just tested myself and http://localhost:8085/editor does load the index.html page, however it is just a white screen - which makes sense as the ./swagger-editor-dist/swagger-editor.css references would call http://localhost:8085/swagger-editor-dist/swagger-editor.css which doesn't exist.
If I do call http://localhost:8085/editor/index.html directly the page loads normally, because the relative ./swagger-editor-dist/swagger-editor.css path would map from the /editor folder in the URL.
Other than that, the -Defaults is working and using index.html for me; do you get something different like a 404 page?
I created 2 samples and removed the swagger-editor-dist folder.
Now is using the /src/Misc/swagger-editor-dist folder as source.
swagger-editor.ps1- the editor works but not-Defaults @('index.html')swagger-editor-broken.ps1- the editor doesn't work but-Defaults @('index.html')works as expected
Hi @mdaneri,
I'm still seeing the same as my previous response. For the swagger-editor-broken.ps1 I would expect this to not work, as the order the static routes are created in does matter, and the longer /editor/swagger-editor-dist route does need to be created before the /editor route.
For swagger-editor.ps1 though, this does load the index.html content when navigating to http://localhost:8085/editor for me; though I do get a white screen because the CSS/JS it's trying to load can't be found:
However if I update the index.html CSS/JS to include /editor as well:
<link href="./editor/swagger-editor-dist/swagger-editor.css" rel="stylesheet">
Then loading the page works:
This is expected to me, because the CSS/JS relative paths will be looking for http://localhost:8085/swagger-editor-dist/swagger-editor.css when loaded from http://localhost:8085/editor, because the /editor part is a page not a folder like it would be with http://localhost:8085/editor/index.html.
Is this the same as what you're seeing, or are you seeing something different - like a 404 page instead of a white page?
Sorry, I misread your previous comment when you said that "the ordering that hierarchical routes like that are created does matter." I understood the opposite 😀
Do we have this limitation in the documentation?
Do you think is worth spending time to fix it?
It'd be a difficult one to fix without breaking people's integrations who are already aware that the order matters. Best to fix it in 3.0.0 😉
For now, adding a section into the /Tutorials/Routes/Utilities/StaticContent page in the docs it good enough.
Maybe a check with a warning can be good enough
I had a thought about this last night; it should be fine to fix for just static content. I've an idea on how to quickly fix it, which I'll commit in a moment.
Please can you fix the default page issue?
What is the problem you're actually seeing with -Default, have you any screenshots, etc.? Because for me it's working as expected.
That commit, for static content only, will sort the paths it's searching on into the correct order. I've tested it using your editor examples, and flipping the two static route definitions around, and all works now for me regardless of order the routes are created in.
Yes, it is working, and the route order is not relevant now. But still, the page doesn't work unless I use http://localhost:8085/editor/index.html
The page should work without the need to specify index.html in the URL. if you try http://localhost:8085 automatically, you are redirected to http://localhost:8085/editor, and the index.html is loaded, but the links on the page are broken.
This is the issue with -Default
The way -Default is working is as expected, the issue is caused by the way relative paths work in HTML.
The only way I can fix this is to add a switch parameter which tells Pode to redirect to /editor/index.html instead of rendering the index.html file as /editor.
I've added a -RedirectToDefault switch to Add-PodeStaticRoute, which will instead reirect to /editor/index.html instead of loading the index.html file in place.