preface
I believe that if you are doing H5 partners should be familiar with CORS, we will talk about how to achieve CORS through two simple methods in this article today.
Without further ado, share today’s Repo: gitlab.com/yafeya/reac…
A few notes to the Repo:
- Technology stack
Asp.Net Core
As aREST API Server
React
As aClient
nginx
Serve as the reverse proxy serverDocker
andDocker-Compose
As operating environment
- Software that needs to be installed in advance
git
dotnet core sdk
node
&npm
create-react-app
docker
&docker-compose
1. What isCORS
CORS = Cross Origin Resource Sharing, called cross-domain resource sharing. Although the literal translation of the original text should be called cross-source, it is generally called cross-domain according to popular terminology.
1.1 Cross-domain & co-domain
To understand what CORS is, we can first understand what homology or homology is.
In fact, co-domain is a standard for AJAX, which is a guarantee of browser security. The following is an excerpt from teacher Ruan Yifeng’s article.
Originally, it meant that the Cookie set by page A could not be opened on page B unless the two pages were “cognate”. “Homology” means “three of the same”.
- The agreement is the same
- Domain name is the same
- The same port
With the development of the Internet, the same origin policy has become stricter and stricter. Currently, there are three behaviors that are restricted if they are not homologous.
- Cookie, LocalStorage, and IndexDB cannot be read.
- DOM is not available.
- AJAX requests cannot be sent.
1.2 Cross-domain Scenarios
We can give an example of a cross-domain scenario: if our website is published at http://www.yafeya.com, then we can get the following information about our website
project | value |
---|---|
agreement | http |
The domain name | www.yafeya.com |
port | 80 |
So the cross-domain case for the following scenario
http://www.yafeya.com/api/weatherforecast
The same domainhttps://www.yafeya.com/api/wetherforecast
Cross-domain, different protocols & ports (HTTPS defaults to port 443)http://yafeya.com/api/weatherforecast
Cross-domain, different domain nameshttp://www.yafeya.com:9981/api/weatherforecast
The cross-domain port is different
Therefore, only the web page under the first url can be shared with the web page under http://www.yafeya.com.
1.3 AJAX
The same origin policy of
This same origin policy is an AJAX policy. In other words, it only exists in javascript calls. There are no restrictions on back-end technologies.
In fact, the backend restriction is achieved through other technologies, such as Authentication and JWT. Simply put, the credentials are stored in a JSON token that needs to be presented when accessing the API to obtain resources. We’ve mentioned this model briefly in the Strapi series, so if you’re interested you can take an archaeology test.
Article address: juejin.cn/post/696567…
2. Two simple methods to Enable CORS
There are basically two efficient methods to deal with CORS:
- On the server
Enable All Origin
Or addwhitelist
- will
H5
andREST Api Server
Through the reverse proxy proxy to the sameDomain
Under the
Let’s take a look at each of these methods in our Demo.
3. Server
endAllow Origins
3.1 methods
In our Demo, we can use the following code to run the Demo in a Development environment. In the Development environment, CORS is implemented through Allow All Orgins.
# in WebApiServer folder
dotnet run --launch-profile WebApiServer
Copy the code
# in web-api-client folder
yarn start
Copy the code
Server side, in the Development environment, allows all Origin, Header, and Method. Set a Whitelist Policy for Cors and apply it. We’re not going to show you the code here.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
// Allow all origin, header & method in Development Env.
app.UseCors(builder =>
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
}
// ...
}
Copy the code
On the Client side, you set up the Server Api address of the development environment to link to in the.env.development file.
# .env.development
REACT_APP_API_URL=http://localhost:5000/WeatherForecast
Copy the code
With AllowAnyOrigin set on the Server side, we can bypass the same-origin Policy when calling the API on the Client side.
3.2 the problem
- Allow Any Origin
Using this approach, we make our resources accessible to other Unexpect domains. This is not what we want, and it creates some security issues.
- Set up the
whitelist
There are also some problems with this approach, because if we change the deployment URL, our code will change a lot, even if we write in configuration, it will be a headache to change it all the time.
Personally, I believe that code mainly solves business logic, but deployment is Devops’ job, and code should focus on solving business logic, not Devops.
3.3 Solutions
So, based on the above two points, we introduced the following solution, if we deploy the Api Server and Client under the same domain, there is no problem with CORS, so we introduced Nginx to solve this problem.
4. Use a reverse proxy to deploy the same-domain website
4.1 Network Structure
The following figure shows the network structure of Demo:
Part of the REST API Client follows the deployment pattern in the previous article. There is another way to deploy a react-Demo, which is to put the react-Demo compilation through the dist directory in the root directory of the Nginx Gateway above, but the focus of this article is on the CORS workaround, not the Nginx deployment. In addition, the current deployment mode is more isolated and each Docker Container is more independent, which is convenient to illustrate the problem.
4.2 Request Process
In the network diagram above, I have numerically indicated the process of Request and Response when a user requests a web page from the browser. Suppose our Nginx Gateway is deployed on the domain name https://my-domain.
-
- The user accesses this in the browser
URL
.https://my-domain
- The user accesses this in the browser
-
Nginx Gateway
Will thisRequst
Forwarded toReact-Demo
thisdocker-container
-
React-Demo
In the web, yeahRest-Api-Server
thisdocker-container
forREST-API
request#. Env file configuration API address below REACT_APP_API_URL = https://some-domain/api/WeatherForecastCopy the code
As shown in the above code, in the React – Demo website of https://some-domain/api/WeatherForecast’s request, will be Nginx – reverse proxy Gateway to http://web-api-server, However, from the perspective of the observer, the react-demo is actually accessing resources from https://some-domain, so the browser will assume that we are accessing resources in the same domain, thus circumventing the same-origin policy check.
-
http://web-api-server
willAPI
theresponse
Back to theReact-Demo
-
React-Demo
The webDom
As aResponse
Back to theNginx-Gateway
-
Nginx-Gateway
The webDom
theResponse
Returns to the browser based onDom Response
Render the web page to the user
4.3 docker-compose.yaml
Let’s look at the implementation of docker-comemage. yaml. Docker-compose contains three containers or services. Docker-compose combines these three services into a LOCAL area network (LAN). Each service can access each other by service-name.
Web-api-server and web-api-client are two services that can be accessed by nginx-gateway. Reverse proxy browser access to https://some-domain and https://some-domain/api into these services. Let’s look at the nginx-gateway setup.
version: '3'
services:
web-api-server:
image: yafeya/web-api-server
environment:
ASPNETCORE_ENVIRONMENT: Production
restart: always
ports:
- '5000:80'
web-api-client:
image: yafeya/react-demo
restart: always
ports:
- '9981:80'
depends_on:
- web-api-server
nginx-gateway:
image: nginx:stable-alpine
volumes:
- ./nginx:/etc/nginx/conf.d
- /etc/ssl:/etc/ssl
ports:
- 80: 80
- 443: 443
restart: always
Copy the code
4.4 Nginx.conf
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name _;
ssl_certificate /etc/ssl/fullchain.pem;
ssl_certificate_key /etc/ssl/private.pem;
ssl on;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:! aNULL:! eNULL:! EXPORT:! CAMELLIA:! DES:! MD5:! PSK:! RC4;
ssl_prefer_server_ciphers on;
# Requests to https://some-domain will be reverse-proxy to http://web-api-client
location / {
proxy_pass http://web-api-client;
}
# Requests to https://some-domain/api will be reverse-proxy to http://web-api-server
location /api {
proxy_pass http://web-api-server;}}Copy the code
For the https://some-domain/api request, nginx-gateway reverse proxies to http://web-api-server. Note that the nginx-gateway reverse proxy does not end with /, which is http://web-api-server/, which means that, If we use a relative path, that is, our Api must be accessible via http://web-api-server/api/*.
For asp.net core, we add the following code to achieve this. In this way, the subdirectory API is added to the route of the entire site.
app.UsePathBase($"/api"); Copy the code
4.5 Starting the Production Environment
Executing the following code will start docker-compose and get all the Containers running.
# repo folder
./build
docker-compose up -d
Copy the code