django#AJAX

DjangoでAJAX(非同期通信)のシンプルコードです。
>参考LINK
http://musings.tinbrain.net/blog/2015/aug/28/vanilla-js-meets-djangos-csrf/
https://blog.garstasio.com/you-dont-need-jquery/ajax/

その前に、まず復習しましょう。

## POSTとはなに？GETとはなに？
英語に抵抗がない方には下記のLINKで参考できます↓
https://www.w3schools.com/tags/ref_httpmethods.asp
https://stackoverflow.com/questions/3477333/what-is-the-difference-between-post-and-get
実はPOSTとGETだけではなくHTTP MethodsはPUT、DELETEなどもあり
(Siemen社ｍのPLCはGET/PUTを使ってThird-Partyとやり取りしたりもあり)。簡単にいうと、
### GET Method
リソースからデータを要求する。まぁ、データくださいって感じですね、
GET Methodはおそらく一番使ってるでしょう。GETは以下の特徴があります：

* Cached可能
* Browser Historyの中に保存できる
* BrowserのBookmarkできる
* データの扱い(パスワードとか)基本しない
* 長さの制限がある
* データ要求(変更ない)のとき使う

### The POST Method
データをサーバーなどに送り、リソースを操作する。
まぁ、パスワードを変更します！って感じですね。POSTは以下の特徴があります：

* Cachedしない	
* Browser Historyの中に保存しない
* BrowserのBookmarkできない
* 長さの制限がない

## AJAXとはなに？

https://www.w3schools.com/js/js_ajax_intro.asp

AJAXは開発者の魔法です。なぜかというとあなたはAJAXを使えばWEB PAGEをF5を押さずに更新できます。
WEB PAGEがLOADしたあとでもサーバーとデータをやり取りできます。AJAXの正式の名前はAsynchronous JavaScript And XMLですが、
プログラム言語ではありません。AJAXはBrowserのbuilt-inオブジェクトを使って実現できるものです。

XMLHttpRequestオブジェクト-Web serverにデータを要求する。
Javascript,HTML DOMオブジェクト-Userデータを表示する。
AJAXはちょっと混乱させる名前かもしれませんけど、AJAXはを使うときは必ずXML使うとは限るじゃないです。
Plain text、Json textでも大丈夫です。

* まずイベントがWEB PAGEの中に発生。（例えばPAGEがLOADされた、ボタン押し）
* JavascriptはXMLHttpRequestオブジェクトを生成。
* XMLHttpRequestはWEB Serverに要求を送る。
* サーバー側が要求を処理する。
* サーバー側が返事する。
* Javascriptが返事内容をEncodeする。
* 画面が変わる。

## CSRFとはなに？
https://en.wikipedia.org/wiki/Cross-site_request_forgery

CSRFは(Cross Site Request Forgery protection)なんでしょうか？
例えにばあなたがお菓子屋さんりの招待状をもらってお菓子食べ放題のところ行くことになりました。菓子屋さんは警備さんがあって中に入る前最初に招待状の確認が要求されます。そして警備さんに招待状に渡したけど代わりにチケットがもらいました。今後再入場するときも招待状ではなく警備さんがあなたのチッケトを確認することだけになりました。
とある日、悪い子があなたに彼の家に遊んでたとき、彼はあなたが持ってるチケットをコピーしました。最後、あの悪い子があなたの“チケット”もってあなたの振りにしてお菓子屋さんの中に入ることができした！
これはCSRFですね。中にある“Loging CSRF”つまり怪しいWEBの中にScript、Form linkなどが含まられてはあなたの認証情のなにかもらってあなたの振りでLoginしいろんな操作をします。
先の例だと、
お菓子屋＝銀行、怪しいWEBが悪い子のお家。

## AJAXとGET

home.htmlを作ります。

```django
<h1>home</h1>
		
		<button type='submit'
			onclick='XMLHttpRequestInGet()' 
		>Submit In Get</button>

		{% csrf_token %}
		<button type='submit'
			onclick='XMLHttpRequestInPOST()' 
		>Submit In POST</button>

{% load static %}
<script src="{% static 'jQuery.js' %}"></script>
<script src="{% static 'script.js' %}"></script>
```

画面はこうなりましたね。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/745c6e4e-b3d2-4af7-2c46-60e43d1363ce.png)

次はviews.py にGET Methodを送るときの処理を書きます。

```py
def send_request(request):
	print('****************')
	print('ajax is done')
	return HttpResponse("ajax is done!")
```

urls.pyにPathを追加します。

```py
from django.contrib import admin
from django.urls import path
from . import views

urlpatterns = [
	path('admin/', admin.site.urls),
    path('',views.homepage,name='homepage'),
    path('send_Request/',views.send_request,name='send_request'),
    path('post_request/',views.post_request,name='post_request')
]
```
次は一番大事なJavascriptですね。
今回使ってるのはscript.jsとjQuery.jsだけですね。
jQueryはWebで最新バージョンをダンロードしてください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/e23bf1c7-97ad-f907-341f-10bf16040fb1.png)

