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 CoreAs aREST API Server
    • ReactAs aClient
    • nginxServe as the reverse proxy server
    • DockerandDocker-ComposeAs 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/weatherforecastThe same domain
  • https://www.yafeya.com/api/wetherforecastCross-domain, different protocols & ports (HTTPS defaults to port 443)
  • http://yafeya.com/api/weatherforecastCross-domain, different domain names
  • http://www.yafeya.com:9981/api/weatherforecastThe 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 AJAXThe 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 serverEnable All OriginOr addwhitelist
  • willH5andREST Api ServerThrough the reverse proxy proxy to the sameDomainUnder the

Let’s take a look at each of these methods in our Demo.

3. ServerendAllow 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 thewhitelist

    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.

    1. The user accesses this in the browserURL.https://my-domain
    1. Nginx GatewayWill thisRequstForwarded toReact-Demothisdocker-container
    1. React-DemoIn the web, yeahRest-Api-Serverthisdocker-containerforREST-APIrequest
      #. 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.

    1. http://web-api-serverwillAPItheresponseBack to theReact-Demo
    1. React-DemoThe webDomAs aResponseBack to theNginx-Gateway
    1. Nginx-GatewayThe webDomtheResponseReturns to the browser based onDom ResponseRender 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