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

Make the recommendation to not use dart:io/dart:html for HTTP requests stronger #5391

Merged
merged 20 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions examples/misc/lib/library_tour/async/basic.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import 'dart:html';
import 'package:http/http.dart';

void miscDeclAnalyzedButNotTested() {
const url = 'humans.txt';
final url = Uri.parse('humans.txt');
final httpClient = Client();

{
// #docregion then
HttpRequest.getString(url).then((String result) {
httpClient.read(url).then((String result) {
print(result);
});
// #enddocregion then
}

{
// #docregion catchError
HttpRequest.getString(url).then((String result) {
httpClient.read(url).then((String result) {
print(result);
}).catchError((e) {
// Handle or ignore the error.
Expand Down
1 change: 1 addition & 0 deletions examples/misc/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ dependencies:
args: ^2.4.2
characters: ^1.3.0
examples_util: { path: ../util }
http: ^1.1.2

dev_dependencies:
lints: ^3.0.0
Expand Down
123 changes: 10 additions & 113 deletions src/guides/libraries/_dart-html-tour.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,119 +322,15 @@ subclasses. Some common events include:

### Using HTTP resources with HttpRequest

Formerly known as XMLHttpRequest, the [HttpRequest][] class
gives you access to HTTP resources from within your browser-based app.
Traditionally, AJAX-style apps make heavy use of HttpRequest. Use
HttpRequest to dynamically load JSON data or any other resource from a
web server. You can also dynamically send data to a web server.


#### Getting data from the server

The HttpRequest static method `getString()` is an easy way to get data
from a web server. Use `await` with the `getString()` call
to ensure that you have the data before continuing execution.

<?code-excerpt "html/test/html_test.dart (getString)" plaster="none" replace="/await.*;/[!$&!]/g; /Future<\w+\W/void/g"?>
{% prettify dart tag=pre+code %}
void main() async {
String pageHtml = [!await HttpRequest.getString(url);!]
// Do something with pageHtml...
}
{% endprettify %}

Use try-catch to specify an error handler:

<?code-excerpt "html/lib/html.dart (try-getString)"?>
```dart
try {
var data = await HttpRequest.getString(jsonUri);
// Process data...
} catch (e) {
// Handle exception...
}
```

If you need access to the HttpRequest, not just the text data it
retrieves, you can use the `request()` static method instead of
`getString()`. Here's an example of reading XML data:

<?code-excerpt "html/test/html_test.dart (request)" replace="/Future<\w+\W/void/g; /await.*;/[!$&!]/g"?>
```dart
void main() async {
HttpRequest req = await HttpRequest.request(
url,
method: 'HEAD',
);
if (req.status == 200) {
// Successful URL access...
}
// ···
}
```

You can also use the full API to handle more interesting cases. For
example, you can set arbitrary headers.

The general flow for using the full API of HttpRequest is as follows:

1. Create the HttpRequest object.
2. Open the URL with either `GET` or `POST`.
3. Attach event handlers.
4. Send the request.

For example:

<?code-excerpt "html/lib/html.dart (new-HttpRequest)"?>
```dart
var request = HttpRequest();
request
..open('POST', url)
..onLoadEnd.listen((e) => requestComplete(request))
..send(encodedData);
```

#### Sending data to the server

HttpRequest can send data to the server using the HTTP method POST. For
example, you might want to dynamically submit data to a form handler.
Sending JSON data to a RESTful web service is another common example.

Submitting data to a form handler requires you to provide name-value
pairs as URI-encoded strings. (Information about the URI class is in
the [URIs section][URIs] of the [Dart Library Tour.][Dart Library Tour])
You must also set the `Content-type` header to
`application/x-www-form-urlencoded` if you wish to send data to a form
handler.

<?code-excerpt "html/test/html_test.dart (POST)"?>
```dart
String encodeMap(Map<String, String> data) => data.entries
.map((e) =>
'${Uri.encodeComponent(e.key)}=${Uri.encodeComponent(e.value)}')
.join('&');
void main() async {
const data = {'dart': 'fun', 'angular': 'productive'};
var request = HttpRequest();
request
..open('POST', url)
..setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded',
)
..send(encodeMap(data));
await request.onLoadEnd.first;
if (request.status == 200) {
// Successful URL access...
}
// ···
}
```
You should avoid directly using `dart:html` to make HTTP requests.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if you think that I should just remove this section completely.

The [`HttpRequest`][] class in `dart:html` is platform-dependent
and tied to a single implementation.
Instead, use a higher-level library like
[`package:http`]({{site.pub-pkg}}/http).

The [Fetch data from the internet][] tutorial
explains how to make HTTP requests
using `package:http`.

### Sending and receiving real-time data with WebSockets

Expand Down Expand Up @@ -542,9 +438,10 @@ For more information about Dart web libraries, see the
[AnchorElement]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-html/AnchorElement-class.html
[dart:html]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-html/dart-html-library.html
[Dart Library Tour]: /guides/libraries/library-tour
[Fetch data from the internet]: /tutorials/server/fetch-data
[Document]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-html/Document-class.html
[Element]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-html/Element-class.html
[HttpRequest]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-html/HttpRequest-class.html
[`HttpRequest`]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-html/HttpRequest-class.html
[IndexedDB]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-indexed_db/dart-indexed_db-library.html
[MessageEvent]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-html/MessageEvent-class.html
[Nodes]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-html/Node-class.html
Expand Down
29 changes: 9 additions & 20 deletions src/guides/libraries/_dart-io-tour.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,27 +234,15 @@ void processRequest(HttpRequest request) {

#### HTTP client

The [HttpClient][] class
helps you connect to HTTP resources from your Dart command-line or
server-side application. You can set headers, use HTTP methods, and read
and write data. The HttpClient class does not work in browser-based
apps. When programming in the browser, use the
[dart:html HttpRequest class.][HttpRequest]
Here's an example of using HttpClient:

<?code-excerpt "misc/test/library_tour/io_test.dart (client)" replace="/Future<\w+\W/void/g"?>
```dart
void main() async {
var url = Uri.parse('http://localhost:8888/dart');
var httpClient = HttpClient();
var request = await httpClient.getUrl(url);
var response = await request.close();
var data = await utf8.decoder.bind(response).toList();
print('Response ${response.statusCode}: $data');
httpClient.close();
}
```
You should avoid directly using `dart:io` to make HTTP requests.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me know if you think that I should just remove this section completely.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good to keep these sections for two releases or so to point users in the right direction. Curious what Marya thinks though.

In the sections you remove, do consider linking to the /tutorials/server/fetch-data page as an alternative to learn more though since these changes remove learning material.

I do think making the corresponding note/warning on the API's dartdocs more prominent will have a larger effect than any changes here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great suggestion! I've added a link to the tutorial.

There is a code review out for making the corresponding change to the API docs:
https://dart-review.googlesource.com/c/sdk/+/340283

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's good to keep these sections for two releases or so to point users in the right direction. Curious what Marya thinks though.

I'm wondering about the severity of this discouragement. Like, if it's a huge problem that people are using HttpRequest / HttpClient, then I'd say completely remove the sections. If it's not so severe, then a better middle ground (for a release or two, like Parker said), would be to keep the old content, but put your new disclaimer at the top of the section before the old content starts. And also pare down all the recommendation-like parts of the old stuff.

I'd say do this for both pages. So here for example, it'd be:

#### HTTP client

You should avoid directly using `dart:io` to make HTTP requests....
(all your new content up to recommending the fetch-data tutorial)

If you still need to use `HttpClient`, here's an example:

(code block)

If you want to go this route, let me know and I can leave another comment on the other section (dart:html) with a similar mini-rough draft of: starting with your discouragement text + how I'd pare it down severely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want to be pretty forceful about our recommendation - we are using the same initial wording in the SDK documentation. But we are preserving the example so users can find it there.

Wouldn't it be better to avoid having examples in our library tour that should not be used except in rare circumstances?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for explaining, definitely trust your judgement here!

You can make the final call here, but just to be extra explicit for readers, you could explicitly state that there's still an example in the sdk docs (that's the API docs right?) if you really need it (like Parker said, having an example in one place then making it disappear too well all of a sudden isn't ideal). So, after lines 245 in this file, and 333 at the end of the html-tour file, something like:

Though no longer recommended / not recommended for the majority of use cases, 
you can find more information on / an example of `HttpClient`/`HttpRequest` 
in the [API documentation](link-to-api-docs).

Approving in the mean time so you can make the final call, but I'll come back and check again if you do change something else and want another look. Thanks for doing this!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the purpose the library tour to indoctrinate developers new to Dart? Those developers should start with package:http (or some equivalent package).

I preserved the link to the class and it still has an example there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and don't trust my judgement; if often sucks ;-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the purpose the library tour to indoctrinate developers new to Dart?

Definitely so. What Parker and I are referring to is the kind of nebulous, hard to measure, off-chance that "what if someone, somewhere has a link to this specific section that they hold onto for reference that says "example here for whatever", and the off-chance that they'll be greatly perturbed if all of a sudden that "whatever"s not there any more and it's not immediately obvious where to find it.

This definitely isn't a big deal so again, your call :) And your judgement about how important it is that no one remotely even goes poking around the old stuff unless they're absolutely digging for it is based on a lot more context than I have, I'm just citing a general best practice for tech writing. And yes, you do have the link on the API names which I'm ok with!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed answer. I think that I'm OK with it as-is.

The [HttpClient][] class in `dart:io` is platform-dependent
and tied to a single implementation.
Instead, use a higher-level library like
[`package:http`]({{site.pub-pkg}}/http).

The [Fetch data from the internet][] tutorial
explains how to make HTTP requests
using `package:http`.

### More information

Expand All @@ -269,6 +257,7 @@ For more information about server-side and command-line app development, see the
[library tour]: /guides/libraries/library-tour
[dart:io]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-io/dart-io-library.html
[Directory]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-io/Directory-class.html
[Fetch data from the internet]: /tutorials/server/fetch-data
[File]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-io/File-class.html
[HttpClient]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-io/HttpClient-class.html
[HttpRequest]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-html/HttpRequest-class.html
Expand Down
7 changes: 4 additions & 3 deletions src/guides/libraries/library-tour.md
Original file line number Diff line number Diff line change
Expand Up @@ -1196,13 +1196,13 @@ see the [asynchronous programming codelab](/codelabs/async-await).
{% endcomment %}

You can use `then()` to schedule code that runs when the future completes. For
example, `HttpRequest.getString()` returns a Future, since HTTP requests
example, [`Client.read()`][] returns a Future, since HTTP requests
can take a while. Using `then()` lets you run some code when that Future
has completed and the promised string value is available:

<?code-excerpt "misc/lib/library_tour/async/basic.dart (then)"?>
```dart
HttpRequest.getString(url).then((String result) {
httpClient.read(url).then((String result) {
print(result);
});
```
Expand All @@ -1212,7 +1212,7 @@ object might throw.

<?code-excerpt "misc/lib/library_tour/async/basic.dart (catchError)"?>
```dart
HttpRequest.getString(url).then((String result) {
httpClient.read(url).then((String result) {
print(result);
}).catchError((e) {
// Handle or ignore the error.
Expand Down Expand Up @@ -1806,6 +1806,7 @@ To learn more about the Dart language, see the

[ArgumentError]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-core/ArgumentError-class.html
[Assert]: /language/error-handling#assert
[`Client.read()`]: {{site.pub-api}}/http/latest/http/Client/read.html
[Comparable]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-core/Comparable-class.html
[Dart API]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}
[DateTime]: {{site.dart-api}}/{{site.data.pkg-vers.SDK.channel}}/dart-core/DateTime-class.html
Expand Down
13 changes: 9 additions & 4 deletions src/tutorials/server/fetch-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,17 @@ see [What is a URL?][] on the mdn web docs.

## Retrieve the necessary dependencies

You can directly use `dart:io` or `dart:html` to make HTTP requests,
however those libraries are platform dependent.
`package:http` provides a cross-platform library
The `package:http` library provides a cross-platform solution
for making composable HTTP requests,
with optional fine-grained control.

{{site.alert.note}}
You should avoid directly using `dart:io` or `dart:html`
to make HTTP requests.
Those libraries are platform-dependent
and tied to a single implementation.
{{site.alert.end}}

To add a dependency on `package:http`,
run the following [`dart pub add`][] command
from the top of your repo:
Expand Down Expand Up @@ -230,7 +235,7 @@ which can also be seen in your browser at
```json
{
"name": "http",
"latestVersion": "0.13.5",
"latestVersion": "1.1.2",
"description": "A composable, multi-platform, Future-based API for HTTP requests.",
"publisher": "dart.dev",
"repository": "https://github.com/dart-lang/http"
Expand Down