Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Blazor Hybrid Relative Path Handling #10898

Closed
sbwalker opened this issue Oct 25, 2022 · 12 comments
Closed

Blazor Hybrid Relative Path Handling #10898

sbwalker opened this issue Oct 25, 2022 · 12 comments
Labels
area-blazor Blazor Hybrid / Desktop, BlazorWebView proposal/open s/duplicate 2️⃣ This issue or pull request already exists t/enhancement ☀️ New feature or request

Comments

@sbwalker
Copy link

Description

One of the major benefits of Blazor Hybrid is the ability to re-use a common set of components across web, mobile, and desktop. The razor component model works for the majority of use cases involving code, however there are currently some challenges when it comes to the management of static assets - specifically around relative paths. Note that static assets refer to any file which is delivered in an immutable form ie. CSS, JavaScript, images, icons, etc...The following diagram attempts to visualize the current behavior for static assets in various Blazor hosting models, as well as highlight the limitations in Blazor Hybrid.

image

On the left side you have a razor component which is intended to be used across all hosting models. It contains a reference to a static asset (x.png) using a relative path. The reference to the static asset will be rendered in a consistent manner across each of the hosting models. For Blazor Server and Blazor WebAssembly, the relative path refers to the static asset on the Server. However, for Blazor Hybrid, the relative path refers to the static asset on the client.

  1. The means that there is no longer a single source of truth for your static assets. It means that anytime a new version of a static is created, it must be replicated or synchronized across all of the client installations. This may be acceptable in some environments, however in dynamic applications which allow for extensive run-time configuration this is highly problematic. For a simple example, consider an application which has a rich text editor where users can upload images to the server and then reference those images using standard relative paths. The image will be rendered correctly in Blazor Server and Blazor WebAssembly - however the image will not be rendered in Blazor Hybrid (because the image does not exist on the client). A more advanced example may be a modular system where an administrator can install new functionality at run-time... and this functionality will often have dependencies on static assets (which are expected to reside on the trusted server). Again, the functionality will work correctly in Blazor Server and Blazor WebAssembly - however not in Blazor Hybrid.

  2. As explained above there are many scenarios where a Blazor Hybrid app may want a relative path static asset to refer to the object on a trusted server. Currently the only way to accomplish this is to use absolute paths in your components (ie. instead of src="x.png" the component would need to render src="https://mytrustedserver.com/x.png"). Hardcoding an absolute path is a bad practice for a variety of reasons, so this means that components would need to include some extra logic when creating references to static assets (ie. add the absolute path when running on Blazor Hybrid, else render a relative path). But polluting your components with this type of path logic does not feel like the correct solution. Unfortunately there currently does not seem to be any other solution to solve this problem currently.

I understand that there are some benefits of managing static assets on the client. Specifically, it would provide better performance as there would be less latency. Also it would allow the client to run off-line - ie. without a server connection. However the reality is that very few client applications in the real world need to run off-line - usually they need to interact with public APIs to retrieve data. Which means that off-line support is an edge case - not the primary use case for this technology. And if your app is going to access public APIs then why not allow it to also access public static assets? The counter-argument is that Blazor Hybrid already allows you to access public static assets - you just need to use an absolute Url reference - however this brings us full circle back to the discussion above.

On the topic of public APIs, they obviously need to access resources which do not exist on the client. This can be easily accomplished with HttpClient by configuring it as part of startup by providing a BaseAddress. Then the razor components can inject HttpClient and they will work fine regardless of the Blazor hosting model. Externalizing the behavior of relative paths to static assets would provide similar benefits.

One possible suggestion is to enhance the BlazorWebView with an optional property such as "StaticAssetBaseAddress". If a value was provided then all static assets could use it as a prefix - which would provide the ability to reference resources on a trusted server without resorting to custom logic within the components themselves.

Public API Changes

have not researched API changes needed

Intended Use-Case

explained above

@sbwalker
Copy link
Author

sbwalker commented Oct 25, 2022

