본문 바로가기
  • Survival Plan
개발 이야기

RESTful API with Flask

by IT/머신러닝 엔지니어의 독서/경제/육아 공부 리치윈드 - windFlex 2020. 3. 10.

 

우선 Flask에서 Rest를 지원(?)하고 있는 flask_restful 을 설치한다. 

단순 PIP로 설치하면 된다. 

 

flask app를 구동하기 위한 기본 app를 만든다.

flask restful에서는 get/post 등을 기본으로 지원하고 있어서 아래와 같이 입력 후 

‘pythonn -m app.py’를 실행해 주면 기본적으로 localhost:5000에서 application이 구동되는 것을 확인할 수 있다. 



from flask import Flask
from flask_restful import Resource, Api
from flask_restful import reqparse

app = Flask(__name__)
api = Api(app)

class CheckData(Resource):
    def post(self):            
        return {'status': 'success'}
        
    def get(self):
        return {'status': 'success'}

api.add_resource(CheckData,'/check_data')

if __name__ == '__main__':
    app.run(debug=True)

 

 

위 처럼 작성후 실행하고, 웹브라우저로 접속하거나, curl로 접속을 테스트하면, 아래와 같은 결과를 확인할 수 있다. 

CheckData 클래스는 flask에서 restful api로 미리 작성해둔 Resource 클래스를 상속 받았기 때문에, Restful에 필요한 많은 처리들을 내장해 두었다. 

{
    "status": "success"
}

 

이제, POST로 입력을 받았을 때, 처리 행동을 상세화 해 본다. 

reqparse 에서는 request argument들을 자동 처리해 준다. 

reqparse.RequestParser().add_argumnet() 를 통하여 입력 파라미터를 설정할 수 있으며, 

마찬가지로 parse_args()를 통하여 입력을 파싱받을 수 있다. 

 (해당 함수에 SQL injection등의 비규칙적인 입력에대한 대응처리-Secure Coding가 되어 있는지..모르겠다.. 확인해보길..)

 

from flask import Flask
from flask_restful import Resource, Api
from flask_restful import reqparse

app = Flask(__name__)
api = Api(app)

class CheckData(Resource):
    def post(self):
        try:
            parser = reqparse.RequestParser()
            parser.add_argument('log_date', type=str)
            parser.add_argument('data')
            args = parser.parse_args()
            print('## args ##')
            
            _log_date = args['log_date']
            _data = args['data']
            print(args)
            
            return {'log_date': _log_date, 'data': _data, 'status': 'success'}
        except Exception as e:
            return {'error': str(e)}
        
        
    def get(self):
        return {'status': 'success'}

api.add_resource(CheckData,'/check_data')

if __name__ == '__main__':
    app.run(debug=True)

Exception 처리를 위하여, try / Except를 사용했음을 참고하기 바란다. 



TEST with CURL

위에서 우리는 POST Method에 해당하는 루틴을 작성하였다. 따라서, 웹브라우저에서 파라미터리를 입력하여 실행 시켜도 결과가 나오지 않는다. 이 경우 get() 함수를 실행하므로 여전히 status에 대한 내용만 출력될 것이다. 

 

따라서, 아래와 같이 curl 명령어를 이용한 data를 포함한 POST request를 발송하여 본다.

 

curl -X POST http://localhost:5000/check_data -H "Content-Type: application/json; charset=utf-8" -d ‘{“log_date”:”2019-10-14”, “data” :”dataFrame 1,2,3,4”}’

 

일반적으로 사용하는 작은따옴표(‘), 큰따옴표(“)에 따라서 CURL 명령어가 다르게 동작할 수 있으니, 이것에 주의 한다. (* 많은 경우에 큰따옴표와 작은따옴표가 동일한 역할을 하기 때문에 작은따옴표를 큰따옴표로 감싸거나, 그 반대인 경우가 있다. ) 따라서, 따옴표간 순서에 따라 결과가 달라지거나 에러가 발생할 수 있으므로 이에 주의가 필요하다. 

 

매번 curl로 테스트하기에 번거롭기 때문에 아래와 같이 파일로 만들어서 데이터를 보내는 방법을 추천한다. 

 

curl -X POST http://localhost:5000/check_data -H "Content-Type: application/json; charset=utf-8" -d "@data.json"

(* JSON 입력 테스트 등을 위해서 Insomnia 등의 도구가 사용되기도 한다.)

 

아래는 data.json의 내용이다.

data.json

{ 
    "log_date" : "2019-10-14",
    "data" : "dataFrame 1,2,3,4"
}

 

그러나, POST 의 컨텐츠는 일반적으로 이것보다 매우 복잡하다. 또한 JSON 형식의 입력은 대다수의 경우, array를 포함하고 있으므로, Data Array로 전환이 필요하다. 

 

Flask Restful API에서는 변수입력 시 list/array 타입을 별도로 지원한다.  이런 경우 해당 필드를 add_argument 할 때, action=’append’를 추가 해준다. 

