当前位置:网站首页>【博主推荐】asp.net WebService 后台数据API JSON(附源码)
【博主推荐】asp.net WebService 后台数据API JSON(附源码)
2022-07-06 09:13:00 【xcLeigh】
文章列表
【博主推荐】asp.net WebService 后台数据API(附源码)
Web service是一个基于可编程的web的应用程序,用于开发分布式的互操作的应用程序,也是一种web服务
WebService的特性有以下几点:
1.使用XML(标准通用标记语言)来作为数据交互的格式。
2.跨平台性,因为使用XML所以只要本地应用可以连接网络解析XML就可以实现数据交换,比如安卓、IOS、WindowsPhone等都可以实现对Web service的数据交互。
3.基于HTTP协议,直接跨越防火墙,通用型强;界面中贴的代码只是主要实现流程,具体的实现方法,外部方法见项目资源源码下载
代码实现
- 1.给前端数据提供增删改查API;
- 2.内置使用mysql、oracle数据库,多配置,可以在配置文件自动切换需要的数据库;
- 3.api接口过滤器机制,可以过滤自己想要过滤的请求;
- 4.可以提供json和xml的数据格式;
- 5.代码下载可以直接运用;
- 6.实现不同的数据库,执行不同的sql语句;
- 7.内含websocket服务端,接收处理客户端websocket数据;
- 8.附带websocket客户端源码;
- 9.带文本日志文件输出;
webservice简单创建过程
先根据这篇教程了解怎么简单创建一个web服务,以便更好深入了解
C#中WebService的创建和调用
1.配置全局应用程序类Global.asax
Application_Start 和 Application_End
第一次访问站点时,创建HttpApplication对象,此时会触发Application_Start,并创建HttpApplication实例池,应用接请求就从池中获取实例,进行处理。在所有的HttpApplication实例闲置达到超时时间,触发应用程序池回收,或者重启站点时就会触发Application_End事件,应用程序池的闲置超时时间可以在iis设置,站点下bin目录下的文件发生改变,webconfig配置改变等导致站点重启的事件都会触发Application_End。
Session_Start 和 Session_End
单个用户访问Web应用时,启动会话,服务器为该用户创建一个独立的Session对象,并触发Session_Start事件,此时Session处于可用状态。用户在该会话建立后可以发起若干次请求,当服务器一段时间内未收到用户请求,达到会话超时时间时,触发Session_End事件,服务器释放为当前用户保存Session的内存。也可以在应用程序中调用Session.Abandon()可以手动取消Session,清空服务器保存的Session,直到再次调用Session时,又会触发Session_Start,但是SessionID不会变化。所有会触发Application_End的事件都会在此之前触发Session_Start。可以在Web.Config中添加设置Session过期时间(timeout单位为分钟)。
Application_BeginRequest 和 Application_EndRequest
在用户会话启动后,每次发起的请求都会触发Application_BeginRequest事件,并在请求完成时触发Application_EndRequest事件。
Application_BeginRequest 里配置过滤
#region 过滤客户端xss恶意脚本提交
if (Request.Cookies != null)
{
if (XSSFilter.CookieData())
{
Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Response.Write(JsonUtil.getText1Json("您提交的Cookie数据有恶意字符!"));
Response.End();
}
}
if (Request.UrlReferrer != null)
{
if (XSSFilter.referer())
{
Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Response.Write(JsonUtil.getText1Json("您提交的Referrer数据有恶意字符!"));
Response.End();
}
}
if (Request.RequestType.ToUpper() == "POST")
{
if (XSSFilter.PostData())
{
Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Response.Write(JsonUtil.getText1Json("您提交的Post数据有恶意字符!"));
Response.End();
}
}
if (Request.RequestType.ToUpper() == "GET")
{
if (XSSFilter.GetData())
{
Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Response.Write(JsonUtil.getText1Json("您提交的Get数据有恶意字符!"));
Response.End();
}
}
#endregion
#region 过滤参数
//遍历Post参数,隐藏域除外
foreach (string i in this.Request.Form)
{
if (i == "__VIEWSTATE") continue;
this.goErr(this.Request.Form[i].ToString());
}
//遍历Get参数。
foreach (string i in this.Request.QueryString)
{
this.goErr(this.Request.QueryString[i].ToString());
}
#endregion
3.配置POST,GET请求返回JOSN
Web.config
<system.web>
<webServices>
<protocols>
<add name="HttpPost" />
<add name="HttpGet" />
<add name="HttpSoap" />
<add name="Documentation" />
</protocols>
</webServices>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules>
<remove name="ApplicationInsightsWebTracking" />
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
</modules>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Methods" value="OPTIONS,POST,GET" />
<add name="Access-Control-Allow-Headers" value="x-requested-with" />
<add name="Access-Control-Allow-Origin" value="*" />
</customHeaders>
</httpProtocol>
</system.webServer>
2.新建asmx WEB服务文件
建好后的界面配置
dwsh.asmx代码
using DataServiceBLL;
using DataServiceUtil;
using System;
using System.Web.Script.Services;
using System.Web.Services;
namespace DataServiceAPI.dsa.api.v1.data
{
/// <summary>
/// dwsh 的摘要说明
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。
// [System.Web.Script.Services.ScriptService]
public class dwsh : System.Web.Services.WebService
{
#region 实例化
public OperDataBLL operDataBLL = new OperDataBLL();
#endregion
//api接口见下面效果代码
}
}
3.效果展示
3.1新增api
代码
#region 新增
[WebMethod(Description = "数据新增")]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public void add(string token, string name, string codedata, string detail)
{
if (token != ConfigurationHelperUtil.DSA_TOKEN)
{
Context.Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Context.Response.Write(JsonUtil.getError205Json());//可json,可文本,可xml
Context.Response.End();
}
else
{
string result = "操作成功!";
int code = 200;
try
{
string sql = "";
string db = ConfigurationHelperUtil.DSA_DB;
int num = 0;
if (db.ToUpper() == "MYSQL")
{
sql = "insert into SMS_TEST(NAME, CODE, DETAIL, INSERTTIME)";
sql += " values('" + name + "', '" + codedata + "', '" + detail + "', now())";
num = operDataBLL.OperData(sql);
}
else if (db.ToUpper() == "ORACLE")
{
sql = "insert into SMS_TEST(id,NAME,CODE,DETAIL,INSERTTIME)";
sql += " values(seq_dswh_id.nextval, '" + name + "', '" + codedata + "', '" + detail + "', sysdate)";
num = operDataBLL.OperOracleData(sql);
}
OperLogUtil.WriteFileLog("新增操作sql:" + sql, ConfigurationHelperUtil.DSA_LOG_TYPE_INFO);
if (num > 0)
{
result = "操作成功";
}
else
{
result = "未更新数据";
code = 201;
}
}
catch (Exception)
{
result = "操作异常!";
code = 202;
}
string resultJson = JsonUtil.getTextJson(code, result, 1, "[]");
Context.Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Context.Response.Write(resultJson);//可json,可文本,可xml
Context.Response.End();
}
}
#endregion
3.2修改api
代码
#region 修改
[WebMethod(Description = "数据修改")]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public void update(string token,string id, string name, string codedata, string detail)
{
if (token != ConfigurationHelperUtil.DSA_TOKEN)
{
Context.Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Context.Response.Write(JsonUtil.getError205Json());//可json,可文本,可xml
Context.Response.End();
}
else
{
string result = "操作成功!";
int code = 200;
try
{
string sql = "";
string db = ConfigurationHelperUtil.DSA_DB;
int num = 0;
if (db.ToUpper() == "MYSQL")
{
sql = "update SMS_TEST set NAME='" + name + "', CODE= '" + codedata + "', DETAIL='" + detail + "' where id="+id;
num = operDataBLL.OperData(sql);
}
else if (db.ToUpper() == "ORACLE")
{
sql = "update SMS_TEST set NAME= '" + name + "',CODE= '" + codedata + "',DETAIL= '" + detail + "' where id="+id;
num = operDataBLL.OperOracleData(sql);
}
OperLogUtil.WriteFileLog("修改操作sql:" + sql, ConfigurationHelperUtil.DSA_LOG_TYPE_INFO);
if (num > 0)
{
result = "操作成功";
}
else
{
result = "未更新数据";
code = 201;
}
}
catch (Exception)
{
result = "操作异常!";
code = 202;
}
string resultJson = JsonUtil.getTextJson(code, result, 1, "[]");
Context.Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Context.Response.Write(resultJson);//可json,可文本,可xml
Context.Response.End();
}
}
#endregion
3.3列表api
代码
#region 查询列表
[WebMethod(Description = "列表")]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
//[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public void getlist(string token, string name)
{
if (token != ConfigurationHelperUtil.DSA_TOKEN)
{
Context.Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Context.Response.Write(JsonUtil.getError205Json());//可json,可文本,可xml
Context.Response.End();
}
else
{
string result = "";
try
{
if (name == "" || name == null || name == "null")
{
name = "";
}
else
{
name = " and name like '%" + name + "%'";
}
string sql = "select * from SMS_TEST t where 1=1" + name;
string db = ConfigurationHelperUtil.DSA_DB;
OperLogUtil.WriteFileLog("查询操作sql:" + sql, ConfigurationHelperUtil.DSA_LOG_TYPE_INFO);
if (db.ToUpper() == "MYSQL")
{
result = operDataBLL.getData(sql);
}
else if (db.ToUpper() == "ORACLE")
{
result = operDataBLL.getOracleData(sql);
}
}
catch (Exception)
{
result = JsonUtil.getError201Json();
}
Context.Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Context.Response.Write(result);//可json,可文本,可xml
Context.Response.End();
}
}
#endregion
3.4删除api
代码
#region 删除
[WebMethod(Description = "数据删除")]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public void del(string token, string id)
{
if (token != ConfigurationHelperUtil.DSA_TOKEN)
{
Context.Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Context.Response.Write(JsonUtil.getError205Json());//可json,可文本,可xml
Context.Response.End();
}
else
{
string result = "操作成功!";
int code = 200;
try
{
string sql = "";
string db = ConfigurationHelperUtil.DSA_DB;
int num = 0;
if (db.ToUpper() == "MYSQL")
{
sql = "delete SMS_TESTdelete from SMS_TEST where id="+id;
num = operDataBLL.OperData(sql);
}
else if (db.ToUpper() == "ORACLE")
{
sql = "delete SMS_TEST where id="+id;
num = operDataBLL.OperOracleData(sql);
}
OperLogUtil.WriteFileLog("删除操作sql:" + sql, ConfigurationHelperUtil.DSA_LOG_TYPE_INFO);
if (num > 0)
{
result = "操作成功";
}
else
{
result = "未更新数据";
code = 201;
}
}
catch (Exception)
{
result = "操作异常!";
code = 202;
}
string resultJson = JsonUtil.getTextJson(code, result, 1, "[]");
Context.Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Context.Response.Write(resultJson);//可json,可文本,可xml
Context.Response.End();
}
}
#endregion
3.5分页列表api
代码
#region 查询列表分页
[WebMethod(Description = "分页列表")]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
//[ScriptMethod(ResponseFormat = ResponseFormat.Xml)]
public void getfyList(string token, string limit, string start, string name)
{
if (token != ConfigurationHelperUtil.DSA_TOKEN)
{
Context.Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Context.Response.Write(JsonUtil.getError205Json());//可json,可文本,可xml
Context.Response.End();
}
else
{
string result = "";
if (!StrUtil.isNum(limit) || !StrUtil.isNum(start))//验证是否合格参数
{
result = JsonUtil.getError203Json();
}
else
{
try
{
if (name == "" || name == null || name == "null")
{
name = "";
}
else
{
name = " and name like '%" + name + "%'";
}
string db = ConfigurationHelperUtil.DSA_DB;
if (db.ToUpper() == "MYSQL")
{
//limit a,b a为开始行数 b为几行
int ks = (int.Parse(start) - 1) * int.Parse(limit);
string sql = "SELECT * FROM SMS_TEST WHERE 1=1" + name + " ORDER BY INSERTTIME desc LIMIT " + ks + ", " + limit;
result = operDataBLL.getFyData(sql, "SMS_TEST");
OperLogUtil.WriteFileLog("分页列表操作sql:" + sql, ConfigurationHelperUtil.DSA_LOG_TYPE_INFO);
}
else if (db.ToUpper() == "ORACLE")
{
string sql = "SELECT * FROM SMS_TEST WHERE 1=1" + name;
//false desc true asc
result = operDataBLL.getFyOracleData(sql, "INSERTTIME", false, int.Parse(limit), int.Parse(start));
OperLogUtil.WriteFileLog("分页列表操作sql:" + sql, ConfigurationHelperUtil.DSA_LOG_TYPE_INFO);
}
}
catch (Exception)
{
result = JsonUtil.getError201Json();
}
}
Context.Response.Charset = ConfigurationHelperUtil.DSA_Encode;
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding(ConfigurationHelperUtil.DSA_Encode);
Context.Response.Write(result);//可json,可文本,可xml
Context.Response.End();
}
}
#endregion
4.内置websocket服务端
- 如要启动websocket,取消这几行代码注释即可
protected void Application_Start(object sender, EventArgs e)
{
//启动webScoket
//Thread thread2 = new Thread(new ThreadStart(WebScoket.startWebScoket));//创建线程
//thread2.Start(); //启动线程
//WebScoket.startWebScoket();//用于CMS
}
- websocket代码
using DataServiceBLL;
using DataServiceUtil;
using Newtonsoft.Json.Linq;
using StriveEngine;
using StriveEngine.Core;
using StriveEngine.Tcp.Server;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
namespace DataServiceAPI.App_Code
{
public class WebScoket
{
private static ITcpServerEngine tcpServerEngine;
private static bool hasTcpServerEngineInitialized;
public static OperDataBLL operDataBLL = new OperDataBLL();
public static void startWebScoket()
{
OperLogUtil.WriteFileLog("启动WebScoket", ConfigurationHelperUtil.DSA_LOG_TYPE_WEBSCOKET);
try
{
if (tcpServerEngine == null)
{
tcpServerEngine = NetworkEngineFactory.CreateTextTcpServerEngine(int.Parse(ConfigurationHelperUtil.DSA_WebScoket), new DefaultTextContractHelper("\0"));//DefaultTextContractHelper是StriveEngine内置的ITextContractHelper实现。使用UTF-8对EndToken进行编码。
}
if (hasTcpServerEngineInitialized)
{
tcpServerEngine.ChangeListenerState(true);
}
else
{
InitializeTcpServerEngine();
}
}
catch (Exception ee)
{
OperLogUtil.WriteFileLog(ee.Message, ConfigurationHelperUtil.DSA_LOG_TYPE_ERROR);
}
}
public static void InitializeTcpServerEngine()
{
tcpServerEngine.ClientCountChanged += new CbDelegate<int>(tcpServerEngine_ClientCountChanged);
tcpServerEngine.ClientConnected += new CbDelegate<System.Net.IPEndPoint>(tcpServerEngine_ClientConnected);
tcpServerEngine.ClientDisconnected += new CbDelegate<System.Net.IPEndPoint>(tcpServerEngine_ClientDisconnected);
tcpServerEngine.MessageReceived += new CbDelegate<IPEndPoint, byte[]>(tcpServerEngine_MessageReceived);
tcpServerEngine.Initialize();
hasTcpServerEngineInitialized = true;
}
public static void tcpServerEngine_ClientCountChanged(int count)
{
OperLogUtil.WriteFileLog("在线数量: " + count, ConfigurationHelperUtil.DSA_LOG_TYPE_WEBSCOKET);
}
public static void tcpServerEngine_ClientConnected(System.Net.IPEndPoint ipe)
{
string msg = string.Format("{0} 上线", ipe);
OperLogUtil.WriteFileLog(msg, ConfigurationHelperUtil.DSA_LOG_TYPE_WEBSCOKET);
}
public static void tcpServerEngine_ClientDisconnected(System.Net.IPEndPoint ipe)
{
string msg = string.Format("{0} 下线", ipe);
OperLogUtil.WriteFileLog(msg, ConfigurationHelperUtil.DSA_LOG_TYPE_WEBSCOKET);
}
public static void tcpServerEngine_MessageReceived(IPEndPoint client, byte[] bMsg)
{
string msg = System.Text.Encoding.UTF8.GetString(bMsg); //消息使用UTF-8编码
msg = msg.Substring(0, msg.Length - 1); //将结束标记"\0"剔除
OperLogUtil.WriteFileLog("收到:" + client.Address + ":" + msg, ConfigurationHelperUtil.DSA_LOG_TYPE_WEBSCOKET);
#region 处理接收到的数据
//此处处理接收到的数据
#endregion
}
#region 根据客户端推送单条信息
public static void sendInfo(IPEndPoint client, string msg)
{
try
{
msg = msg + "\0";// "\0" 表示一个消息的结尾
byte[] bMsg = System.Text.Encoding.UTF8.GetBytes(msg);//消息使用UTF-8编码
tcpServerEngine.SendMessageToClient(client, bMsg);
}
catch (Exception ee)
{
OperLogUtil.WriteFileLog(ee.Message, ConfigurationHelperUtil.DSA_LOG_TYPE_ERROR);
}
}
#endregion
#region 推送消息,推送所有在线的客户端
public static void sendInfoAll(string msg)
{
try
{
List<IPEndPoint> list = tcpServerEngine.GetClientList();//获取在线设备
if (list.Count > 0)
{
for (int i = 0; i < list.Count; i++)
{
try
{
IPEndPoint client = list[i];
if (client == null)
{//没有客户端
OperLogUtil.WriteFileLog("没有客户端:" + client.Address + ":" + client.Port, ConfigurationHelperUtil.DSA_LOG_TYPE_WEBSCOKET);
}
else if (!tcpServerEngine.IsClientOnline(client))
{//客户端不在线
OperLogUtil.WriteFileLog("客户端不在线:" + client.Address + ":" + client.Port, ConfigurationHelperUtil.DSA_LOG_TYPE_WEBSCOKET);
}
else
{
msg = msg + "\0";// "\0" 表示一个消息的结尾
byte[] bMsg = System.Text.Encoding.UTF8.GetBytes(msg);//消息使用UTF-8编码
tcpServerEngine.SendMessageToClient(client, bMsg);
OperLogUtil.WriteFileLog("客户端:" + client.Address + ":" + client.Port + "发送:" + msg, ConfigurationHelperUtil.DSA_LOG_TYPE_WEBSCOKET);
}
}
catch (Exception ee)
{
OperLogUtil.WriteFileLog("sendInfoAll1" + ee.Message, ConfigurationHelperUtil.DSA_LOG_TYPE_ERROR);
}
}
}
}
catch (Exception ee)
{
OperLogUtil.WriteFileLog("sendInfoAll2" + ee.Message, ConfigurationHelperUtil.DSA_LOG_TYPE_ERROR);
}
}
#endregion
#region 判断是字符串是数字
/**
* 判断字符串是否是数字
*/
public static Boolean isNumber(String value)
{
return isInteger(value);
}
/**
* 判断字符串是否是整数
*/
public static Boolean isInteger(String value)
{
try
{
int num = int.Parse(value);
return true;
}
catch (Exception e)
{
return false;
}
}
#endregion
}
}
5.源码下载
边栏推荐
- Esp8266 at+cipstart= "", "", 8080 error closed ultimate solution
- UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xd0 in position 0成功解决
- Postman Interface Association
- Solution to the problem of cross domain inaccessibility of Chrome browser
- How to change php INI file supports PDO abstraction layer
- First blog
- Timestamp with implicit default value is deprecated error in MySQL 5.6
- MySQL21-用戶與權限管理
- Installation and use of MySQL under MySQL 19 Linux
- MySQL完全卸载(Windows、Mac、Linux)
猜你喜欢
MySQL 29 other database tuning strategies
Postman uses scripts to modify the values of environment variables
Case identification based on pytoch pulmonary infection (using RESNET network structure)
Win10: how to modify the priority of dual network cards?
A brief introduction to the microservice technology stack, the introduction and use of Eureka and ribbon
Mysql35 master slave replication
[C language foundation] 04 judgment and circulation
Mysql30 transaction Basics
CSDN问答模块标题推荐任务(二) —— 效果优化
MySQL23-存儲引擎
随机推荐
Discriminant model: a discriminant model creation framework log linear model
MySQL25-索引的创建与设计原则
UEditor国际化配置,支持中英文切换
MySQL29-数据库其它调优策略
PyTorch RNN 实战案例_MNIST手写字体识别
Why is MySQL still slow to query when indexing is used?
text 文本数据增强方法 data argumentation
Postman environment variable settings
C language advanced pointer Full Version (array pointer, pointer array discrimination, function pointer)
February 13, 2022 - Maximum subarray and
Installation and use of MySQL under MySQL 19 Linux
Use of dataset of pytorch
Solve the problem that XML, YML and properties file configurations cannot be scanned
使用OVF Tool工具从Esxi 6.7中导出虚拟机
Windchill配置远程Oracle数据库连接
CSDN-NLP:基于技能树和弱监督学习的博文难度等级分类 (一)
[leectode 2022.2.13] maximum number of "balloons"
Emotional classification of 1.6 million comments on LSTM based on pytoch
【博主推荐】C#生成好看的二维码(附源码)
Some problems in the development of unity3d upgraded 2020 VR