首页 > 编程语言 >使用wxpython开发跨平台桌面应用,对WebAPI调用接口的封装

使用wxpython开发跨平台桌面应用,对WebAPI调用接口的封装

时间:2024-11-13 09:58:03浏览次数:1  
标签:WebAPI url self json 接口 跨平台 wxpython result data

我在前面介绍的系统界面功能,包括菜单工具栏、业务表的数据,开始的时候,都是基于模拟的数据进行测试,数据采用JSON格式处理,通过辅助类的方式模拟实现数据的加载及处理,这在开发初期是一个比较好的测试方式,不过实际业务的数据肯定是来自后端,包括本地数据库,SqlServer、Mysql、Oracle、Sqlite、PostgreSQL等,或者后端的WebAPI接口获取,本篇随笔逐步介绍如何对后端的数据接口进行建模以及提供本地WebAPI代理接口类的处理过程。

1、定义Web API接口类并测试API调用基类

我在随笔《使用wxpython开发跨平台桌面应用,动态工具的创建处理》中介绍了关于工具栏和菜单栏的数据类,以及模拟方式获得数据进行展示,如下界面所示。

如菜单数据的类信息,如下所示。

class MenuInfo:
    id: str  # 菜单ID
    pid: str  # 父菜单ID
    label: str  # 菜单名称
    icon: str = None  # 菜单图标
    path: str = None  # 菜单路径,用来定位视图
    tips: str = None  # 菜单提示
    children: list["MenuInfo"] = None

这些数据和后端数据接口的定义一致,那么就很容易切换到动态的接口上。

在系统开发的初期,我们可以先试用模拟方式获得数据集合,如通过一个工具来来获得数据,如下所示。

为了更好的符合实际的业务需求,我们往往需要根据服务端的接口定义来定义调用Web API接口的信息。

我们为了全部采用Python语言进行开发,包括后端的内容,采用 基于SqlAlchemy+Pydantic+FastApi 的后端框架

该后端接口采用统一的接口协议,标准协议如下所示。

{
  "success": false,
  "result":  T ,
  "targetUrl": "string",
  "UnAuthorizedRequest": false,
  "errorInfo": {
    "code": 0,
    "message": "string",
    "details": "string"
  }
}

其中的result是我们的数据返回,有可能是基本类型(如字符串、数值、布尔型等),也有可能是类集合,对象信息,字典信息等等。

如果是分页查询返回结果集合,其结果如下所示。

展开单条记录明细如下所示。

如果我们基于Pydantic模型定义,我们的Python对象类定义代码如下所示

from pydantic import  BaseModel
from typing import Generic, Type, TypeVar, Optional
T = TypeVar("T")

# 自定义返回模型-统一返回结果
class AjaxResponse(BaseModel, Generic[T]):
    success: bool = False
    result: Optional[T] = None
    targetUrl: Optional[str] = None
    UnAuthorizedRequest: Optional[bool] = False
    errorInfo: Optional[ErrorInfo] = None

也就是结合泛型的方式,这样定义可以很好的抽象不同的业务类接口到基类BaseApi中,这样增删改查等处理的接口都可以抽象到BaseApi里面了。

权限模块我们涉及到的用户管理、机构管理、角色管理、菜单管理、功能管理、操作日志、登录日志等业务类,那么这些类继承BaseApi,就会具有相关的接口了,如下所示继承关系。

 

2、对异步调用进行测试和接口封装

为了理解客户端Api类的处理,我们先来介绍一些简单的pydantic 入门处理,如下我们先定义一些实体类用来承载数据信息,如下所示。

from typing import List, TypeVar, Optional, Generic, Dict, Any
from datetime import datetime
from pydantic import BaseModel, Field
T = TypeVar("T")

class AjaxResult(BaseModel, Generic[T]):
    """测试统一接口返回格式"""
    success: bool = True
    message: Optional[str] = None
    result: Optional[T] = None

class PagedResult(BaseModel, Generic[T]):
    """分页查询结果"""
    total: int
    items: List[T]

class Customer(BaseModel):
    """客户信息类"""
    name: str
    age: int

一般业务的结果是对应的记录列表,或者实体类对象格式,我们先来测试解析下它们的JSON数据,有助于我们理解。

# 对返回结果数据格式的处理
json_data = """{
    "total": 100,
    "items": [
        {"name": "Alice", "age": 25},
        {"name": "Bob", "age": 30},
        {"name": "Charlie", "age": 35}
    ]
}"""
paged_result = PagedResult.model_validate_json(json_data)
print(paged_result.total)
print(paged_result.items)

以上正常解析到数据,输出结果如下所示。

100
[{'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 30}, {'name': 'Charlie', 'age': 35}]
True

如果我们换为统一返回的结果进行测试,如下所示。

json_data = """{
    "success": true,
    "message": "success",
    "result": {
        "total": 100,
        "items": [
            {"name": "Alice", "age": 25},
            {"name": "Bob", "age": 30},
            {"name": "Charlie", "age": 35}
        ]
    }
}"""

ajax_result = AjaxResult[PagedResult].model_validate_json(json_data)
print(ajax_result.success)
print(ajax_result.message)
print(ajax_result.result.total)
print(ajax_result.result.items)

同样的可以获得正常的输出。

True
success
100
[{'name': 'Alice', 'age': 25}, {'name': 'Bob', 'age': 30}, {'name': 'Charlie', 'age': 35}]

我们通过 model_validate_json 接口可以转换字符串内容为对应的业务类对象,而通过 model_validate 函数可以转换JSON格式为业务类对象。

而对于接口的继承处理,我们采用了泛型的处理,可以极大的减少基类代码的编写,如下基类定义和子类定义,就可以简单很多,所有逻辑放在基类处理即可。

class BaseApi(Generic[T]):
    def test(self) -> AjaxResult[Dict[str, Any]]:
        json_data = """{
            "success": true,
            "message": "success",
            "result": {"name": "Alice", "age": 25}
        }"""
        result = AjaxResult[Dict[str, Any]].model_validate_json(json_data)
        return result

    def get(self, id: int) -> AjaxResult[T]:
        json_data = """{
            "success": true,
            "message": "success",
            "result": {"name": "Alice", "age": 25}
        }"""
        result = AjaxResult[T].model_validate_json(json_data)
        return result

    def getlist(self) -> AjaxResult[List[T]]:
        json_data = """{
            "success": true,
            "message": "success",
            "result": [
                {"name": "Alice", "age": 25},
                {"name": "Bob", "age": 30},
                {"name": "Charlie", "age": 35}
            ]
        }"""
        result = AjaxResult[List[T]].model_validate_json(json_data)
        return result


class UserApi(BaseApi[Customer]):
    pass

user_api = UserApi()
result = user_api.getlist()
print(result.success)
print(result.message)
print(result.result)

result = user_api.get(1)
print(result.success)
print(result.message)
print(result.result)

result = user_api.test()
print(result.success)
print(result.message)
print(result.result)

可以看到,子类只需要明确好继承关系即可,不需要编写任何多余的代码,但是又有了具体的接口处理。

 

3、实际HTTTP请求的封装处理

一般对于服务端接口的处理,我们可能需要引入 aiohttp 来处理请求,并结合Pydantic的模型处理,是的数据能够正常的转换,和上面的处理方式一样。

首先我们需要定义一个通用HTTP请求的类来处理常规的HTTP接口数据的返回,如下所示。

class ApiClient:
    _access_token = None  # 类变量,用于全局共享 access_token

    @classmethod
    def set_access_token(cls, token):
        """设置全局 access_token"""
        cls._access_token = token

    @classmethod
    def get_access_token(cls):
        """获取全局 access_token"""
        return cls._access_token

    def _get_headers(self):
        headers = {}
        if self.get_access_token():
            headers["Authorization"] = f"Bearer {self.get_access_token()}"
        return headers

    async def get(self, url, params=None):
        async with aiohttp.ClientSession() as session:
            async with session.get(
                url, headers=self._get_headers(), params=params
            ) as response:
                return await self._handle_response(response)

    async def post(self, url, json_data=None):
        async with aiohttp.ClientSession() as session:
            async with session.post(
                url, headers=self._get_headers(), json=json_data
            ) as response:
                return await self._handle_response(response)

    async def put(self, url, json_data=None):
        async with aiohttp.ClientSession() as session:
            async with session.put(
                url, headers=self._get_headers(), json=json_data
            ) as response:
                return await self._handle_response(response)

    async def delete(self, url, params=None):
        async with aiohttp.ClientSession() as session:
            async with session.delete(
                url, headers=self._get_headers(), params=params
            ) as response:
                return await self._handle_response(response)

    async def _handle_response(self, response):
        if response.status == 200:
            return await response.json()
        else:
            response.raise_for_status()

这些我来基于通用ApiClient的辅助类,对业务接口的调用进行一个简单基类的封装,命名为BaseApi,接受泛型类型定义,如下所示。

class BaseApi(Generic[T]):
    base_url = "http://jsonplaceholder.typicode.com/"
    client: ApiClient = ApiClient()

    async def getall(self, endpoint, params=None) -> List[T]:
        url = f"{self.base_url}{endpoint}"
        json_data = await self.client.get(url, params=params)
        # print(json_data)
        return list[T](json_data)

    async def get(self, endpoint, id) -> T:
        url = f"{self.base_url}{endpoint}/{id}"
        json_data = await self.client.get(url)
        # return parse_obj_as(T,json_data)
        adapter = TypeAdapter(T)
        return adapter.validate_python(json_data)

    async def create(self, endpoint, data) -> bool:
        url = f"{self.base_url}{endpoint}"
        await self.client.post(url, data)

        return True

    async def update(self, endpoint, id, data) -> T:
        url = f"{self.base_url}{endpoint}/{id}"
        json_data = await self.client.put(url, data)

        adapter = TypeAdapter(T)
        return adapter.validate_python(json_data)

    async def delete(self, endpoint, id) -> bool:
        url = f"{self.base_url}{endpoint}/{id}"
        json_data = await self.client.delete(url)
        # print(json_data)
        return True

我这里使用了一个 测试API接口很好的网站:https://jsonplaceholder.typicode.com/,它提供了很多不同业务对象的接口信息,如下所示。

统一提供GET/POST/PUT/DELETE等常规Restful动作的处理

如我们获取列表数据的接口如下,返回对应的JSON集合。

 通过对应的业务对象不同的动作处理,我们可以测试各种接口。 

 注意,我们上面的接口都是采用了async/awati的对应异步标识来处理异步的HTTP接口请求。

上面我们定义了BaseApi,具有常规的getall/get/create/update/delete的接口,实际开发的时候,这些会根据后端接口请求扩展更多基类接口。

基于基类BaseApi定义,我们创建其子类PostApi,用来获得具体的对象定义接口。

class PostApi(BaseApi[post]):
    # 该业务接口类,具有基类所有的接口

    # 并增加一个自定义的接口
    async def test(self) -> Db:
        url = "http://my-json-server.typicode.com/typicode/demo/db"
        json_data = await self.client.get(url)
        # print(json_data)
        return Db.model_validate(json_data)

这里PostApi 具有基类所有的接口:getall/get/create/update/delete的接口, 并可以根据实际情况增加自定义接口,如test 接口定义。

测试代码如下所示。

async def main():
    post_api = PostApi()
result = await post_api.getall("posts") print(len(result)) result = await post_api.get("posts", 1) print(result) result = await post_api.create( "posts", {"title": "test", "body": "test body", "userId": 1} ) print(result) result = await post_api.update( "posts", 1, {"title": "test2", "body": "test body2", "userId": 1, "id": 1} ) print(result) result = await post_api.delete("posts", 1) print(result) result = await post_api.test() print(result) if __name__ == "__main__": asyncio.run(main())

运行例子,输出如下结果。

 

标签:WebAPI,url,self,json,接口,跨平台,wxpython,result,data
From: https://www.cnblogs.com/wuhuacong/p/18543247

相关文章

  • 使用wxpython开发跨平台桌面应用,基类列表窗体的抽象封装处理
    在开发一套系统框架的时候,除了关注实现系统的功能实现外,我们对于系统的各个方面都是应该精益求精,以最少的编码做最好的事情,在开发的各个层次上,包括前端后端,界面处理、后端处理、常用辅助类、控件封装等等方面,我们都可以通过抽象、重用等方式,实现代码的优化、简化,以期达到快速开发......
  • 使用wxpython开发跨平台桌面应用,动态工具的创建处理
    在我们开发系统的时候,往往需要一个很容易理解功能的工具栏,工具栏是一个系统的快速入口,美观易用的工具栏是可以给系统程序增色不少的,本篇随笔介绍在使用wxpython开发跨平台桌面应用,工具栏的动态展现效果,以及多级工具栏显示等的创建处理过程。1、wxpython工具栏介绍在wxPython中......
  • 通过C++跨平台的预编译宏来区分不同的操作系统:Win32/Win64/Unix/Linux/MacOS
    因为C++具有跨平台的特性,所以有些需求一套代码就多端使用,比如我最近在学习的OpenGLES。但是,不同平台还是具有一定差异性,所以我们首先得判断出是什么平台?比如iOS系统和Android系统。那么如何判断呢?我们接着往下看!要检查C或C代码中主机的操作系统,我们需要检查编......
  • ubuntu系统 运行 .net core8 webapi
    注册微软密钥和软件仓库,执行以下命令:wgethttps://packages.microsoft.com/config/ubuntu/22.04/packages-microsoft-prod.deb-Opackages-microsoft-prod.debsudodpkg-ipackages-microsoft-prod.deb仅安装.NET运行时:sudoapt-getupdate;\sudoapt-getinstall-yapt-trans......
  • WebAPI 初学 Visual Studio 2022,.NET 6.0(EF 代码迁移)
    按照一步一步来,您将能够创建api选择C#、Windows和WebApi 创建API后,单击绿色按钮运行应用程序,现在我们可以看到Demo项目正在运行。尽管所有结构都是自动创建的,以运行API。此版本已自动配置Swagger。这是演示API。VisualStudio会自动添加所需的库。现......
  • .NET 8 高性能跨平台图像处理库 ImageSharp
    阅读目录前言项目介绍项目使用常用方法常用滤镜项目地址总结最后前言传统的System.Drawing库功能丰富,但存在平台限制,不适用于跨平台开发。.NET8的发布,ImageSharp成为了一个更好的选择。ImageSharp是一个完全开源、高性能且跨平台的图像处理库,专为.NET设计......
  • .NET 8 高性能跨平台图像处理库 ImageSharp
    合集-.NET开源项目(27) 1.推荐一款界面优雅、功能强大的.NET+Vue权限管理系统08-052..NET开源权限认证项目MiniAuth上线08-063..NET与LayUI实现高效敏捷开发框架08-084..NET8+Blazor多租户、模块化、DDD框架、开箱即用08-095.推荐一个优秀的.NETMAUI组件......
  • 如何使用nssm将asp.net core/.net6/.net8的webapi项目、mvc项目、控制台项目等注册为w
    nssm工具可以将asp.netFramework、asp.netcore、net6、.net8、.net10及后续本的的webapi项目、mvc项目、控制台项目、winform项、WPF项目等注册为windows服务。不仅限于上面这些,nssm可以将所有windows可执行文件注册为windows服务。下面,使用nssm将asp.net8的webapi项目注册为w......
  • 跨平台使用高德地图服务
    高德地图-初始化目标:注册高德地图开放平台并初始化地图步骤:准备工作准备-地图JSAPI2.0|高德地图APIVue中使用JSAPI结合Vue使用-基础-进阶教程-地图JSAPI2.0|高德地图API参考文档Web开发-JSAPI文档流程:注册&认证个人开发者===>创建web应用====>得到key......
  • 使用wxpython开发跨平台桌面应用,基类对话框窗体的封装处理
    在开发桌面界面的时候,往往都需要对一些通用的窗体进行一些抽象封装处理,以便统一界面效果,以及继承一些通用的处理过程,减少重复编码。本篇随笔介绍使用wxpython开发跨平台桌面应用,基类对话框窗体的封装处理,介绍基于wx.lib.sized_controls.SizedDialog对话框类的基类封装,以便简化子......