parser.add_argument('data', action='append')

 

data2.json - data array

{ 
    "log_date" : "2019-10-14",
    "data" :  [ {"test": "data1"}, {"test":"23423"}, {"test":"abcdef"}]    
}

상기 json 파일과 같이 data 필드에 array 형 데이터터가 입력될 경우를 가정하여 본다.



from flask import Flask
from flask_restful import Resource, Api
from flask_restful import reqparse

app = Flask(__name__)
api = Api(app)

class CheckData(Resource):
    def post(self):
        try:
            parser = reqparse.RequestParser()
            parser.add_argument('log_date', type=str)
            parser.add_argument('data', action='append')
            args = parser.parse_args()
            print('## args ##')
            
            _log_date = args['log_date']
            _data = args['data']
            print(args)
            
            return {'log_date': _log_date, 'data': _data, 'status': 'success'}
        except Exception as e:
            return {'error': str(e)}
        
        
    def get(self):
        return {'status': 'success'}

api.add_resource(CheckData,'/check_data')

if __name__ == '__main__':
    app.run(debug=True)

상기 코드를 작성후 아래와 같이 실행해 본다. 

curl -X POST http://localhost:5000/check_data -H "Content-Type: application/json; charset=utf-8" -d "@data2.json"

 

이제 실제 데이터 샘플을 통하여, 정상 동작하는지 점검 하여 보자.

아래와 같이 data.json을 실제 데이터 샘플 array로 내용을 변경하였다. 

 

data3.json - data array of json string

{ 
    "log_date" : "2019-10-14",
    "data" :  [ 
        { "result_seq" : 532176403, 
            "log_dttm" : "2019-07-01 00:00:16",
            "rule_id" : 80021, 
            "rule_nm" : "80021 - DOS-ISMM TCP PSH Flooding", 
            "company_group_seq" : 50, 
            "event_nm" : "NaN", 
            "signature" : "TCP PSH Flooding", 
            "device_type" : "DD", 
            "vendor" : "Secui", 
            "count" : 14, 
            "filter_yn" : "N", 
            "dest_mac" : 50649, 
            "auto_yn" : "N", 
            "data_type" : "ISAPLog", 
            "device_severity" : "NaN", 
            "action" : "D", 
            "src_direction" : "OUT", 
            "dest_direction" : "OUT", 
            "conduct_section" : "RD"
            }, 
            { "result_seq" : 532176403,  
                "log_dttm" : "2019-07-01 00:00:16", 
                "rule_id" : 80021, 
                "rule_nm" : "80021 - DOS-ISMM TCP PSH Flooding", 
                "company_group_seq" : 50,
                "event_nm" : "NaN", 
                "signature" : "TCP PSH Flooding", 
                "device_type" : "DD", 
                "vendor" : "Secui", 
                "count" : 14, 
                "filter_yn" : "N", 
                "dest_mac" : 50649, 
                "auto_yn" : "N", 
                "data_type" : "ISAPLog", 
                "device_severity" : "NaN", 
                "action" : "D", 
                "src_direction" : "OUT", 
                "dest_direction" : "OUT", 
                "conduct_section" : "RD"
            },
                { "result_seq" : 532176403,  
                "log_dttm" : "2019-07-01 00:00:16", 
                "rule_id" : 80021, 
                "rule_nm" : "80021 - DOS-ISMM TCP PSH Flooding", 
                "company_group_seq" : 50,
                "event_nm" : "NaN", 
                "signature" : "TCP PSH Flooding", 
                "device_type" : "DD", 
                "vendor" : "Secui", 
                "count" : 14, 
                "filter_yn" : "N", 
                "dest_mac" : 50649, 
                "auto_yn" : "N", 
                "data_type" : "BlackIP", 
                "device_severity" : "NaN", 
                "action" : "D", 
                "src_direction" : "OUT", 
                "dest_direction" : "OUT", 
                "conduct_section" : "RD"
            },
        { "result_seq" : 532176404,  
            "log_dttm" : "2019-07-01 00:00:16", 
            "rule_id" : 80021, 
            "rule_nm" : "80021 - DOS-ISMM TCP PSH Flooding", 
            "company_group_seq" : 50,
            "event_nm" : "NaN", 
            "signature" : "TCP PSH Flooding", 
            "device_type" : "DD", 
            "vendor" : "Secui", 
            "count" : 14, 
            "filter_yn" : "N", 
            "dest_mac" : 50649, 
            "auto_yn" : "N", 
            "data_type" : "BlackIP", 
            "device_severity" : "NaN", 
            "action" : "D", 
            "src_direction" : "OUT", 
            "dest_direction" : "OUT", 
            "conduct_section" : "RD" 
        }
    ]
      
}

curl 명령어를 통하여 실행하였을 경우, 정상 동작하는 것을 확인 할 수 있다. 



[클라이언트 사이드]



[서버 사이드]

 

댓글0