Solving cross source resource sharing (CORS) and automatic startup uwsgi - Day 09 on Amazon Linux 2

Solving cross source resource sharing (CORS) and automatic startup uwsgi - Day 09 on Amazon Linux 2

On the back end of the application, we have solved the following problems:

  • Database access: MariaDB.
  • RESTful API implementation: upload pictures using post.
  • Back end server integration: Nginx and merge Django.

Finally, there are two problems to be solved: cross source resource sharing and automatic startup. Cross origin resource sharing (CORS) means that when a user agent requests a resource that is not the current file source, such as a resource from a different domain, protocol or port, a cross origin HTTP request will be established. This situation most often occurs in the development of mobile application WebView. Usually, many frameworks known as cross mobile platform development use WebView for development. Developers can easily deploy to different devices, such as iPhone and Android, as long as they write HTML + JavaScript + CSS web page syntax and package it in WebView components. However, such web design will involve cross source resource sharing (CORS), because mobile web pages are usually http://127.0.0.1/index.html or file:///index.html The RESTful API we designed is http://[EC2_IPv4]/imgUpload /. Obviously, the domain is quite different.

Below, we simply write an example to test, and put the file on EC2 and our own computer to observe its running results. The following figure is part of our code. It can be seen that the first part is to set the field name of upload as fileUpload; The second part is to specify the call specific RESTful API through POST; The third part analyzes and displays the results of the response.


Figure 1. Some JavaScript code for uploading pictures

Then connect to EC2 through the terminal or putty, create the test page fishrecog.html for uploading pictures in the upload directory, and place the complete code in the appendix. Remember to start uwsgi before accessing the RESTful API.

# Switch to the correct directory
cd /home/ec2-user/fishRecognition/fishsite
# Enable uwsgi to access the RESTful API
uwsgi --ini uwsgi.ini
# This is the directory of the static web page
cd upload/
# Create test web page file
vi fishrecog.html

In the following figure, you can see that when you drag a graph to the specified dotted box, you will call impUpload, a RESTful API, and the response results will be displayed at the bottom of the graph.


Figure 2. Test web page results on EC2

But when we open the same web page locally (file: / / [localpath] / fishrecog. HTML). There is no response from the same operation. Open the developer tool of Chrome and you can see that the error is caused by CORS in the Console window, and the security restriction of CORS is caused by the browser. When a CORS request occurs, the browser will ask whether the server supports it. If not, an error message will be sent.


Figure 3. Test web page results on the local host

Therefore, you must open the access restrictions of CORS in Django, open settings.py and set a middleware to respond to CORS requests, as shown in the figure below.

fishsite/settings.py

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'fishsite.cors.CorsMiddleware',
]


Figure 4. Adding middleware settings in settings.py

Add middleware file

CORS requests can be divided into the following two types:

  • Simple request: for one request, you can specify the allowed source access control allow origin. If not specified, use * to agree to the requests of all sources.
  • Non simple request: for two requests, one request will be sent before sending data for "preflight". Only after the "preflight" passes, the request will be sent again.

The pre inspection contents are as follows:

  • Request method: OPTIONS
  • "Pre check" is actually a check. If the check passes, the data is allowed to be transmitted. If the check fails, the message you really want to send will not be sent
  • How to "pre check"
    • If the complex request is a PUT or other method request, the server needs to set a method to allow a request, otherwise the "pre check" fails
      * Access-Control-Request-Method = GET, POST, ...
    • If the request content header is set for a complex request, the server needs to set the allowed request content header, otherwise the "pre check" fails
      Access-Control-Request-Headers = text/plain,text/html, ...

The relevant implementation codes are as follows.

fishsite/cors.py

from django.utils.deprecation import MiddlewareMixin
from django.conf import settings

class CorsMiddleware(MiddlewareMixin):

    def process_response(self,request,response):
        response['Access-Control-Allow-Origin'] = '*'
        #response['Access-Control-Allow-Credentials'] =  'true'
        if request.method == "OPTIONS":
            response["Access-Control-Allow-Methods"] = "GET,POST,PUT,DELETE"
            response["Access-Control-Allow-Headers"] = "Content-Type"
            response.status_code = 200
        return response


Figure 5. Confirm relevant positions of all files

Because Django has been modified, to restart uwsgi, delete the original uwsgi trip first

ps aux | grep uw
kill -9 8300 8302
uwsgi --ini uwsgi.ini


Figure 6. Restarting uwsgi

Then go back to the browser on the local side, find the picture that has just been opened in the local file mode, and then drag another picture to see the return result of the RESTful API at the bottom of the picture; In the developer tool window, select the network function page. You can find that the browser will send two imgUpload requests. The first is pre check. You can find that the request method is OPTIONS. After the pre check request is successful, the second actual request will be sent.


Figure 7. Re execute the local web page operation

After completing CORS, we hope to automatically start all services, including uwsgi. Therefore, we need to manually set it to automatically start uwsgi every time the system is started. There is a / etc/rc.local file in Linux, and the script file will be automatically executed every time the system is started, so we only need to put the commands to be executed into the file, We add a line of instructions at the end of the file. The detailed relevant positions are shown in the figure below.

/etc/rc.local

# Execute the command as EC2 user user 
su ec2-user -c 'cd /home/ec2-user/fishRecognition/fishsite;/home/ec2-user/fishRecognition/bin/uwsgi -d --ini uwsgi.ini'


Figure 8. Adding uwsgi command to startup execution file

As long as you restart EC2, you can confirm whether it takes effect after running the previous screen.