Note that Oqtane (https://github.com/oqtane/oqtane.framework) is a modular app framework for Blazor and is already using the approach described above of including logic to render an absolute path when running on Blazor Hybrid, else render a relative path. In most cases it is working fine... however it does not seem to be the best solution... and there are some edge cases which are still not supported.

For example, CSS files sometimes contain a url() function to include other CSS files. And usually the paths used in the url() function are specified in a relative manner. And because a CSS file is static there is no way to modify the behavior to use an absolute Url when running on Blazor Hybrid (without changing the CSS file itself - which is usually not recommended, especially if the CSS file is part of third part distribution such as an Icon library).

@font-face
{
    font-family: 'boxicons';
    font-weight: normal;
    font-style: normal;

    src: url('../fonts/boxicons.eot');
    src: url('../fonts/boxicons.eot') format('embedded-opentype'),
    url('../fonts/boxicons.woff2') format('woff2'),
    url('../fonts/boxicons.woff') format('woff'),
    url('../fonts/boxicons.ttf') format('truetype'),
    url('../fonts/boxicons.svg?#boxicons') format('svg');
}

The use case of adding functionality to an application at run-time which is mentioned in the original post above is better explained in this blog: https://www.oqtane.org/blog/!/51/blazor-client-side-assembly-servicing . The modular architecture in Oqtane supports the dynamic servicing of micro-services and micro-frontends to create composable apps.

@Eilon
Copy link
Member

Eilon commented Oct 27, 2022

Possibly related/similar to dotnet/aspnetcore#42924 ?

@mkArtakMSFT
Copy link
Member

Thanks for contacting us, @sbwalker.
This seem to be a reasonable ask, but I'm not sure how common this scenario would be. I'm going to park this in the backlog for now so that we can accumulate more feedback about this and see if this is something more people need.

@mkArtakMSFT mkArtakMSFT added the t/enhancement ☀️ New feature or request label Nov 8, 2022
@mkArtakMSFT mkArtakMSFT removed their assignment Nov 8, 2022
@mkArtakMSFT mkArtakMSFT added this to the Backlog milestone Nov 8, 2022
@ghost
Copy link

ghost commented Nov 8, 2022

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

@sbwalker
Copy link
Author

I realize that I did not provide a minimal repro - however the scenario is very easy to replicate. Basically create a shared razor component which contains an image tag. For the src attribute of the image tag, use a relative path to identify the resource (ie. image.png ). This component will work fine on Blazor Server and Blazor WebAssembly. Now assume that you do NOT want to package and deploy image.png with your Blazor Hybrid app - you want to reference the image on the server as your single source of truth. Run your Blazor Hybrid app and you will get a broken image (because the resource does not exist locally on the device). So what is the guidance to support this scenario ie. a shared component which works consistently in all Blazor hosting models? I would be very surprised if this scenario was an uncommon requirement.

@Eilon
Copy link
Member

Eilon commented Nov 14, 2022

@sbwalker alright I think I understand now. An issue with something like a BlazorWebView.StaticAssetBaseAddress property is that how would the system know for which relative paths it should be applied? Presumably most relative paths should be handled in the already-working way, and only specific cases should use that prefix? Do you have an idea of what logic could be used to differentiate those cases?

In my mind the only "reliable" way to do this would have to have a helper called public static string GetAssetUrl(string relativePath) that returns the correct path on every platform: on web platforms it returns the string as-is, and on .NET MAUI it inserts the server prefix. Then you call that helper everywhere you need to resolve a path, for example, <img src="@GetAssetUrl("images/x.jpg")" alt="whatever" />.

@sbwalker
Copy link
Author

sbwalker commented Feb 3, 2023

@Eilon the GetAssetUrl() suggestion is exactly what I am doing in Oqtane already... but it feels like the wrong approach to treat this as a development concern (by forcing it to be implemented in the code itself) rather than a hosting model concern. And the suggestion does not handle the other scenario I documented above where static assets may also contain relative paths (ie. CSS url() method).

To answer your question about the BlazorWebView property suggestion I was envisioning that if the new property was set and a reference was made to a relative path then it would add the property value to the path automatically. If the property was not set (the default) then no change in behavior. A developer would not be able to "mix modes" - if they want to bundle static resources locally with their app as part of deployment (the recommended model today) then they should not set the property. If they want to reference remote static resources then they can set the property. They would not be able to have some resources local and some remote as the property would have no way to determine how to handle them (unless this was enhanced to also include an asset manifest - but that would not be a requirement for the initial implementation)

@sbwalker
Copy link
Author

sbwalker commented May 1, 2023

The ability to have razor components work seamlessly across ALL hosting models is definitely something that clients want. In addition, they do not always want to take on the burden of updating all client installations whenever a new version of their application is available. In many cases they would rather centralize the assets so that all client installations can share them. This is where a remote trusted server for assets can be very useful. Allowing components to leverage the same trusted remote server for CSS, JS, images, etc... allows the maximum code reusability across desktop, mobile, and web scenarios.

@sbwalker
Copy link
Author

sbwalker commented Mar 13, 2024

@Eilon is there any update on this item? The shared razor component story would be greatly improved by this remote resource reference capability. The Android Asset Pack/Play Asset Delivery (PAD) with its Dynamic Delivery (ie. FastFollow or OnDemand) options provide a similar capability for native Android apps - referencing external assets so they do not need to be bundled with an app.

@mkArtakMSFT
Copy link
Member

Thanks for your patience, @sbwalker.
We think that we will enable this scenario through #11382. Closing this one as there is no other work we plan to do here.

@mkArtakMSFT mkArtakMSFT closed this as not planned Won't fix, can't repro, duplicate, stale Apr 5, 2024
@mkArtakMSFT mkArtakMSFT added the s/duplicate 2️⃣ This issue or pull request already exists label Apr 5, 2024
@sbwalker
Copy link
Author

sbwalker commented Apr 5, 2024

@mkArtakMSFT I am not sure I understand how #11382 relates to this issue. Basically, all I need is way to reference remote assets in my shared components (without having to use absolute Urls) as this would allow me to create a shared component which works consistently in Blazor Web and Blazor Hybrid deployments. I am talking about remote static assets such as CSS, JS, or image files where there are no security/auth concerns. Do you suggest I add this clarification to #11382 so that it is considered in the final solution?

@Eilon
Copy link
Member

Eilon commented Apr 23, 2024

@mkArtakMSFT I am not sure I understand how #11382 relates to this issue. Basically, all I need is way to reference remote assets in my shared components (without having to use absolute Urls) as this would allow me to create a shared component which works consistently in Blazor Web and Blazor Hybrid deployments. I am talking about remote static assets such as CSS, JS, or image files where there are no security/auth concerns. Do you suggest I add this clarification to #11382 so that it is considered in the final solution?

Hi @sbwalker , this is from what we talked about at MVP Summit. In #11382 we would allow custom handling of requests coming from the WebView. So an app could say "I want to handle requests to /user/profile/** in some custom way - perhaps by rewriting the URL, or by serving custom content (e.g. load from a database, etc.). So while I don't think it directly solves "how to reference remote assets" it would create a hook to enable an app to do so.

@github-actions github-actions bot locked and limited conversation to collaborators May 25, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Blazor Hybrid / Desktop, BlazorWebView proposal/open s/duplicate 2️⃣ This issue or pull request already exists t/enhancement ☀️ New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants