-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Uri.toString() for "mailto" does not encode spaces properly (+
instead of %20
)
#43838
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
Comments
The Dart |
Same here. How to fix it? |
The lazy approach would be: print(uri.toString().replaceAll("+", "%20")); A more intricate approach could be: final uri = Uri.parse('mailto:[email protected]?subject=' +
'Example Subject & Symbols are allowed!'.replaceAll(" ", "%20"));
print(uri.toString()); or final uri = Uri(
scheme: 'mailto',
path: '[email protected]',
query:
'subject=Example Subject & Symbols are allowed!'.replaceAll(" ", "%20")
);
print(uri.toString()); There currently is no way to use the (If we do a complete rewrite of the |
I'm personally constructing the final subject = Uri.encodeComponent("Example Subject & Symbols are allowed!")
final uri = "mailto:[email protected]?subject=$subject"; It seems to work fine. |
This is backwards though, as the generic form of URL encoding is percent encoding (click on your link there, then click on "URL encoding" in that text, which leads to https://en.wikipedia.org/wiki/Percent-encoding). The issue isn't that
The correct statement is that there is no way to configure
These suggestions are both incorrect, as they will not encode anything else. Notably in this case, the subject will just be "Example Subject ", because the unencoded
Perhaps an alternate constructor that does the right thing as an interim solution? This issue makes it very hard for people to correctly use the Or, alternately, switch on the scheme and keep the existing behavior for http/https, and do a breaking change for any others? While it is a breaking change, it's hard to imagine many scenarios where anyone would prefer the current behavior for other schemes. |
(Also, perhaps the encodeComponent docs could be updated in the short term; currently they say "For encoding the query part consider using encodeQueryComponent." which is actively misleading advice for any non-http URI. It could say "For encoding the query part of an HTTP or HTTPS URI, consider ...") |
The simplest safe solution I can see at this point to use Uri for anything that has query parameters but isn't an http* URL is to manually re-implement what
For |
https://url.spec.whatwg.org/ is the controlling standard here and it describes our current behaviour as invalid. Since we are not following the standard, we should fix our behaviour accordingly. |
Changing our behavior to match the whatwg spec was one of our plans for Dart 2.0, but it didn't pan out (we significantly over-estimated how much change we could do in the time available). See #29420 It's still a worthy goal, and would make web compilers save a lot of code, but also a highly breaking change to anyone using the existing It's going to be a big undertaking. The (Reducing the complexity was one of the reasons for rewriting |
Can you comment on the possiblity of doing one of the two small changes I suggested above (a new named constructor for non-web Uris, or a breaking change for the constructor just for non-web schemes)? I understand there are other issues that make a major rewrite desirable, but if we could avoid letting the perfect being the enemy of the good in fixing this particular issue in the short term it would make it feasible to use |
I don't see how making our behaviour less broken could be considered a breaking change. Could you elaborate on the flow that you're concerned would break? |
If we don't normalize on creation (and the whatwg spec doesn't), then people relying on creating two |
A new constructor which does only rudimentary normalization (and the /// Creates a URI with the given scheme and content.
///
/// The [scheme] must be a valid, non-empty scheme and
/// the [content] is everything which occurs
/// after the colon following the scheme.
///
/// If [scheme] is a *recognized scheme*,
/// one of `http`, `https`, `ws`, `wss`, `file` or `package`,
/// the URI is normalized as normal for `Uri.parse("$scheme:$content")`.
/// Otherwise the URI is assumed to be non-hierarchical and
/// the only normalization which happens is scheme normalization,
/// percent-escape normalization of existing escapes,
/// and percent-escaping of non-visible-ASCII characters of the content,
/// where non-ASCII characters are UTF-8 encoded first.
///
/// The entirety of the normalized content will be accessible as the [path]
/// of the created URI.
///
/// Example:
/// ```dart
/// var uri = Uri.raw("Unknown", "#!%%0a?/\xa0::\n[]");
/// print(uri.path); // #!%25%0A?/%C2%A0::%0A[]
/// ```
factory Uri.raw(String scheme, String content) => ... That should cover |
I'm confused; I don't think this issue has anything to do with normalization. AFAICT it's purely a question of whether Consider this code: // Case a.
final parameters = <String, String>{
'subject': 'a & b',
'body': 'c & d',
};
var uri = Uri(
scheme: 'mailto',
path: '[email protected]',
queryParameters: parameters,
);
print(uri.toString());
// Case b.
uri = Uri(
scheme: 'mailto',
path: '[email protected]',
query: parameters.entries.map((e) => '${Uri.encodeQueryComponent(e.key)}=${Uri.encodeQueryComponent(e.value)}').join('&'),
);
print(uri.toString());
// Case c.
uri = Uri(
scheme: 'mailto',
path: '[email protected]',
query: parameters.entries.map((e) => '${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}').join('&'),
);
print(uri.toString()); It prints:
The first two (a and b) are identical (presumably the first is actually using I'm simply suggesting a new named constructor that is identical to the
That seems equivalent to |
To elaborate on the background here, as an illustration of the use case I'm trying to solve: the
To help users avoid these pitfalls, we added an example to the plugin README that demonstrates how to correctly (we thought; I didn't realize that My goal here is to have a similarly-high-level example code to replace that incorrect example. Given that the issue here is with Anything that involves the developer already having had to correctly construct a properly encoded |
I don't see why we need a new constructor. The current one is just behaving incorrectly. I could see an argument for accepting an optional named boolean parameter, which is basically what the standard does, that enables the |
That works for me as well. I'm just trying to find an option that doesn't involve waiting an indefinitely long time to rewrite everything, given that it sounds like changing behavior—even incorrect behavior—before that point is a non-starter. (I'm not saying I agree that we shouldn't just change the behavior to be right, just that if that's a non-negotiable, I would like some other option that can be implemented in the short term.) |
Changing broken behaviour should not be a non-starter. What we have today is a bug. We are corrupting user data. |
The The encoding of If that's not what users are expecting, then that is a usability problem, but it is not a bug. We can add a flag to the constructor to make it use percent normal encoding instead of In the longer run, we should do a full breaking change to become WHATWG URL compatible. It would help our JS compilers and probably confuse users less. (Again, I'd have preferred to do that at the Dart 2.0 release, but it didn't make the cut). |
RFC 3986 doesn't specify any sort of
The current behaviour is already broken. This isn't a situation where we are doing something valid but want to change to something else valid and we have to worry about compatibility. We are literally misencoding ASCII space and causing user data corruption as a result. It's just a bug. |
What's the next step here? E.g., is there a formal process for deciding whether to make the breaking change here? |
@lrhn Ping on this question? We're continuing to have users bitten by this issue, and I need to know if this is going to be fixed in Dart, or a workaround added to |
I guess the next step is either:
The breaking change can be either:
A non-breaking change could be:
The easiest to implement is just a clean break switching to new behavior. If you want So the next step is to decide which solution to go for, then we can file a breaking change request for that (if necessary, which is probably likely, the non-breaking approaches are not great). |
Any updates on this? having this problem with the url_launcher plugin |
Either of these options sounds good to me. Who are the other stakeholders who need to weigh in prior to making the breaking change request? |
Just as a side note, I created the |
This comment was marked as off-topic.
This comment was marked as off-topic.
So, when is this going to happen? |
Switching to the WhatWG URL standard? If/when it becomes a high enough priority to someone in power, and some team has the spare resources to implement the WhatWG URL specification in Dart (or a binding to a native implementation, which will be easier on the web), and finds a way to introduce it in a non-breaking way, either as a second class or as a change in behavior for Just introducing a Which means starting out with a design and migration plan, listing the differences in API and behavior and planning how to bridge those gaps. Then implementing the actual WhatWG spec with a Dart API (which may be a big effort, but not necessarily a hard one). Then upgrading all existing libraries that use It's not easy to change the behavior of a core API. It's bloody darn hard, going on impossible in some cases. I'd love to do the implementation. (OK, I'd be favorable towards doing the implementation, a lot of it will probably be tedious.) The migration, integration and long-tail bug-fixing of code that now works slightly differently ... not so much. (The WhatWG URL spec is the controlling standard for browsers, and how they treat URLs. Its goal is to obsolete RFC 3986/3987. It hasn't officially done that yet, according to RFC 3986, probably because the WhatWG URL spec hasn't been released as an RFC, and an RFC can only be officially obsoleted by another RFC. Maybe, I don't know the IETF RFC rules.) It'll happen, if it happens, when the time is ripe and there is a solid plan for making it succeed. An RFC 3986-compliant URI is usually "good enough", it can do most of the same things that the WhatWG URL can do, it just has some different defaults in behavior for some schemes. The problems don't come from something being impossible, just from some operations not behaving like the user expected. You can still reach the same end goal, you just can't port JS code directly. So "there is a workaround" to most actual problems. (The If it's just not using |
There are still proposals for small changes to fix the issue that don't require a complete rewrite of the entire library.
That's irrelevant in the context of this issue though, because as noted above the current behavior is not RFC 3986 compliant. |
Hi.
This ticket was first opened on
flutter
bug tracker: flutter/flutter#68406It seems
Uri.toString()
convert spaces to+
insubject
andbody
formailto
scheme.This seems to be a problem, when the link is opened with
url_launcher
for example then the subject displayed in the mail app is like this:For some reason, the app does not handle
+
symbols in the uri as expected. However, it works fine if%20
is used instead.I tested this with
url_launcher
v5.7.5,flutter
v1.23.0,dart
v2.10.0, Android v8.0.0 and opening the link with Gmail app.Should the spaces be automatically converted to
%20
instead to ensure portability?The text was updated successfully, but these errors were encountered: