Template Engine dan Fungsi Handle Post
Template Engine
Sebelumnya kita sudah membuat web server dinamis yang di dalamnya dapat menjalankan program lain contohnya codingan seperti berikut ini
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> {% l = _QUERY_STRING['login'] u = _POST['username'] p = _POST['password'] emit(l) if (u == 'aku') and (p == 'saya'): emit('Login successful') else: emit('Login failed') %}</body></html> |
Maka saat web server di jalankan, yang terjadi baris kode didalam {%%} akan di eksekusi, sebelum itu baris kode tersebut perlu di ganti kedalam baris kode yang dapat di eksekusi sehingga dapat di render, nah temen-temen untuk merubah atau membaca program tersebut maka dibuatlah class template , kodenya seperti berikut :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | import reclass Template(): """Compile an text into a template function""" def __init__(self, text): self.delimiter = re.compile(r'{%(.*?)%}', re.DOTALL) self.tokens = self.compile(text) def kompile(self, text): tokens = [] for index, token in enumerate(self.delimiter.split(text)): if index % 2 == 0: # plain string if token: tokens.append((False, token.replace('%\}', '%}').replace('{\%', '{%'))) else: # code block # find out the indentation lines = token.replace('{\%', '{%').replace('%\}', '%}').splitlines() # indent = 0 # for l in lines: # if l.strip(): # indent = min([len(l)- len(l.strip())]) # print(indent) indent = min([len(l) - len(l.lstrip()) for l in lines if l.strip()]) realigned = '\n'.join(l[indent:] for l in lines) tokens.append((True, compile(realigned, '<tempalte> %s' % realigned[:20], 'exec'))) return tokens def render(self, context = None, **kw): """Render the template according to the given context""" global_context = {} if context: global_context.update(context) if kw: global_context.update(kw) # add function for output def emit(*args): result.extend([str(arg) for arg in args]) def fmt_emit(fmt, *args): result.append(fmt % args) global_context['emit'] = emit global_context['fmt_emit'] = fmt_emit # run the code result = [] for is_code, token in self.tokens: if is_code: exec(token, global_context) else: result.append(token) return ''.join(result) |
Disini saya akan membedah kodenya diawali dari atas , pada posisi paling atas terdapat kode import re , dimana re singkatan dari Regular Expression , Regular expression (regex) merupakan deretan karakter yang dipakai untuk pencarian string atau teks dengan menggunakan pola (pattern). Contohnya pada penulisan email nuriasafitri805@gmail.com, jika diteliti dengan seksama email memiliki semacam pola, setelah huruf atau angka akan di ikuti dengan tanda @ selanjutnya di ikuti huruf lalu titik dan huruf lagi. jadi Regex membantu kita mencari string tertentu dalam text yang banyak.
Jika sudah mengimport modul yang akan dipakai selanjutnya menulis kode di awali dengan membuat class yang kita beri nama Templat selanjutnya kita membuat constructor , constructor merupakan method yang pertama kali di jalankan ketika class atau objek di buat , pada python untuk membuat constructor kita menuliskannya seperti berikut
1 | def __init__(self, text): |
Membuat function atau method di python dimulai dengan def kemudian di lanjutkan dengan nama methodnya lalu lalu parameter didalam kurung, karena method di atas merupakan constructor maka nama methodnya harus _init_ ,di setiap class harus ada self yang menjadi parameter pertamanya , dan juga ada parameter text yang kita buat guna menampung data yang diterima dari luar class
1 2 3 | def __init__(self, text): self.delimiter = re.compile(r'{%(.*?)%}', re.DOTALL) self.tokens = self.kompile(text) |
Selanjutnya pada constructor kita membuat properti self.delimiter yang di isi dengan hasil compile dari Regular Expression dan membuat properti self.token yang di isi dengan hasil method kompile dengan inputan text, lalu kita lanjutkan dengan method kompile.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | def kompile(self, text): tokens = [] for index, token in enumerate(self.delimiter.split(text)): if index % 2 == 0: # plain string if token: tokens.append((False, token.replace('%\}', '%}').replace('{\%', '{%'))) else: # code block # find out the indentation lines = token.replace('{\%', '{%').replace('%\}', '%}').splitlines() # indent = 0 # for l in lines: # if l.strip(): # indent = min([len(l)- len(l.strip())]) # print(indent) indent = min([len(l) - len(l.lstrip()) for l in lines if l.strip()]) realigned = '\n'.join(l[indent:] for l in lines) tokens.append((True, compile(realigned, '<tempalte> %s' % realigned[:20], 'exec'))) return tokens |
method kompile menerima parameter self dan text, kemudian kita membuat array kosong yang di beri nama
1 | tokens = [] |
kemudian di lakukan perulangan seperti berikut
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | for index, token in enumerate(self.delimiter.split(text)): if index % 2 == 0: # plain string if token: tokens.append((False, token.replace('%\}', '%}').replace('{\%', '{%'))) else: # code block # find out the indentation lines = token.replace('{\%', '{%').replace('%\}', '%}').splitlines() indent = min([len(l) - len(l.lstrip()) for l in lines if l.strip()]) realigned = '\n'.join(l[indent:] for l in lines) tokens.append((True, compile(realigned, '<tempalte> %s' % realigned[:20], 'exec')))return tokens |
Perulangan di atas untuk mendapatkan index dan token , index merupakan index dari text yang sudah di compile menggunakan Regular Expression, sehingga text berubah menjadi array dan juga memiliki index yang ambil oleh index dan token mengambil isi dari array, selanjutnya di lakukan pengecekan jika index merupakan modulus 2 dan juga ada token di dalamnya atau tidak kosong maka token dikirim apa adanya, dan ketika index bukan modulus dua atau ganjil maka token akan di splitlines() kemudian di hilangkan tab atau indentasi karena pada python indentasi ayau spasi sangat berpengaruh, setelah di sesuaikan tabnya maka di lakukan realign atau penyusunan ulang setelah itu di kirim.
Setelah kode sudah di lakukan compile maka selanjutnya di lakukan eksekusi , method yang menjalankan eksekusi kita beri nama render() yang memiliki parameter self dan context yang di inisialisasikan dengan None, kodenya seperti berikut
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | def render(self, context = None, **kw): """Render the template according to the given context""" global_context = {} if context: global_context.update(context) if kw: global_context.update(kw) # add function for output def emit(*args): result.extend([str(arg) for arg in args]) def fmt_emit(fmt, *args): result.append(fmt % args) global_context['emit'] = emit global_context['fmt_emit'] = fmt_emit # run the code result = [] for is_code, token in self.tokens: if is_code: exec(token, global_context) else: result.append(token) return ''.join(result) |
Buat lib baru yang bernama global_context , kemudian dilakukan pengecekan ketika context memiliki isi maka global_context akan di update dengan context lalu membuat function untuk eksekusi program
1 2 3 4 | def emit(*args): result.extend([str(arg) for arg in args])def fmt_emit(fmt, *args): result.append(fmt % args) |
Selanjutnya di dalam global_context di masukan
1 2 | global_context['emit'] = emitglobal_context['fmt_emit'] = fmt_emit |
Kemudian di lakukan pengecekan jika self.token merupakan kode maka akan di eksekusi
1 2 3 4 5 6 | result = []for is_code, token in self.tokens: if is_code: exec(token, global_context) else: result.append(token) |
Fungsi Handle Post
Pada web server dinamis yang kemarin terdapat fungsi tambahan untuk menangani post, dimana ketika clien mengirim method post maka akan di handle oleh function handle post untuk kodenya sebagai berikut
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | def handle_post(url, http_version, data): url = "htdocs/%s"%(url) if os.path.exists(url) and not os.path.isdir(url): response_line = b''.join([http_version.encode(), b'200', b'OK']) content_type = mimetypes.guess_type(url)[0] or 'text/html' entity_header = b''.join([b'Content-type: ', content_type.encode()]) file = open(url, 'r') html = file.read() file.close() template = Template(html) _POST = {} for x in data.split('&'): y = x.split('=') _POST[y[0]]=y[1] print(_POST) context = { '_POST' : _POST } message_body = template.render(context).encode() else : response_line = b''.join([http_version.encode(), b'404', b'Not Found']) entity_header = b'Content-Type: text/html' message_body = b'<h1>404 Not Found</h1>' crlf = b'\r\n' response = b''.join([response_line, crlf, entity_header, crlf, crlf, message_body]) return response |
di karenakan ada handle post maka terjadi perubahan di handle request
1 2 3 4 5 6 | if(method == 'GET'): response = handle_get(url, http_version)elif(method == 'POST'): data = request_message[len(request_message)-1] response = handle_post(url, http_version,data)return response |
di karenakan handle post mengirim data , maka data tersebut dapat di ambil pada request_line yang terakhir selanjutnya dikirim sebagai parameter di handle post untuk di proses, berikut contoh penerapan method post
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <form action="ceklogin.html" method="POST"> <input type="text" name="username" > <input type="password" name="password" > <input type="submit" value="Login"> </form></body></html> |
di atas adalah form login dan ketika tombol submit di klik maka akan mengirim request ke server dengan method POST lalu halaman akan di alihkan ke halaman ceklogin.html, proses yang terjadi pada handle_post hampir sama dengan handle_get yang membedakan yaitu adanya data yang di kirim , ketika kita menekan tombol submit yang terjadi adalah browser mengirim method POST dengan url ceklogin.html, data username dan juga password yang kemudian data ini di masukan kedalam context dengan perulangan di bawah ini
1 2 3 | for x in data.split('&'): y = x.split('=') _POST[y[0]]=y[1] |
Kemudian data yang sudah di masukan tadi di render dan di rubah ke bentuk byte yang selanjutnya di simpan ke massage_body untuk di return
Komentar
Posting Komentar