reference material

  • Cross source resource sharing (CORS), https://developer.mozilla.org/zh-TW/docs/Web/HTTP/CORS
  • Cross-Origin Resource Sharing (CORS), https://web.dev/cross-origin-resource-sharing/
  • How to Enable CORS in NGINX, https://ubiq.co/tech-blog/enable-cors-nginx/
  • Status Codes, https://www.django-rest-framework.org/api-guide/status-codes/
  • python full stack development, Day100(restful interface, DRF component, DRF cross domain (cors component)), https://www.cnblogs.com/xiao987334176/articles/9457580.html
  • Execute script from rc.local as user instead of root, https://serverfault.com/questions/422950/execute-script-from-rc-local-as-user-instead-of-root

appendix

fishrecog.html

<head>
<meta charset="utf-8"/>
<title>Ornamental fish identification system</title>
<style>
#holder { border: 10px dashed #ccc; width: 300px; min-height: 300px; margin: 20px auto;}
#holder.hover { border: 10px dashed #0c0; }
#holder img { display: block; margin: 10px auto; }
#holder p { margin: 10px; font-size: 14px; }
progress { width: 100%; }
progress:after { content: '%'; }
.fail { background: #c00; padding: 2px; color: #fff; }
.hidden { display: none !important;}
</style>
</head>
<body>
<article>
  <div id="holder">
  </div> 
  <p id="upload" class="hidden"><label>Drag and drop is not supported, but you can select pictures directly:<br><input type="file"></label></p>
  <p id="filereader">File API &amp; FileReader API not supported</p>
  <p id="formdata">XHR2's FormData is not supported</p>
  <p id="progress">XHR2's upload progress isn't supported</p>
  <p>Upload progress: <progress id="uploadprogress" max="100" value="0">0</progress></p>
  <p>The ornamental fish identification system can directly drag a picture to the dotted line area</p>
  <div id="result"></div>
</article>
<script>
var holder = document.getElementById('holder'),
    tests = {
      filereader: typeof FileReader != 'undefined',
      dnd: 'draggable' in document.createElement('span'),
      formdata: !!window.FormData,
      progress: "upload" in new XMLHttpRequest
    }, 
    support = {
      filereader: document.getElementById('filereader'),
      formdata: document.getElementById('formdata'),
      progress: document.getElementById('progress')
    },
    acceptedTypes = {
      'image/png': true,
      'image/jpeg': true,
      'image/gif': true
    },
    progress = document.getElementById('uploadprogress'),
    fileupload = document.getElementById('upload');

"filereader formdata progress".split(' ').forEach(function (api) {
  if (tests[api] === false) {
    support[api].className = 'fail';
  } else {
    // FFS. I could have done el.hidden = true, but IE doesn't support
    // hidden, so I tried to create a polyfill that would extend the
    // Element.prototype, but then IE10 doesn't even give me access
    // to the Element object. Brilliant.
    support[api].className = 'hidden';
  }
});

function previewfile(file) {
  if (tests.filereader === true && acceptedTypes[file.type] === true) {
    var reader = new FileReader();
    reader.onload = function (event) {
      var image = new Image();
      image.src = event.target.result;
      image.width = 250; // a fake resize
      holder.appendChild(image);
    };

    reader.readAsDataURL(file);
  }  else {
    holder.innerHTML += '<p>Document Uploaded ' + file.name + ' ' + (file.size ? (file.size/1024|0) + 'K' : '');
    console.log(file);
  }
}

function readfiles(files) {
    //debugger;
    var formData = tests.formdata ? new FormData() : null;
    for (var i = 0; i < files.length; i++) {
      if (tests.formdata) formData.append('fileUpload', files[i]);
      previewfile(files[i]);
    }

    if (tests.formdata) {
      var xhr = new XMLHttpRequest();
      xhr.open('POST', 'http://13.229.139.117/imgUpload/');
      xhr.onreadystatechange = function() {
			if (xhr.readyState == XMLHttpRequest.DONE) {
				result = document.getElementById('result');
				var tr=xhr.responseText;
				var utr=unescape(tr.replace(/\\u/gi, '%u'));
				//jsonarr = eval('(' + xhr.responseText + ')');
				var arr = JSON.parse(utr);

				console.log(arr);
				var str ='<p>Fish Name:'+arr[0].fishName+'</p><p>Identification quantity:'+arr[0].fishQtn+'</p><p>Latin name:'+arr[0].LatinName+'</p><p>Haunt area:'+arr[0].distribution+'</p>'; 
				result.innerHTML = str;
			}
		}
      xhr.onload = function() {
        progress.value = progress.innerHTML = 100;
      };

      if (tests.progress) {
        xhr.upload.onprogress = function (event) {
          if (event.lengthComputable) {
            var complete = (event.loaded / event.total * 100 | 0);
            progress.value = progress.innerHTML = complete;
          }
        }
      }

      xhr.send(formData);
    }
}

if (tests.dnd) { 
  holder.ondragover = function () { this.className = 'hover'; return false; };
  holder.ondragend = function () { this.className = ''; return false; };
  holder.ondrop = function (e) {
    this.className = '';
    e.preventDefault();
    readfiles(e.dataTransfer.files);
  }
} else {
  fileupload.className = 'hidden';
  fileupload.querySelector('input').onchange = function () {
    readfiles(this.files);
  };
}

</script>

</body>

Keywords: Linux AWS Django Back-end

Added by mick_otoole on Wed, 27 Oct 2021 14:28:23 +0300