python利用flask框架和tornado框架搭建api微服务——连接数据库返回带参求情结果(三)

目 录

1.python利用flask框架和tornado框架搭建api微服务——HelloWorld的实现(一)

2.python利用flask框架和tornado框架搭建api微服务——结合html网页实现get和post(二)

3.python利用flask框架和tornado框架搭建api微服务——连接数据库返回带参求情结果(三)

4.python利用flask框架和tornado框架搭建api微服务——python虚拟机启动API(四)

5.python利用flask框架和tornado框架搭建api微服务——Linux下查看某个端口对应的进程并kill进程的操作(关闭API服务进程)(五)

6.python利用flask框架和tornado框架搭建api微服务——完善API文档以及API调用(六)

背 景

  上篇我们写了个单机版的,但是实际操作中,肯定是用数据库的,我这里用的SQL SERVER,所以环境内要新增pip install pymssql来安装连接SQL SERVER的包,至于MYSQL的,请自己发挥,毕竟子曾经曰过,“举一隅不以三隅反,则不复也。”

实战之构建日志包

  和上一篇一样,首先是我们构建一个日志,方便收集api服务的各种操作日志,就命名为logUtil.py吧,这个日志包需要在logUtil.py文件的当前目录下新建一个log文件夹,不然会报错,我偷懒了,应该写个如果没该文件,就创建一下的,logUtil.py的具体代码如下:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# vim:fenc=utf-8


"""
log module, need a ./log directory
"""

import logging.config

config = {
    'version': 1,
    'formatters': {
        'simple': {
            'format': '%(asctime)s-%(thread)d-%(filename)s[line:%(lineno)d]%(levelname)s: %(message)s',
            'datefmt': '%Y-%m-%d %H:%M:%S',
            'encoding': 'utf-8',
            'filemode': 'a'
        }
        # other formater
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'simple'
        },
        'file': {
            'class': 'logging.handlers.TimedRotatingFileHandler',
            'filename': './log/' + __name__ + '.log',
            'level': 'DEBUG',
            'formatter': 'simple',
            'when': 'h',
            'interval': 1
        }
        # other handler
    },
    'loggers': {
        'StreamLogger': {
            'handlers': ['console'],
            'level': 'DEBUG',
        },
        'FileLogger': {
            # have console and file
            'handlers': ['console', 'file'],
            'level': 'DEBUG',
        }
        # other logger
    }
}

logging.config.dictConfig(config)
StreamLogger = logging.getLogger("StreamLogger")
FileLogger = logging.getLogger("FileLogger")

实战之项目文件夹分布

  每次我们下载一个软件解压后,都可以看到各个文件和文件夹井然有序,当然我们自己构建的项目也自然不能太low,不然对不起我们这张帅气的脸对吧,这个项目主要是获取iot设备的信息,总体文件夹命名为iot_location_desc_match,分布如下:

iot_location_desc_match/   # 总文件夹
  ├ iot_location_desc_match.py    # 应用启动程序
  ├ get_location_from_mssql.py    #从数据库获取
  ├ logUtil.py       # 日志包
  ├ log/             # log文件夹,存放操作日志
  │   ├ logUtil.log 
  │   └ main.logUtil.log    
  ├ templates/       #flask规定,不能改名,存放html文件
  │   ├ get.html
  │   └ post.html 
  ├ conf/            #自定义,用来存放配置文件,数据库的登录配置信息
      └ myconfig,ini #数据库的登录配置信息就写在这里

实战之处理代码的实现

  这里主要的实现代码有两部分,一部分是重数据库上获取数据的get_location_from_mssql.py,另一个是总体的API功能iot_location_desc_match.py其中大概思路就是API去调用数据库的数据,数据库返回json格式的数据给到API,再返回给用户,具体访问数据库的代码如下:

  • get_location_from_mssql.py
      代码之前先把数据库的配置信息写好,如文件夹分布所示,把数据库的配置信息写在当前目录的conf目录下的myconfig,ini文件内,内容如下:

[mssql_config]
host = 10.232.19.98
port = 1433
user = dw_user_reader
passwd = iloveyou123
db = db_iot

  然后开始我们的get_location_from_mssql.py代码如下:
  注意:代码内用到json.dumps方法是用来将python字符类型转成json字典类,注意如果返回的json内包含中文的话,一定要在里面加上参数ensure_ascii=False,不然会导致中文返回的是/u的unicode,影响心情

#! /usr/bin/env python
#encoding=utf-8
# -*- coding: utf-8 -*-
# vim:fenc=utf-8

import pymssql
import configparser
import os
import sys
import logUtil
import json

logger = logUtil.FileLogger   #导入日志包


