反向代理与跨域描述
什么是跨域?
跨域(Cross-Origin Resource Sharing, CORS)是指在浏览器中,当一个网页的脚本试图从一个域名(协议、域名、端口)请求另一个域名的资源时,浏览器会阻止这种请求,除非目标服务器明确允许这种跨域请求。这是为了防止恶意网站通过脚本访问其他网站的资源,从而保护用户的安全。
跨域请求的三个条件
- 协议不同:例如,
http
和https
是不同的协议。 - 域名不同:例如,
example.com
和api.example.com
是不同的域名。 - 端口不同:例如,
example.com:80
和example.com:443
是不同的端口。
只要这三个条件中有一个不同,就会触发跨域问题。
为什么要用nginx反向代理解决跨域问题?
nginx是exe,不是前端的网页不会受到同源策略的影响,所以可以使用nginx反向代理解决跨域。
前端程序将请求发送给nginx,nginx获取到请求的URL会根据配置文件将请求的URL进行转发,收到数据后再返回给前端。
效果展示
代码描述
在nginx中添加配置文件
location /api/ {
proxy_pass http://t.weather.itboy.net;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
}
服务配置
server {
listen 58231;
server_name localhost;
nginx会处理对localhost:58231的请求,然后进行匹配,如果是localhost:58231/api/
的请求则会将请求转发给
http://t.weather.itboy.net
,所以在前端发送的请求的地址应该是nginx的地址, const response = await fetch('http://localhost:58231/api/weather/city/101030100');
在unity的代码中是使用配置文件,需要在StreamingAssets
中创建一个txt,名称是request_url
,内容是请求的URL:http://localhost:58231/api/weather/city/101030100
。 然后会发送请求到nginx,nginx根据反向代理配置将请求转发到真正的服务器。
错误处理
已经添加了proxy_pass 配置但是还是提示不能跨域或者404/502
-
proxy_pass
后面没有斜杠:- 如果
proxy_pass
后面没有斜杠,Nginx 会将匹配的location
路径附加到proxy_pass
的 URL 后面。 - 举个例子:
当你访问location /api/ { proxy_pass http://t.weather.itboy.net; }
http://localhost:58231/api/weather/city/101030100
时,Nginx 会将请求转发到http://t.weather.itboy.net/api/weather/city/101030100
。
- 如果
-
proxy_pass
后面有斜杠:- 如果
proxy_pass
后面有斜杠,Nginx 会将匹配的location
路径替换为proxy_pass
的 URL。 - 举个例子:
当你访问location /api/ { proxy_pass http://t.weather.itboy.net/; }
http://localhost:58231/api/weather/city/101030100
时,Nginx 会将请求转发到http://t.weather.itboy.net/weather/city/101030100
。所以就不可访问就404了。
- 如果
测试网站
一个可以使用get请求获取假数据的网站,网站本身是支持跨域的,所以不配置nginx也可以进行通信。
https://jsonplaceholder.typicode.com/
一个天气预报的API,可以进行get请求的测试
http://t.weather.itboy.net/api/weather/city/101030100
相关代码
Nginx配置
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 58231;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location /api/ {
# 将http://t.weather.itboy.net 替换为实际的服务器
proxy_pass http://t.weather.itboy.net;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
}
# On-disk Brotli-precompressed data files should be served with compression enabled:
location ~ .+\.(data|symbols\.json)\.br$ {
gzip off;
add_header Content-Encoding br;
default_type application/octet-stream;
}
# On-disk Brotli-precompressed JavaScript code files:
location ~ .+\.js\.br$ {
gzip off;
add_header Content-Encoding br;
default_type application/javascript;
}
# On-disk Brotli-precompressed WebAssembly files:
location ~ .+\.wasm\.br$ {
gzip off;
add_header Content-Encoding br;
default_type application/wasm;
}
# On-disk gzip-precompressed data files should be served with compression enabled:
location ~ .+\.(data|symbols\.json)\.gz$ {
gzip off;
add_header Content-Encoding gzip;
default_type application/octet-stream;
}
# On-disk gzip-precompressed JavaScript code files:
location ~ .+\.js\.gz$ {
gzip off;
add_header Content-Encoding gzip;
default_type application/javascript;
}
# On-disk gzip-precompressed WebAssembly files:
location ~ .+\.wasm\.gz$ {
gzip off;
add_header Content-Encoding gzip;
default_type application/wasm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
Unity代码
using Best.HTTP;
using Cysharp.Threading.Tasks;
using System;
using UnityEngine;
using UnityEngine.Networking;
public class SendGetR : MonoBehaviour
{
private string requestUrlFilePath = Application.streamingAssetsPath + "/request_url.txt";
async void Start()
{
Debug.Log("开始发送请求啦!");
string fileContent = await ReadFileAsync(requestUrlFilePath);
await GetObtainCodingRulesAsync(fileContent);
}
/// <summary>
/// 异步读取文件内容
/// </summary>
/// <param name="filePath">文件路径</param>
/// <returns>文件内容</returns>
public async UniTask<string> ReadFileAsync(string _filePath)
{
try
{
using (UnityWebRequest uwr = UnityWebRequest.Get(_filePath))
{
await uwr.SendWebRequest();
Debug.Log("请求到的URL是:" + uwr.downloadHandler.text);
return uwr.downloadHandler.text;
}
}
catch (System.Exception e)
{
Debug.LogError("读取文件失败: " + e.Message);
return null;
}
}
/// <summary>
/// 异步获取赋码规则
/// </summary>
/// <param name="url">请求的URL</param>
/// <returns>赋码规则列表</returns>
public async UniTask<string> GetObtainCodingRulesAsync(string _url)
{
try
{
var request = new HTTPRequest(new System.Uri(_url), HTTPMethods.Get, (req, res) =>
{
if (res.IsSuccess)
{
Debug.Log("返回的数据是:" + res.DataAsText);
}
else
{
Debug.Log("发送失败tmp_requestURL: " + _url);
}
});
await request.Send();
await UniTask.WaitUntil(() => request.Response.DataAsText != null);
return request.Response.DataAsText;
}
catch (Exception e)
{
Debug.LogError($"发送失败: {e.Message}");
}
return "没有获取到数据";
}
}
测试使用html代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>发送GET请求</title>
</head>
<body>
<h1>发送GET请求到CSDN</h1>
<p>请点击按钮发送请求,并在浏览器控制台查看结果。</p>
<!-- 添加一个按钮 -->
<button id="sendRequestButton">发送请求</button>
<script>
// 发送GET请求的函数
async function sendGetRequest() {
try {
// 发送GET请求到你的Nginx服务器
const response = await fetch('http://localhost:58231/api/weather/city/101030100');
// 检查响应状态
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 读取响应内容
const data = await response.text();
// 在控制台中显示响应内容
console.log('Response status:', response.status);
console.log('Response data:', data);
} catch (error) {
// 捕获并显示错误
console.error('Error:', error);
}
}
// 获取按钮元素
const button = document.getElementById('sendRequestButton');
// 为按钮添加点击事件监听器
button.addEventListener('click', sendGetRequest);
</script>
</body>
</html>