背景
由于项目中,很多时候只需要执行SQL或者是存储过程,这个时候不想去动服务接口的代码,所以当前设计思路应运而生。
数据库表设计
1. CommonApiMainConfig
**当前为配置主表**
字段 | 类型 | 是否为空 | 说明 |
---|---|---|---|
Id | bigint | 否 | 主键 |
[Key] | varchar(50) | 否 | 接口的主要关键键 |
Version | int | 否 | 接口的版本 |
RequestType | varchar(50) | 否 | 请求类型 Get/Post |
Remark | varchar(500) | 是 | |
ProcedureName | varchar(500) | 是 | 存储过程名称 |
Sql | varchar(max) | 是 | SQL语句 ,如果填了存储过程则忽略SQL语句 |
IsEnable | bit | 是 | 是否启用 |
IsSystem | bit | 是 | 是否为系统 |
ResultType | varchar(50) | 是 | 返回的数据类型(Array:多个数据表的所有数据;SingleArray:第一个数据表的所有数据;Object:第一个数据表的第一行数据) |
InUser | varchar(500) | 是 | 新增用户 |
Indate | datetime | 是 | 新增时间 |
2. CommonApiInputParamConfig
**当前为输入参数配置表**
字段 | 类型 | 是否为空 | 说明 |
---|---|---|---|
Id | bigint | 否 | 主键 |
[Key] | varchar(50) | 否 | 接口的主要关键键 |
Version | int | 否 | 版本 |
ParameterName | varchar(100) | 否 | 参数名称 |
ParameterType | varchar(50) | 否 | 参数值类型 |
IsEnable | bit | 是 | 是否启用 |
InUser | varchar(500) | 是 | 新增用户 |
Indate | datetime | 是 | 新增时间 |
代码实现
模型
/// <summary>
/// 作者:Sky
/// 时间:2023-04-23 10:30:15
/// 描述:
/// 说明:本文件由工具自动生成。
/// </summary>
public partial class CommonApiMainConfig
{
/// <summary>
/// 请求类型 Get/Post
/// </summary>
public string RequestType { get; set; }
/// <summary>
/// 存储过程名称
/// </summary>
public string ProcedureName { get; set; }
/// <summary>
/// SQL语句 ,如果填了存储过程则忽略SQL语句
/// </summary>
public string Sql { get; set; }
/// <summary>
/// 是否为系统内置接口
/// </summary>
public bool? IsSystem { get; set; }
/// <summary>
/// 返回的数据类型(Array:多个数据表的所有数据;SingleArray:第一个数据表的所有数据;Object:第一个数据表的第一行数据)
/// </summary>
public string ResultType { get; set; }
public DateTime Indate { get; set; }
public string InUser { get; set; }
public int Version { get; set; }
public string Key { get; set; }
public string Remark { get; set; }
public bool IsEnable { get; set; }
public long Id { get; set; }
}
/// <summary>
/// 作者:Sky
/// 时间:2023-04-01 14:34:51
/// 描述:
/// 说明:本文件由工具自动生成。
/// </summary>
public partial class CommonApiInputParamConfig
{
public long Id { get; set; }
/// <summary>
/// 参数名称
/// </summary>
public string ParameterName { get; set; }
/// <summary>
/// 参数值类型
/// </summary>
public string ParameterType { get; set; }
/// <summary>
/// 是否启用
/// </summary>
public bool IsEnable { get; set; }
public int Version { get; set; }
public string InUser { get; set; }
public DateTime Indate { get; set; }
public string Key { get; set; }
}
/// <summary>
/// 返回父类
/// </summary>
public class ReturnBase
{
public ReturnBase()
{
}
public ReturnBase(ResultType ResultType)
{
this.state = ResultType.ToString();
}
public ReturnBase(string message)
{
this.message = message;
}
public ReturnBase(ResultType ResultType, string message)
{
this.state = ResultType.ToString();
this.message = message;
}
public ReturnBase(ResultType ResultType, string message, dynamic data)
{
this.state = ResultType.ToString();
this.message = message;
this.data = data;
}
public string state { get; set; } = ResultType.success.ToString();
public string message { get; set; } = "操作成功!";
/// <summary>
/// 返回数据
/// </summary>
public dynamic data { get; set; }
}
辅助代码
public static string ToJson(this DataSet ds)
{
return Dataset2Json(ds);
}
public static string ToJson(this DataTable dt, bool isAppendTotal, bool isSingle)
{
return DataTable2Json(dt, isAppendTotal, isSingle);
}
public static string Dataset2Json(DataSet ds)
{
//total表示DataSet里dataTable的数据总条数
StringBuilder json = new StringBuilder();
if (ds.Tables.Count > 1)
{
json.Append("[");
}
foreach (DataTable dt in ds.Tables)
{
//{"total":5,"rows":[
//json.Append("{\"total\":");
//if (total == -1)
//{
// json.Append(dt.Rows.Count);
//}
//else
//{
// json.Append(total);
//}
//json.Append(",\"data\":[");
json.Append(DataTable2Json(dt, true));
//json.Append(","attr1":"Large"")
//json.Append("]},");
json.Append(",");
}
if (ds.Tables.Count > 0)
{
json.Remove(json.Length - 1, 1);
}
if (ds.Tables.Count > 1)
{
json.Append("]");
}
return json.ToString();
}
/// <summary>
/// 表格转JSON
/// </summary>
/// <param name="dt"></param>
/// <param name="isAppendTotal">是否添加total</param>
/// <param name="isSingle">是否为单个</param>
/// <returns></returns>
public static string DataTable2Json(DataTable dt, bool isAppendTotal = false, bool isSingle = false)
{
StringBuilder jsonBuilder = new StringBuilder();
if (isSingle)
{
jsonBuilder.Append("{");
for (int j = 0; j < dt.Columns.Count; j++)
{
jsonBuilder.Append("\"");
jsonBuilder.Append(dt.Columns[j].ColumnName);
jsonBuilder.Append("\":");
jsonBuilder.Append(GetRowValue(dt.Rows[0][j]));
jsonBuilder.Append(",");
}
if (dt.Columns.Count > 0)
{
jsonBuilder.Remove(jsonBuilder.Length - 1, 1);
}
jsonBuilder.Append("}");
}
else
{
if (isAppendTotal)
{
jsonBuilder.Append("{\"total\":");
jsonBuilder.Append($"{dt.Rows.Count},\"rows\":[");
}
else
{
jsonBuilder.Append("[");
}
for (int i = 0; i < dt.Rows.Count; i++)
{
//jsonBuilder.Append("{"total":5,"rows":[");
jsonBuilder.Append("{");
for (int j = 0; j < dt.Columns.Count; j++)
{
jsonBuilder.Append("\"");
jsonBuilder.Append(dt.Columns[j].ColumnName);
jsonBuilder.Append("\":");
jsonBuilder.Append(GetRowValue(dt.Rows[i][j]));
jsonBuilder.Append(",");
}
jsonBuilder.Append("},");
}
if (dt.Rows.Count > 0)
{
jsonBuilder.Remove(jsonBuilder.Length - 1, 1);
}
if (isAppendTotal)
{
jsonBuilder.Append("]}");
}
else
{
jsonBuilder.Append("]");
}
}
return jsonBuilder.ToString();
}
private static string GetRowValue(object value)
{
if (value == null)
{
return "null";
}
if (value.GetType().Name == typeof(bool).Name)
{
return value.ToString().ToLower();
}
List<string> typeNames = new List<string>() { typeof(int).Name, typeof(double).Name, typeof(decimal).Name, typeof(float).Name };
if (typeNames.IndexOf(value.GetType().Name) > -1)
{
return value.ToString();
}
return "\"" + value.ToString() + "\"";
}
核心代码
当前代码是基于Dapper操作数据的,如有需要则将操作数据部分换成其他的;可以在这基础上继续进行改进,目前是相对比较基础的实现。
[HttpGet]
[HttpPost]
[Route("api/CommonApi/{key}")]
public dynamic CommonApi(string key, int version = 0)
{
try
{
using (var context = DapperHelper.GetContext(Factory.Eds42EMS))
{
CommonApiMainConfig commonApiConfig;
if (version == 0) //返回最新版本有效的
{
commonApiConfig = context.Select<CommonApiMainConfig>().Where(w => w.Key == key && w.IsEnable && w.RequestType == Request.Method.Method).OrderByDescending(o => o.Version).QuerySingle();
}
else
{
commonApiConfig = context.Select<CommonApiMainConfig>().Where(w => w.Key == key && w.IsEnable && w.RequestType == Request.Method.Method && w.Version == version).QuerySingle();
}
if (commonApiConfig == null)
{
return new ReturnBase(ResultType.error, "当前接口未实现,请联系系统管理员!", null);
}
var apiParam = new Dictionary<string, dynamic>();
if (commonApiConfig.RequestType.ToLower() == "get")
{
var getRequestParams = Request.GetQueryNameValuePairs();
foreach (var item in getRequestParams)
{
apiParam.Add(item.Key, item.Value);
}
}
else if (commonApiConfig.RequestType.ToLower() == "post")
{
//目前仅支持常规类型的参数
var content = Request.Content.ReadAsStringAsync().Result;
if (!string.IsNullOrEmpty(content))
{
var obj = Newtonsoft.Json.Linq.JValue.Parse(content);
foreach (var item in obj)
{
apiParam.Add(((Newtonsoft.Json.Linq.JProperty)item).Name, ((Newtonsoft.Json.Linq.JProperty)item).Value.HasValues ? ((Newtonsoft.Json.Linq.JProperty)item).Value.ToList() : ((Newtonsoft.Json.Linq.JValue)((Newtonsoft.Json.Linq.JProperty)item).Value).Value);
}
}
//var fileOperateKey = obj["FileOperateKey"].ToString();
}
else
{
return new ReturnBase(ResultType.error, $"当前接口的此类型【{Request.Method.Method}】未实现,请联系系统管理员!", null);
}
var commonApiParamConfig = context.Select<CommonApiInputParamConfig>().Where(w => w.Key == key && w.Version == commonApiConfig.Version && w.IsEnable).QueryMany().ToList();
//DapperLambda.Dapper.DynamicParameters param = new DapperLambda.Dapper.DynamicParameters();
var param = new List<SqlParameter>();
foreach (var item in commonApiParamConfig)
{
if (apiParam.Any(a => a.Key.ToLower() == item.ParameterName.ToLower()))
{
param.Add(new SqlParameter("@" + item.ParameterName, apiParam.FirstOrDefault(a => a.Key.ToLower() == item.ParameterName.ToLower()).Value));
}
//param.Add(item.ParameterName, item.Value);
}
DataSet resultDs = new DataSet();
if ((string.IsNullOrEmpty(commonApiConfig.ProcedureName)) && (string.IsNullOrEmpty(commonApiConfig.Sql)))
{
return new ReturnBase(ResultType.error, $"当前接口的配置无效,必须配置ProcedureName或Sql,请联系系统管理员!", null);
}
else if (!string.IsNullOrEmpty(commonApiConfig.ProcedureName))
{
resultDs = DapperHelper.ExecuteStoredProcedure(commonApiConfig.ProcedureName, param.ToArray(), Factory.Eds42EMS);
}
else if (!string.IsNullOrEmpty(commonApiConfig.Sql))
{
resultDs = DapperHelper.ExecuteSql(commonApiConfig.Sql, param.ToArray(), Factory.Eds42EMS);
}
var resultJSON = "";
#region 解析返回值
if (!string.IsNullOrEmpty(commonApiConfig.ResultType))
{
if (commonApiConfig.ResultType.ToLower() == "array")
{
resultJSON = resultDs.ToJson();
}
else if (commonApiConfig.ResultType.ToLower() == "singlearray")
{
resultJSON = resultDs.Tables[0].ToJson(true, false);
}
else if (commonApiConfig.ResultType.ToLower() == "object")
{
resultJSON = resultDs.Tables[0].ToJson(false, true);
}
else
{
//未实现
return new ReturnBase(ResultType.error, $"当前接口的配置无效,必须配置正确的ResultType,请联系系统管理员!", null);
}
}
else
{
return new ReturnBase(ResultType.error, $"当前接口的配置无效,必须配置ResultType,请联系系统管理员!", null);
}
#endregion
return new ReturnBase(ResultType.success, null, Newtonsoft.Json.JsonConvert.DeserializeObject(resultJSON));
}
}
catch (System.Data.SqlClient.SqlException ex)
{
return new ReturnBase(ResultType.error, ex.Message.ToString(), null);
}
catch (Exception ex)
{
return new ReturnBase(ResultType.error, ex.ToString(), null);
}
}
demo数据
CommonApiMainConfig 表数据
CommonApiInputParamConfig 表数据
存储过程Test
-- =============================================
-- Author:
-- Create date: 2023-4-1 16:32:08
-- Description: 测试
-- =============================================
ALTER PROCEDURE [dbo].[Test]
@a varchar(50),
@b varchar(50)
AS
BEGIN
select @a as a, @b as b;
select @a as a, @b as b;
END
示例效果
post请求跟get请求差不多,这里就不举例了。以下举例为get请求的。
- get请求,Key为Test的接口,地址栏传参a,b
(由于没有带有版本号,则取的是版本最高的,也就是数据version为99的数据配置,返回的data为一个对象)
- get请求,Key为Test的接口,地址栏传参a,b ,带有version版本则取对应版本的数据
返回的data是一个数组,且数组中的存储过程返回了多个表
返回的data是一个数组,且只返回表格中的第一个表格
后续可自行实现
- 输入参数的数据类型卡控
- 目前是只实现单值(int,string,double等)的,后续可以根据考虑一下object对象,Object对象集合等等类型作为post请求的(例如: 批量插入数据的时候就可以使用[{a:1,b:2},{a:2,b:3}])
…
等等等等,思路决定出路~