Flutter Web的CORS问题

5
我正在使用flutter-web和.net webapi。为了提交我的请求,我尝试了Dio和Dart HTTP包。由于CORS问题,两者都没有起作用。请告诉我我做错了什么?有没有解决此问题的方法?
当使用postman进行测试时,API没有任何问题。
示例代码:
我在webapi中添加了var cors = new EnableCorsAttribute("", "", "*"); config.EnableCors();。Flutter HTTP请求是基于dio构建的。
Dio dio= new Dio();
  Future postData2(user) async{
    debugPrint(user.toString());
    dynamic data = {
    'phone_number': user['country_code'] + user['phone_number'],
    'password':user['password']
    };
    final String pathUrl = "http://localhost:62435/api/Token/GetToken";
    var response = await dio.post(pathUrl, data:data, options: Options(
        headers: {
          'content-type': 'application/json',
          'Access-Control-Allow-Origin':'true'
        },
    ));
    return response.data;
  }

//Http : dart

  Future postData(user) async{
    dynamic data = {
      'phone_number': user['country_code'] + user['phone_number'],
      'password':user['password']
    };
    final String pathUrl = "http://localhost:62435/api/Token/GetToken";
    dynamic response = _http.post(
      pathUrl,
      body : data,
      headers : {
        HttpHeaders.contentTypeHeader : 'application/json',
        //'Access-Control-Allow-Origin':'true'
      }
    );
    debugPrint( //response.statusCode +
        " " + response.data.toString());
  }

对于 Dio 来说,至少检查请求已经发送。

输入图片描述

输入图片描述

输入图片描述

使用 Dio 我遇到了以下错误:

Dio 请求标头在网络选项卡中。请求仍处于挂起状态,无法完成。

Request URL: http://localhost:62435/api/Token/GetToken
Referrer Policy: no-referrer-when-downgrade
Provisional headers are shown
Access-Control-Allow-Origin: true
content-type: application/json; charset=utf-8
Referer: http://localhost:63440/
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36
{phone_number: "123124", password: "safaSF"}

该错误日志中的这行代码告诉您正在进行跨域请求。因此,它无法通过,您应该在服务器端将 http://localhost:59789 添加为允许的来源之一。但通常情况下,在 Postman 中进行测试时,这将起作用,因为它不是来自另一个 web 域。为了测试目的,在服务器端可以使用 * 允许所有域。如果您提供所使用的服务器,我们可以找到如何为该服务器进行配置的方法。 - Abhilash Chandran
嗨,Abhilash,我已经尝试在WebAPI中使用以下代码: var cors = new EnableCorsAttribute("", "", "*"); config.EnableCors(); - S P
未来的postData(user) async{ dynamic data = {'phone_number': user['country_code'] + user['phone_number'],'password':user['password']}; final String pathUrl = "http://localhost:62435/api/Token/GetToken"; print(jsonEncode(data)); dynamic response = _http.post(pathUrl,body : jsonEncode(data),headers : {HttpHeaders.contentTypeHeader : 'application/json','Access-Control-Allow-Origin':'true'}); if (response.statusCode == 200) { return response; } else { throw Exception('加载失败'); } } - S P
现在你已经解决了旧的错误。这是否意味着出现了新的错误?你尝试过我的建议答案吗? - Abhilash Chandran
这是当时出现的一个新错误。你的建议是对的。过了一会儿,这个问题得到了解决。我很快就会发布解决方案。 - S P
3个回答

2
当您使用Flutter Web并调用API时,Flutter会使用:
预检请求(在调用API之前)

这是一个请求,用于检查是否理解CORS协议,并且服务器是否使用特定的方法和标头进行了确认。
它是一个OPTIONS请求,使用三个HTTP请求标头:
1.Access-Control-Request-Method,
2.Access-Control-Request-Headers,
3.and the Origin header.

在这种情况下,您需要检查您的API是否允许使用OPTIONS方法。

Access-Control-Allow-Origin: https://foo.bar.org
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE

如果您的API不接受,您需要配置响应以发送OPTIONS到Access-Control-Allow-Methods,例如:

Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Request-Headers: origin, x-requested-with
Origin: https://foo.bar.org

有关更多详细信息,请参阅Mozilla词汇表

0

浏览器调试工具(F12)以及在Flutter客户端和服务器之间的Wireshark是解决这个问题非常有价值的。

浏览器调试工具快速显示出在预检查时出错。在Flutter Web端这里没有什么可以做的。支持需要由服务器提供。

在我的情况下,我的服务器是用Go语言编写的,使用了Gorilla-Mux框架。我按照这篇不错的文章操作,但仍然遇到了问题。在Wireshark中,我仍然可以看到服务器拒绝了OPTIONS请求,并且服务器日志中没有任何解释。

这个问题帮助我找到了解决方案。一路上有很多错误答案,最终我学到了这些:

  1. 服务器必须对OPTION请求做出反应,客户端通常会发送Access-Control-Request-MethodAccess-Control-Request-Headers(取决于具体的请求)。
  2. 服务器需要返回访问控制响应头。在我的情况下,Access-Control-Allow-HeadersAccess-Control-Allow-Origin是必需的。
  3. 有些答案说Access-Control-Allow-Origin: *是不允许的,但根据RFC的解释,是允许的。
  4. 如果响应中发送了Access-Control-Allowed-Methods,则不需要将OPTIONS列入其中。

由于我使用Gorilla mux,我还发现OPTIONS路由不需要显式注册为处理程序;中间件会处理它。

最低限度地,对于我的 api,Gorilla mux 中只需要配置一个响应头:Allowed Headers。中间件将根据指定的内容响应 Access-Control-Allow-HeadersAccess-Control-Allow-Origin: *。根据 cors.go 中的注释:

// Content-Type must be explicitly declared if accepting Content-Types other than
// application/x-www-form-urlencoded, multipart/form-data, or text/plain.

响应Access-Control-Allow-Headers会自动过滤掉客户端没有请求的头部键。
因此,我的服务器代码使用的是
import "github.com/gorilla/handlers"

并且有
    cors := []handlers.CORSOption{}
    cors = append(cors, handlers.AllowedMethods([]string{"GET", "POST"}))
    cors = append(cors, handlers.MaxAge(3600))
    cors = append(cors, handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}))

    olist := []string{}
    whitelist := os.Getenv("CORS_ALLOWED_ORIGINS")
    if whitelist != "" {
        parts := strings.Split(whitelist, ",")
        olist = append(olist, parts...)
    }
    if len(olist) == 0 {
        olist = append(olist, "*")
    }
    cors = append(cors, handlers.AllowedOrigins(olist))

使用

http.ListenAndServe(iface, handlers.CORS(cors...)(myRouter))

-1
从上面的评论中我假设你正在使用ASP .net Web API。如果是这种情况,那么你应该正确地使用EnableCorsAttribute的构造函数。根据文档here,你必须在构造函数中使用第一个参数来传递允许的来源列表。在你的情况下,你可以采用以下任一方式。

允许所有来源

public EnableCorsAttribute(
    "*",
    "*",
    ""
)

允许特定来源

public EnableCorsAttribute(
    "http://localhost:59789,http//localhost:another_port,http://example_domain.com",
    "*",
    ""
)

请注意,Access-Control-Allow-Origin响应头。您不应在请求标头中设置它。它告诉浏览器或客户端允许从哪些来源访问服务器。在这里阅读有关CORS的更多信息。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接