def get_fdo_coon(dianzino):  #定义获取数据的函数,其中dianzino是用户给的参数
    if  len(dianzino) != 0 and  dianzino.replace(',','').isdigit(): #因为dianzino都是数字,所以简单的处理下判断是否为空以及是否是数据,传过来的是列表[021312319,0293123120932,0283219241],所以去掉逗号
        dianzino=dianzino.replace(',','\',\'') #传过来的是单纯的字符,在数据库里面用 in语法的话要拼接成 dianzi in ('021312319','0293123120932','0283219241')
        #print(dianzino)
        # 从该项目的conf folder下获取配置
        config = configparser.ConfigParser()
        cur_path = os.path.dirname(__file__)
        # print(cur_path)
        config_path = "./conf/myconfig.ini"  #利用配置文件的包读取数据库登录信息

        logger.info("config_path:%s" % config_path) #收集日志
        config.read(config_path)
        config_host = config['mssql_config']["host"]
        # config_port = int(config['mssql_config']["port"])
        config_user = config['mssql_config']["user"]
        config_passwd = config['mssql_config']["passwd"]
        config_db = config['mssql_config']["db"]

        # 连接fdo的sql server
        conn = pymssql.connect(config_host, config_user, config_passwd, config_db,charset = "GBK")
        cursor = conn.cursor()
        sql = """select DianziNo
            ,CityName
            ,PatrolArea
            ,PatrolMan
            ,BuildingName
            ,b.LocationKey
            ,LocationDesc
            ,DeviceStyleName 
            ,InstallStatus
            ,InstallStatusName
            ,IsPresent
            ,convert(varchar(10),PreRemoveDate,121) as PreRemoveDate
           FROM media.tbb_Building a
           JOIN media.tbb_MediaLocation b ON a.BuildingID=b.BuildingID
           JOIN assemble.tbb_MediaDevice c ON b.LocationID=c.LocationID
           join assemble.tbd_DeviceStyle ds on c.DeviceStyleID=ds.DeviceStyleID
           left join media.tbi_LocationPresentType lp on c.LocationID=lp.LocationID
           WHERE a.ValidStatus>=0 AND b.ValidStatus>=0 AND c.ValidStatus>=0
           and c.DianziNo in('%s')
           order by Dianzino asc"""
          #排一下序,虽然浪费点时间,但是确保每次获取返回的值不会乱序



        my_json_str = []
        sql = sql % (dianzino)
        logger.info("sql:%s" % sql)
        cursor.execute(sql) #执行sql语句
        result = cursor.fetchall() #用先把所有数据都拿出来
        conn.close()
        if len(result) != 0: #如果数据量不为空,则返回json串,注意ensure_ascii=False一定要写,不然有中文的话会返回乱码
          return  json.dumps({"Status Code": 200,\
                              "Data": [\
                                          {\
                                           "DianziNo": row[0],\
                                           "CityName": row[1],\
                                           "PatrolArea": row[2],\
                                           "PatrolMan": row[3],\
                                           "BuildingName": row[4],\
                                           "locationid": row[5],\
                                           "location_desc": row[6],\
                                           "DeviceStyleName": row[7],\
                                           "InstallStatus": row[8],\
                                           "InstallStatusName": row[9],\
                                           "IsPresent": row[10],\
                                           "PreRemoveDate": row[11]\
                                           } for row in result\
                                       ]\
                              },ensure_ascii=False,indent=4)
        else:
            return  json.dumps({"Status Code": 404,"Data":"sorry,this DianziNo not found any location!"},indent=4)#如果没数据,返回状态码404

    else:
        return  json.dumps({"Status Code": 400,"Data":"sorry,your parameter was empty or wrong,such as the DianziNo not digit,please try again!"},indent=4)
        #如果参数不对,返回状态码400

        #conn.close()
def main(argv): #定义带参的main函数
    dianzino = argv[1]
    jsondata=get_fdo_coon(dianzino) #调用get_fdo_coon来获取json数据
    print(jsondata)

if __name__ == '__main__':
    main(sys.argv) #因为用户要传入带参的dianzino,所以要定义一个带参的main函数
  • iot_location_desc_match.py
      接下来是实现功能的匹配脚本iot_location_desc_match.py,用来总体实现根据参数dianzino返回用户需要的信息,期间涉及的HTTP状态码HTTP Status Code)是用以表示网页服务器超文本传输协议响应状态的3位数字,反正4开头的都是不好的,具体可以看看HTTP基础知识了解下,当然我们也不可能去写所有的状态码返回,挑选几个典型的即可。代码具体代码如下:

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
from flask import Flask
from flask import render_template
from flask import request

from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop

import json

import logUtil  #导入自定义的日志包
import get_location_from_mssql.py #导入自定义的获取sql server的数据的包

app = Flask(__name__) #初始化Flask类的app对象

logger = logUtil.FileLogger #初始化日志的对象

