前言

这次项目使用了angular + 第三方c++http服务对接。没有了脱离了熟悉的springboot,刚加接近原生。也遇到了很多问题。

拒绝连接

照着第三方库启动c++服务后,在本机测试可以请求到
image.png

但是使用其他电脑,把localhost改成服务器ip就无法请求到
image.png
通过去看github项目上的issue看到,大家也遇到了如此问题,最后大家的解决办法是以管理员身份运行vs并且将代码从

web::http::experimental::listener::http_listener getStatusListener(L"http://127.0.0.1:8015/getStatus");

改成

web::uri_builder uri;
    uri.set_scheme(U("http"));
    uri.set_host(U("+"));
    uri.set_port(U("8015"));
    uri.set_path(U("/getStatus"));
    web::http::experimental::listener::http_listener getStatusListener(uri.to_uri());

具体原因也没有具体说明(或者我没有看懂)。问了老师后,老师建议配置一下nginx解决。
通过这次配置nginx,理解了nginx本质上还是服务器,原来以为nginx只负责端口转发,springboot起服务作用的是tomcat。就如thinphp的apache一样。

image.png
通过nginx对请求的转发,对于c++服务来说nginx对他的请求就是localhost的地址。从而解决问题。
在后台添加了一个post接口,用于启动捕获数据。使用API Tester插件测试接口。
image.png
但是在启动前台单元测试进行对接的时候,请求同样的地址,发生了请求错误。
image.png
更加奇怪的是,他连续对同一地址请求了两次,而我只操作了一次。第二次返回了200,但是服务器端也没有响应打印信息。第一次请求的CORS error报错在写上一个GET请求接口的时候也遇到过,这是因为前后台不同源,违反了同源策略,解决办法是在相应头上加入Access-Control-Allow-Origin:*字段,允许跨域访问。

// 响应
web::http::http_response response(web::http::status_codes::OK);
response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
response.set_body(jsonResponseFinal);
request.reply(response);

但是再遇到相同问题,这个解决办法没有成功。
现在的问题是
1.为什么同样的错误解决方法只适用于GET请求而不适用与POST请求?
2.并且为什么会请求了两次?
3.为什么API Tester测试没有问题?
带着这些问题问了学长。
学长看了以后发现第二次返回200的请求其实是OPTIONS请求。
image.png
什么是OPTIONS请求呢。我们可以把OPTIONS请求当做先遣队。OPTIONS请求的主要用途有两个
一是获取服务器支持的HTTP请求方法;
二是用来检查服务器的性能。

那些可能会操作数据库的请求(除GET请求外),都会使用 OPTIONS 发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。
Preflighted Requests是CORS中一种透明服务器验证机制。预检请求首先需要向另外一个域名的资源发送一个 HTTP OPTIONS 请求头,其目的就是为了判断实际发送的请求是否是安全的。
下面的情况需要进行预检:
非简单请求,比如使用Content-Type 为 application/xml 或 text/xml 的 POST 请求;(什么是简单请求,什么是非简单请求,请移步阮一峰的跨域资源共享 CORS 详解

下面借用MDN的图来更直观的看一下

image.png
所以解决也就是在options相应头加入允许请求。

web::http::experimental::listener::http_listener startCaptureListener(L"http://127.0.0.1:8015/startCapture");
startCaptureListener.support(web::http::methods::POST, [&](web::http::http_request request) {
        this->startCapture(request);
});
startCaptureListener.support(web::http::methods::OPTIONS, [&](web::http::http_request request) {
        this->allow(request);
});

void YzHttp::HttpService::allow(web::http::http_request request) {
    web::http::http_response response(web::http::status_codes::OK);
    response.headers().add(U("Access-Control-Allow-Origin"), U("*"));
    response.headers().add(U("Access-Control-Allow-Methods"), U("POST"));
    response.headers().add(U("Access-Control-Allow-Headers"), U("Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With"));
    request.reply(response);
}

问题解决。
最后一个问题,为什么用API Tester测试能通过呢。可能无法模拟真正的浏览器访问吧。

总结

通过构建c++http服务,使用更加原生的代码。更能知道自己有哪些http知识不清楚。而springboot框架为我们做好了一切,也让我们没机会接触这些知识。
感谢黄庭祥学长在我解决问题过程中给予的帮助。

参考连接

Server does not work from public IP, but localhost or other SDKs are fine
为什么会有OPTIONS请求
跨源资源共享(CORS)


小强Zzz
1.2k 声望32 粉丝