dio icon indicating copy to clipboard operation
dio copied to clipboard

Use modern Fetch API with Dio on Web

Open mikuhl-dev opened this issue 1 year ago • 17 comments

Request Statement

I am trying to figure out Dio and CORS on web. Google is used here as an example, but it could be anything.

Currently if you were to make a request with Dio to "https://www.google.com/" on web. Your request would fail with a transport error, as the browser blocks the request because Google does not reply with the header that allows the browser to do this.

This is done to prevent websites from sending requests to Google with your current cookies, which could be dangerous.

But what happens if you just want to request the page without cookies? As if you were never logged in at all. It seems that XMLHttpRequest only wants to send cookies, or not send anything at all.

Is this doable with the modern Fetch API? https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API It looks like there are different modes for cors. Possibly one with this effect?

If so than should there be an option to use this modern Fetch API with Dio? Especially when modern Fetch API allows for streaming, which is not possible with XMLHttpRequest.

Solution Brainstorm

No response

mikuhl-dev avatar Mar 15 '23 04:03 mikuhl-dev

In the meantime, you're able to write your own adapter, which makes use of fetch instead of xhr. If you happen to do so, we would appreciate a PR.

If we work on this, we should check for browser compatibility before starting.

ueman avatar Mar 15 '23 06:03 ueman

Regarding the Fetch API, I just found a new plugin that integrates the Fetch API for the http package. https://pub.dev/packages/fetch_client

AlexV525 avatar Mar 15 '23 06:03 AlexV525

We recently received many requests related to obtaining streams directly on the Web platform. This might have a higher priority to implement as a new Dio instance or adapter.

AlexV525 avatar Apr 21 '23 06:04 AlexV525

Related:

  • #1795
  • #1621
  • #1279

AlexV525 avatar Apr 21 '23 06:04 AlexV525

Theoretically, you could wrap the http fetch_client in the ConversionLayerAdapter to enable it easily.

The ConversionLayerAdapter could maybe its own package to allow an easy interopt between http and dio

ueman avatar Apr 21 '23 06:04 ueman

You can directly use fetch_client's underlying fetch_api to get strong typed bindings.

Zekfad avatar May 19 '23 10:05 Zekfad

it is necessary to add in ConversionLayerAdapter or make another layer like that with another name or should we directly add to browser_adapter

meetdhanani17 avatar Sep 19 '23 12:09 meetdhanani17

@meetdhanani17 I'm not sure, I understand what you're trying to say.

Right now, you can't use the ConversionLayerAdapter outside of the native_dio_adapter, but it could be moved into its own package, to allow an easier interopt between dio and http packages. It should not be moved into dio, since it would add a dependency on http.

ueman avatar Sep 19 '23 13:09 ueman

hi, i have used ConversionLayerAdapter to solve “Web Environment cannot obtain real-time stream”,but it doesn't work, it still return the result by one time, here is my example, please help me figure out what i am wrong:

Map<String, dynamic> bodyWrap = {'params': body};
  RequestOptions options =
      RequestOptions(method: "post",contentType: "application/json",responseType: ResponseType.stream);
  options.headers = ChatHttpUtil.getInstance().getDefaultHead();
  options.data = json.encode(bodyWrap);
  options.baseUrl = "http://test-inside-city-ai-assistant.songguo7.com/";
  options.path = "city-ai-assistant/backend/chat/sse/send/v2";
  final cla = ConversionLayerAdapter(BrowserClient());
  final response = await cla.fetch(
    options,
    null,
    null,
  );

  if (response.statusCode == 200 || response.statusCode == 201) {
    ChatData chatData = ChatData(false, [], "", 0);
    ctx.state.chatDataList.add(chatData);
    Stream<Uint8List> stream = response.stream;
    stream.listen((event) {
      print("receiver event");
      Uint8List uint8list = event;
      String string = utf8.decode(uint8list);
      List<String> items = string.split("data:");
      print("List<String> items=$items");
      items.forEach((element) {
        element = element.trim();
        if (element.startsWith("{") && element.endsWith("}")) {
          Map<String, dynamic> map = jsonDecode(element);
          element = element.replaceAll("\n", '\\n');
          ChatBaseInfo chatBaseInfo = ChatBaseInfo.fromJson(map);
          if (chatBaseInfo.code == 200) {
            ctx.state.chatDataList.last.messageId =
                chatBaseInfo.data!.messageId;
            ctx.state.sessionId = chatBaseInfo.data!.sessionId!;
            handleData(
                chatBaseInfo.data!.content!, ctx.state.chatDataList.last);
            if ("stop" == chatBaseInfo.data!.finishReason) {
              ctx.state.isSendingMessage = false;
            }
          } else {
            ctx.state.chatDataList.last.infos
                .add(ChatInfo(1, text: chatBaseInfo.message));
            ctx.state.isSendingMessage = false;
          }
          ctx.dispatch(ChatActionCreator.onUpdate());
        }
      });
    });

zhangyaobin avatar Nov 07 '23 08:11 zhangyaobin

Can you please properly format your example? It's hard to understand without formatting. Typically, you need to do something like this (pseudo code):

final dio = Dio();
dio.adapter = ConversionLayerAdapter(FetchClient()); // from https://pub.dev/packages/fetch_client
final response = await dio.get(...);

Please note that I haven't tested the code and it's from memory so names may be slightly off

ueman avatar Nov 07 '23 09:11 ueman

Can you please properly format your example? It's hard to understand without formatting. Typically, you need to do something like this (pseudo code):

final dio = Dio();
dio.adapter = ConversionLayerAdapter(FetchClient()); // from https://pub.dev/packages/fetch_client
final response = await dio.get(...);

Please note that I haven't tested the code and it's from memory so names may be slightly off

i am sorry ,i'm not familiar with the format, now i use "BrowserClient" instead of "FetchClient",am i wrong?

zhangyaobin avatar Nov 07 '23 09:11 zhangyaobin

The BrowserClient doesn't use the fetch API as far as I know.

ueman avatar Nov 07 '23 09:11 ueman

thanks for your reply, Could you provide me a complete use case?

zhangyaobin avatar Nov 07 '23 09:11 zhangyaobin

must i use FetchClient ?

zhangyaobin avatar Nov 07 '23 09:11 zhangyaobin

Unfortunately, you have to figure that out yourself since I haven't tested or tried anything myself, but the example above should get you there.

ueman avatar Nov 07 '23 09:11 ueman

thank you, now i try with FetchClient to solve the issue.

zhangyaobin avatar Nov 07 '23 09:11 zhangyaobin

Can you please properly format your example? It's hard to understand without formatting. Typically, you need to do something like this (pseudo code):

final dio = Dio();
dio.adapter = ConversionLayerAdapter(FetchClient()); // from https://pub.dev/packages/fetch_client
final response = await dio.get(...);

Please note that I haven't tested the code and it's from memory so names may be slightly off

at last, i resolve the issue with ConversionLayerAdapter(FetchClient(mode: RequestMode.cors)) , thanks again.

zhangyaobin avatar Nov 07 '23 13:11 zhangyaobin