1. 소스코드 넣고 실행하기.
해당 소스코드를 넣고 실행
포트번호는 8090이다.
혹시 에러가 뜰 경우
# -*- coding: utf-8 -*-
를 상단에 추가한다.
2. 소켓 서버가 잘 작동하는지 확인하기
터미널에 netstat -an | grep 8090 를 입력하거나.
꿈나무@초록꿈나무 ~ % netstat -an | grep 8090
tcp4 0 0 *.8090 *.* LISTEN
tcp6 0 0 fe80::1cfb:934f:.63848 fe80::4f4:76af:c.58090 CLOSE_WAIT
네트워크 유틸리티에서 포트 스캔을 통해 가능.
3. 모듈 설명
#!/usr/bin/python
# -*- coding: utf-8 -*-
# from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
# 필요한 모듈을 불러옴.
from http.server import BaseHTTPRequestHandler,HTTPServer #소켓 서버 실행용
from socketserver import ThreadingMixIn # 아직 안 씀. 근데 왜 음영처리 되어있지?
import json # requests : response를 위해 json 형식을 사용함. Key:Value 쌍.
import re # Regular Expression (정규표현식) 모듈. 정규표현식이란? 문자열을 어떻게 처리할지에 대한 기준이 되는 표현 방식.
# 예를 들어 어떤 문장에서 숫자만 골라내고 싶은 경우, 어떤 특정 문자가 반복되는 경우 바꾼다던가, 그 문자를 기준으로 나눈다던가 하는 것이다.
from urllib.parse import parse_qs # URL 문자열을 구성 요소(주소 지정 체계, 네트워크 위치, 경로 등)으로 분리하고,
# 구성 요소를 다시 URL 문자열로 결합하고, '상대 URL'을 절대 'URL'로 변환하는 표준 인터페이스를 정의.
import cgi # Common Gateway Interface
PORT_NUMBER = 8090 # 전역변수. 어디선가 이걸 아래서 쓸거다.
4. 도움말 열기
혹시 다음과 같은 에러가 발생하면 Cannot find declaration to go to
Project Files 우클릭 -> Mark Directory as -> Sources Root를 눌러준다. 혹시 몰라 각 폴더마다 다 눌러줬더니 정상적으로 작동한다.
(도움말 보다 이전 탭으로 가고 싶다면 'Control + 좌우 방향키'로 탭이동이 가능하다.)
5. 상속
class myHandler(BaseHTTPRequestHandler): # Type : class, Name : myHandler, Super class : BaseHTTPRequestHandler (저 위에 form http.server import...)
# Handler for the GET requests
def do_GET(self): # Override다. Super class인 BaseHTTPRequestHandler에 'do_GET' 함수가 있는데 Sub class인 myHandler에서 재정의해서 사용한다.
# 자바에서는 @Override를 붙여서 명시해줬지만 파이썬에서는 명시할 필요 없이 그냥 Super class에 있는 함수명을 그대로 가져다 다시 정의하면 된다.
print('Get request received')
if None != re.search('/api/v1/getrecord/*', self.path):
recordID = (self.path.split('/')[-1]).split('?')[0]
print("recordID = ", recordID)
if recordID == "1" :
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
# Send the html message
self.wfile.write(bytes("<html><head><title>Title goes here.</title></head>", "utf-8")) #"euc-kr"
self.wfile.write(bytes("<body><p>This is a test.</p>", "utf-8"))
self.wfile.write(bytes("<p>Your accessed path: %s</p>" % self.path, "utf-8"))
self.wfile.write(bytes("</body></html>", "utf-8"))
else:
self.send_response(400, 'Bad Request: record does not exist')
self.send_header('Content-Type', 'application/json')
self.end_headers()
Super class에 있는 do_GET은 serve.py에서 'do_GET'을 검색해보면 나온다.
원래 Super class에 있는 do_GET은 저장만 하고 만다.
GET : 서버에 정보를 요청하겠다. 쿼리로 치면 Select
POST : 서버에 정보를 준다. 쿼리로 치면 Insert
PUT : 업데이트.
DELETE : 삭제.
6. 라인이 내려간 후 실행되는 부분
try: # 예외처리. 자바에서 try catch와 같은 기능.
# Create a web server and define the handler to manage the
# incoming request
server = HTTPServer(('', PORT_NUMBER), myHandler) # HTTPServer : 위에 불러온 모듈, '서비스 할 IP' : 공란은 모든 IP에 대해서 서비스를 하겠다.
# PORT_NUMBER : 위에 있는 전역변수 8090, myHandler : BaseHTTPRequestHandler를 상속 받은 오버라이드가 들어갔다.(도움말 열어보면 얘가 들어가야 한다 나옴.)
# HTTPServer에 (괄호)안의 것들을 생성자로 넘겨주는거다. 그렇 server라는 객체를 생성한다.
print ('Started httpserver on port ' , PORT_NUMBER)
# Wait forever for incoming http requests
server.serve_forever() # server 라는 객체를 실행시킨다.
except:
print ('^C received, shutting down the web server')
print("서버 종료1!!")
server.socket.close() # 8090 포트를 닫아준다. (이거 안 닫으면 계속 점유하게된다.) 자원해제하는 절차.
7. 실행
서버를 실행시키고 웹 브라우저에
localhost:8090/api/v1/getrecord/1?k=v
를 입력.
웹 브라우저에는
This is a test.
Your accessed path: /api/v1/getrecord/1?k=v
라고 뜨고,
파이참에는
Get request received
recordID = 1
127.0.0.1 - - [20/Apr/2020 21:47:56] "GET /api/v1/getrecord/1?k=v HTTP/1.1" 200 -
라고 뜬다.
8. 디버깅
디버깅 할 라인에 마우스 클릭하면 빨간 점이 생긴다.
다시 웹브라우저에서 새로고침을 하면
do_GET이 라인 27에서 멈추었다고 보여준다. 실행 순서는 아래서부터 위로 보면 된다.
한 단계씩 디버깅하기. 함수를 만나면 함수를 건너뜀.
한 단계씩 디버깅하기. 함수를 만나면 함수 안으로 들어감.
Step Over로 진행하자.
담겨진 값도 확인이 가능하고
(self, wfile은 각각의 객체들. 그 객체들한테 write를 시켰다.)
단계별로 진행되는 것을 볼 수 있다.
추가적으로 보고싶은 것이 있다면
'+' 버튼을 눌러 원하는 것을 추가한 다음 디버깅을 하면 된다.
9. split
이 부분을 보면
self.path에는 '/api/v1/getrecord/1?k=v'가 담겨있다.
그것을 split하라 '/'를 기준으로, 그리고 인덱스 -1을 취하라. -> '1?k=v'
다시 split하라 '?'를 기준으로, 그리고 인덱스 0을 취하라. -> '1'
그 결과 recordID = '1'이 된다.
10. if문
if recordID == "1" :
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
# Send the html message
self.wfile.write(bytes("<html><head><title>Title goes here.</title></head>", "utf-8")) #"euc-kr"
self.wfile.write(bytes("<body><p>This is a test.</p>", "utf-8"))
self.wfile.write(bytes("<p>Your accessed path: %s</p>" % self.path, "utf-8")) # %s% -> self.path가 요청한 경로 URL을 그대로 받을 수 있다.
self.wfile.write(bytes("</body></html>", "utf-8"))
else:
self.send_response(400, 'Bad Request: record does not exist')
self.send_header('Content-Type', 'application/json')
self.end_headers()
recordID가 '1'이면 실행(response 200), 아니면 (response 400)을 준다.
'localhost:8090/api/v1/getrecord/1?k=v' 대신 'localhost:8090/api/v1/getrecord/3?k=v'으로 바꿔서 해보면
recordID가 '3'이 뜨고 if문에서 조건을 테스트 한 다음 바로 else로 넘어간다.
전체 코드
#!/usr/bin/python
# -*- coding: utf-8 -*-
# from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
# 필요한 모듈을 불러옴.
from http.server import BaseHTTPRequestHandler,HTTPServer #소켓 서버 실행용
from socketserver import ThreadingMixIn # 아직 안 씀. 근데 왜 음영처리 되어있지?
import json # requests : response를 위해 json 형식을 사용함. Key:Value 쌍.
import re # Regular Expression (정규표현식) 모듈. 정규표현식이란? 문자열을 어떻게 처리할지에 대한 기준이 되는 표현 방식.
# 예를 들어 어떤 문장에서 숫자만 골라내고 싶은 경우, 어떤 특정 문자가 반복되는 경우 바꾼다던가, 그 문자를 기준으로 나눈다던가 하는 것이다.
from urllib.parse import parse_qs # URL 문자열을 구성 요소(주소 지정 체계, 네트워크 위치, 경로 등)으로 분리하고,
# 구성 요소를 다시 URL 문자열로 결합하고, '상대 URL'을 절대 'URL'로 변환하는 표준 인터페이스를 정의.
import cgi # Common Gateway Interface
PORT_NUMBER = 8090 # 전역변수. 어디선가 이걸 아래서 쓸거다.
# This class will handle any incoming request from
# a browser
class myHandler(BaseHTTPRequestHandler): # Type : class, Name : myHandler, Super class : BaseHTTPRequestHandler (저 위에 form http.server import...)
# Handler for the GET requests
def do_GET(self): # Override다. Super class인 BaseHTTPRequestHandler에 'do_GET' 함수가 있는데 Sub class인 myHandler에서 재정의해서 사용한다.
# 자바에서는 @Override를 붙여서 명시해줬지만 파이썬에서는 명시할 필요 없이 그냥 Super class에 있는 함수명을 그대로 가져다 다시 정의하면 된다.
print('Get request received')
if None != re.search('/api/v1/getrecord/*', self.path):
recordID = (self.path.split('/')[-1]).split('?')[0]
print("recordID = ", recordID)
if recordID == "1" :
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
# Send the html message
self.wfile.write(bytes("<html><head><title>Title goes here.</title></head>", "utf-8")) #"euc-kr"
self.wfile.write(bytes("<body><p>This is a test.</p>", "utf-8"))
self.wfile.write(bytes("<p>Your accessed path: %s</p>" % self.path, "utf-8")) # %s% -> self.path가 요청한 경로 URL을 그대로 받을 수 있다.
self.wfile.write(bytes("</body></html>", "utf-8"))
else:
self.send_response(400, 'Bad Request: record does not exist')
self.send_header('Content-Type', 'application/json')
self.end_headers()
def do_POST(self):
if None != re.search('/api/v1/addrecord/*', self.path):
ctype, pdict = cgi.parse_header(self.headers['content-type']) # application/json;encoding=utf-8;lang=ko;loc=seoul;...
print(ctype) # application/json
print(pdict) # {encoding:utf-8, lang:ko, loc:seoul}
if ctype == 'application/json':
content_length = int(self.headers['Content-Length']) # 48 bytes
post_data = self.rfile.read(content_length) # .rfile.read( ) 읽어와라. (bytes로 읽어옴.)
receivedData = post_data.decode('utf-8') # .decode('utf-8')을 통해 우리가 아는 문자열로 바뀜.
print(type(receivedData))
tempDict = json.loads(receivedData) # load your str into a dict # 위에 문자열로 바꾼걸 다시 딕셔너리로 바꿈.
#print(type(tempDict)) #print(tempDict['this'])
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(bytes(json.dumps(tempDict), "utf-8")) # 위에서 bytes -> str -> dic 으로 바꿔서 읽어들였던 것을 역순으로 해서 내보낸다. dic -> str -> bytes
elif ctype == 'application/x-www-form-urlencoded':
content_length = int(self.headers['content-length'])
# trouble shooting, below code ref : https://github.com/aws/chalice/issues/355
postvars = parse_qs((self.rfile.read(content_length)).decode('utf-8'),keep_blank_values=True)
#print(postvars) #print(type(postvars)) #print(postvars.keys())
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.wfile.write(bytes(json.dumps(postvars) ,"utf-8"))
else:
self.send_response(403)
self.send_header('Content-Type', 'application/json')
self.end_headers()
else:
self.send_response(404)
self.send_header('Content-Type', 'application/json')
self.end_headers()
# ref : https://mafayyaz.wordpress.com/2013/02/08/writing-simple-http-server-in-python-with-rest-and-json/
return
try:
# Create a web server and define the handler to manage the
# incoming request
server = HTTPServer(('', PORT_NUMBER), myHandler) # HTTPServer : 위에 불러온 모듈, '서비스 할 IP' : 공란은 모든 IP에 대해서 서비스를 하겠다.
# PORT_NUMBER : 위에 있는 전역변수 8090, myHandler : BaseHTTPRequestHandler를 상속 받은 오버라이드가 들어갔다.(도움말 열어보면 얘가 들어가야 한다 나옴.)
# HTTPServer에 (괄호)안의 것들을 생성자로 넘겨주는거다. 그렇 server라는 객체를 생성한다.
print ('Started httpserver on port ' , PORT_NUMBER)
# Wait forever for incoming http requests
server.serve_forever() # server 라는 객체를 실행시킨다.
except:
print ('^C received, shutting down the web server')
print("서버 종료1!!")
server.socket.close() # 8090 포트를 닫아준다. (이거 안 닫으면 계속 점유하게된다.) 자원해제하는 절차.
다음으로 이어짐...
2020/04/20 - [개발자/Python] - Postman (포스트맨)으로 GET, POST URL 보내기
'개발자 > Python' 카테고리의 다른 글
GET 방식 URL Query(쿼리) 분리하기 (0) | 2020.04.21 |
---|---|
Postman (포스트맨)으로 GET, POST URL 보내기 (1) | 2020.04.20 |
Python (파이썬) 웹 크롤링 User Agent (0) | 2020.04.14 |
Python (파이썬) Beautiful Soup 사용법 (0) | 2020.04.13 |
Jupyter Lab Python (파이썬) 경로 이동시키기 (0) | 2020.04.13 |