### GET MethodのPure javascriptバージョン

```js
function XMLHttpRequestInGet(){
  //------------ using pure javascript---------------//
  var xhr = new XMLHttpRequest();
    xhr.open('GET', '/send_Request');
    xhr.onload = function() {
        if (xhr.status === 200) {
           callback(xhr.responseText)
        }
        else {
            alert('Request failed.  Returned status of ' + status)
        }
        function callback(result){
           alert(result+' is done!')
        }
    };
    xhr.send();	
}
```

### GET MethodのjQueryバージョン

```js
function XMLHttpRequestInGet(){
  //------------ using jQuery---------------//
  $.ajax('/send_Request', {
    data: {
        id: 'some-unique-id'
    }
  })
  .then(
      function success(name) {
          alert(name+' is done!');
      },

      function fail(data, status) {
          alert('Request failed.  Returned status of ' + status);
      }
  );
```

ボタン押したら両方ともできましたね～

![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/cbac8a64-fd76-aa63-cfc4-02cc2a3bd9ed.png)
Terminalからみてもありましたね～
![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/ce495729-e178-f384-dedf-58dc8e172478.png)

## AJAXとPOST
まずDjangoはにリクエストがCsrfViewMiddleware のCheckが通らなかったら　‘403 Forbidden’の返事を返しします。
これは以下のどっちかのせいで403が返してくれる：

* CSRFが発生
* プログラムが問題があり
* CSRF tokenがPOST formの中に入ってない。
このエラーはあまり優しくないのでシステムの中にLoggingを作ったいいかもしれない…みたいです！CSRF_FAILURE_VIEWを使えばエラーコードと情報を返すこともできますし、CSRFのエラーもdjango.security.csrf loggerのWarning levelに入ってます。

```py
def csrf_failure(request, reason=""):
　　...
```
### でもどうやって？


* まずCSRF cookieは乱数のセキュリティ値で、他のSiteからアクセスすることができません。このcookieはCsrfViewMiddlewareに設定され、毎回responseが返すときにdjango.middleware.csrf.get_token()がCallされ作られます。
* すべてサーバーに送るPost formsが‘csrfmiddlewaretoken’’といみえないform fieldあり、ここはtemplate tagがやってくれます。
* Web Browserなどから送るリクエストはHTTP GET, HEAD, OPTIONS ,TRACEどっちでもなかれば、CSRF Cookieは必ずcsrfmiddlewaretoken’と一致しないといけません。そうではないと403エラーが返してくれる。CsrfViewMiddlewareはそれをやってくれます。

views.py にPOST Methodを送るときの処理を書きます。

```py
def post_request(request):
	print('-'*10)
	print('ajax is done')
	return HttpResponse('ajax is done in POST!')
```

### POST MethodのPure javascriptバージョン

```js
function XMLHttpRequestInPOST(){

 //------------ using pure javascript---------------//

  function parse_cookies() {
      var cookies = {};
      if (document.cookie && document.cookie !== '') {
          document.cookie.split(';').forEach(function (c) {
              var m = c.trim().match(/(\w+)=(.*)/);
              if(m !== undefined) {
                  cookies[m[1]] = decodeURIComponent(m[2]);
              }
          });
      }
      return cookies;
  }
  var cookies = parse_cookies(); 
  console.log(cookies)
  var user={'user':'soup01'}
  var xhr = new XMLHttpRequest();
  
  xhr.open("POST", "/post_request/");
  xhr.setRequestHeader('X-CSRFToken', cookies['csrftoken']); 
  xhr.send({user});

    xhr.onload = function() {
    if (xhr.status === 200) {
        alert('status code is 200. \n Your responseText is: ' + xhr.responseText);
    }
    else if (xhr.status !== 200) {
        alert('Request failed.  Returned status of ' + xhr.status);
    }
};
}
```

### GET MethodのjQueryバージョン
```js
function XMLHttpRequestInPOST(){
------------ using jQuery---------------//
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});
$.ajax({
        url: '/post_request/',
        data: {'user':'chris'},
        beforeSend: function(xhr, settings) {
          if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
          }
        },
        dataType: "json",
        type: "POST",
      });
}
```
ボタン押したら両方ともできましたね～

![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/5ee33784-c91f-3390-718e-efe2c2bfaa02.png)
Terminalからみてもありましたね～
![image.png](https://qiita-image-store.s3.amazonaws.com/0/154661/035c455b-56c8-d5b0-d3f0-3d9f800fe226.png)


以上になりますが、私はJSにはこんな詳しくないので間違い・もっといい方法があれば教えてください！お願いします！
あとCSRFのことも、わかるようなわからないような…