# set route
@app.route('/')   #设置默认路由
def hello_world():
    return 'Hello, Guys,Welcome!!' #将之前的语句helloworld替换,返回个问候语句


# render_template search templates dir default
@app.route('/get.html')   #定义get.html路由
def get_html():
    return render_template('get.html') #返回get.html网页的响应

#这里的post界面就没什么用了,因为后面处理post是直接用python调用api,没要走网页的form形式
@app.route('/post.html')
def post_html():
    return render_template('post.html')


@app.route('/iot_location_desc_match', methods = ['GET', 'POST']) #定义iot_location_desc_match路由
def iot_location_desc_match(): #定义iot_location_desc_match对应的方法
    if request.method == "GET": #如果用get方法
        # get通过request.args.get("param_name","")形式获取参数值
        logger.info('a GET request') #收集日志
        for key,value in request.args.items():  #将get的参数和值收集
            logger.info('{key}:{value}'.format(key = key, value = value))
            try:
                print(type(value))
                myjs=json.dumps(value)
                pdata=json.loads(value)
                if(len(pdata['dianzino'])>2000): #传入的dianzino是一个list,限制下长度,不要无限长
                     get_result=json.dumps({"Status Code": 500,"Data":"sorry,your parameter amount more than 2000,please decrease some!"},indent=4) #超过两千跑出500的状态码
                else:
                    dianzinos=''
                    for str in pdata['dianzino']:
                         dianzinos=(dianzinos+str+',') #dianzino是个列表,把这个列表的数据串起来,做成如 '0232103,02489021,02401421,'的逗号隔开
                    dianzinos=dianzinos[:-1] #把最后的逗号去掉
                    get_result=get_location_from_mssql.get_fdo_coon(dianzinos) #调用上面自定义的get_location_from_mssql.get_fdo_coon获取数据库的数据,并且以json串返回
            except Exception:
                get_result=json.dumps({"Status Code": 500,"Data":"sorry,the api was wrong,please mail to [email protected]!"},indent=4) #捕获意想不到的异常,返回状态码500
        return get_result #返回结果
    elif request.method == "POST": #如果方法是POST
        # post通过request.form["param_name"]形式获取参数值 这个需要抛弃了,改用request.get_data()获取
        logger.info('a POST request') #收集日志
        value = request.get_data() #不能用request.form["param_name"]了,改成 request.get_data(),具体request的获取数据方法可以百度下官网,种类繁多,有详细介绍
        logger.info('{value}'.format(value = value)) #key已经没用了,我们只要客户传入的value
        try:
            #myjs=json.dumps(value)
            pdata=json.loads(value) #将用户传入的json串转化成python的字符类型
            if(len(pdata['dianzino'])>2000): #限制下客户传入的dianzino长度,别太长,太长了让用户多调几次
                 get_result=json.dumps({"Status Code": 500,"Data":"sorry,your parameter amount more than 2000,please decrease some!"},indent=4) #返回参数太长的状态码500
            else:
                dianzinos=''
                for str in pdata['dianzino']:
                     dianzinos=(dianzinos+str+',')  #dianzino是个列表,把这个列表的数据串起来,做成如 '0232103,02489021,02401421,'的逗号隔开
                dianzinos=dianzinos[:-1] #把最后的逗号去掉
                post_result=get_location_from_mssql.get_fdo_coon(dianzinos)  #调用上面自定义的get_location_from_mssql.get_fdo_coon获取数据库的数据,并且以json串返回
        except Exception:
             post_result=json.dumps({"Status Code": 500,"Data":"sorry,the api was wrong,please mail to [email protected]!"},indent=4) #捕获意想不到的异常,返回状态码500
        return post_result
    else:
        logger.warn('a request is neither a GET nor a POST')


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

    #app.run('0.0.0.0', 5000, debug=True)
    port=7000 #设置程序启动的端口
    http_server = HTTPServer(WSGIContainer(app)) #将api的初始化对应委托给tornado的HTTPServer
    http_server.listen(port) #监听端口
    logger.info('Listening on {}'.format(port))
    IOLoop.instance().start() #启动微服务

实战之启动API

  这里主要讲的是用python调用api,后面用户调用也是用python直接调用api的,不再使用章节二里面说到的浏览器了访问了,如果对网页感兴趣,可以在篇二上修改,这里就直接把api启动起来,先进入总目录下iot_location_desc_match,然后shell启动脚本如下,返回
2020-04-07 19:36:50-140333704324928-iot_location_desc_match.py[line:100]INFO: Listening on 7000就正常了。

 python iot_location_desc_match.py
2020-04-07 19:36:50-140333704324928-iot_location_desc_match.py[line:100]INFO: Listening on 7000

你可能感兴趣的:(Python,微服务API)