当前位置:网站首页>【博主推荐】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.源码下载
边栏推荐
- MySQL23-存儲引擎
- 保姆级手把手教你用C语言写三子棋
- frp内网穿透那些事
- Isn't there anyone who doesn't know how to write mine sweeping games in C language
- [BMZCTF-pwn] 11-pwn111111
- CSDN问答标签技能树(二) —— 效果优化
- Discriminant model: a discriminant model creation framework log linear model
- Baidu Encyclopedia data crawling and content classification and recognition
- Esp8266 at+cipstart= "", "", 8080 error closed ultimate solution
- Nanny hand-in-hand teaches you to write Gobang in C language
猜你喜欢
Moteur de stockage mysql23
Introduction tutorial of typescript (dark horse programmer of station B)
该不会还有人不懂用C语言写扫雷游戏吧
Mysql34 other database logs
Breadth first search rotten orange
UnicodeDecodeError: ‘utf-8‘ codec can‘t decode byte 0xd0 in position 0成功解决
MySQL24-索引的数据结构
【博主推荐】C#生成好看的二维码(附源码)
Mysql33 multi version concurrency control
MySQL 20 MySQL data directory
随机推荐
在jupyter NoteBook使用Pytorch进行MNIST实现
MySQL19-Linux下MySQL的安装与使用
Mysql36 database backup and recovery
MySQL30-事务基础知识
Mysql27 - Optimisation des index et des requêtes
ZABBIX introduction and installation
Download and installation of QT Creator
虚拟机Ping通主机,主机Ping不通虚拟机
Time complexity (see which sentence is executed the most times)
CSDN question and answer tag skill tree (II) -- effect optimization
Advantages and disadvantages of evaluation methods
Mysql21 user and permission management
用于实时端到端文本识别的自适应Bezier曲线网络
npm一个错误 npm ERR code ENOENT npm ERR syscall open
Mysql 其他主机无法连接本地数据库
windows下同时安装mysql5.5和mysql8.0
Security design verification of API interface: ticket, signature, timestamp
Water and rain condition monitoring reservoir water and rain condition online monitoring
Technology | diverse substrate formats
CSDN question and answer module Title Recommendation task (I) -- Construction of basic framework