322 lines
13 KiB
C#
322 lines
13 KiB
C#
|
|
using Newtonsoft.Json;
|
|||
|
|
using Song.ViewData.Attri;
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Reflection;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
|
|||
|
|
namespace Song.ViewData
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 执行具体的方法
|
|||
|
|
/// </summary>
|
|||
|
|
public class ExecuteMethod
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
#region 单件模式
|
|||
|
|
private static readonly ExecuteMethod _instance = new ExecuteMethod();
|
|||
|
|
private ExecuteMethod() { }
|
|||
|
|
/// <summary>
|
|||
|
|
/// 返回实例
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static ExecuteMethod GetInstance()
|
|||
|
|
{
|
|||
|
|
return _instance;
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
#region 创建并缓存实例对象
|
|||
|
|
//存储对象的键值对,key为对象的类名称(全名),value为对象自身
|
|||
|
|
private Dictionary<string, object> _objects = new Dictionary<string, object>();
|
|||
|
|
/// <summary>
|
|||
|
|
/// 创建对象,如果存在,则直接返回;如果不存在,创建后返回
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="letter"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static object CreateInstance(Letter letter)
|
|||
|
|
{
|
|||
|
|
string assemblyName = "Song.ViewData";
|
|||
|
|
string classFullName = String.Format("{0}.Methods.{1}", assemblyName, letter.ClassName);
|
|||
|
|
object obj = null;
|
|||
|
|
//由缓存中查找,是否存在
|
|||
|
|
ExecuteMethod curr = ExecuteMethod.GetInstance();
|
|||
|
|
foreach (KeyValuePair<string, object> kv in curr._objects)
|
|||
|
|
{
|
|||
|
|
if (classFullName.Trim().Equals(kv.Key, StringComparison.CurrentCultureIgnoreCase))
|
|||
|
|
{
|
|||
|
|
obj = kv.Value;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (obj != null) return obj;
|
|||
|
|
//如果之前未创建,则重新创建
|
|||
|
|
Type type = null;
|
|||
|
|
Assembly assembly = Assembly.Load(assemblyName);
|
|||
|
|
Type[] types = assembly.GetExportedTypes()
|
|||
|
|
.Where(t => t.GetInterfaces().Contains(typeof(IViewAPI)))
|
|||
|
|
.ToArray();
|
|||
|
|
foreach (Type info in types)
|
|||
|
|
{
|
|||
|
|
if (info.FullName.Equals(classFullName, StringComparison.CurrentCultureIgnoreCase))
|
|||
|
|
{
|
|||
|
|
type = info;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (type == null) throw new Exception(
|
|||
|
|
string.Format("调用的对象'{0}'不存在, 可能是'{1}'拼写错误",
|
|||
|
|
classFullName, letter.ClassName));
|
|||
|
|
obj = System.Activator.CreateInstance(type); //创建对象
|
|||
|
|
ExecuteMethod.GetInstance()._objects.Add(type.FullName, obj); //记录到缓存
|
|||
|
|
return obj;
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 执行,按实际结果返回
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="letter">客户端递交的参数信息</param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static object Exec(Letter letter)
|
|||
|
|
{
|
|||
|
|
//1.创建对象,即$api.get("account/single")中的account
|
|||
|
|
object execObj = ExecuteMethod.CreateInstance(letter);
|
|||
|
|
//2.获取要执行的方法,即$api.get("account/single")中的single
|
|||
|
|
MethodInfo method = getMethod(execObj.GetType(), letter);
|
|||
|
|
//3#.验证方法的特性,一是验证Http动词,二是验证是否登录后操作,三是验证权限
|
|||
|
|
//----验证Http谓词访问限制
|
|||
|
|
HttpAttribute.Verify(letter.HTTP_METHOD, method);
|
|||
|
|
//LoginAttribute.Verify(method);
|
|||
|
|
//----范围控制,本机或局域网,或同域
|
|||
|
|
bool isRange = RangeAttribute.Verify(letter, method);
|
|||
|
|
//----验证是否需要登录
|
|||
|
|
LoginAttribute loginattr = LoginAttribute.Verify(method);
|
|||
|
|
|
|||
|
|
|
|||
|
|
//4.构建执行该方法所需要的参数
|
|||
|
|
object[] parameters = getInvokeParam(method, letter);
|
|||
|
|
//5.执行方法,返回结果
|
|||
|
|
object objResult = null; //结果
|
|||
|
|
//只有get方式时,才使用缓存
|
|||
|
|
CacheAttribute cache = null;
|
|||
|
|
if (letter.HTTP_METHOD.Equals("put", StringComparison.CurrentCultureIgnoreCase))
|
|||
|
|
CacheAttribute.Remove(method, letter);
|
|||
|
|
if (letter.HTTP_METHOD.Equals("get", StringComparison.CurrentCultureIgnoreCase))
|
|||
|
|
cache = CacheAttribute.GetAttr<CacheAttribute>(method);
|
|||
|
|
if (cache != null)
|
|||
|
|
{
|
|||
|
|
objResult = CacheAttribute.GetResult(method, letter);
|
|||
|
|
if (objResult == null)
|
|||
|
|
{
|
|||
|
|
objResult = method.Invoke(execObj, parameters);
|
|||
|
|
CacheAttribute.Insert(cache.Expires, method, letter, objResult);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
objResult = method.Invoke(execObj, parameters);
|
|||
|
|
}
|
|||
|
|
//将执行结果写入日志
|
|||
|
|
LoginAttribute.LogWrite(loginattr, objResult);
|
|||
|
|
return objResult;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 执行,返回结构封装到DataResult对象中
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="letter">客户端递交的参数信息</param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public static DataResult ExecToResult(Letter letter)
|
|||
|
|
{
|
|||
|
|
DateTime time = DateTime.Now;
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (!"weishakeji".Equals(letter.HTTP_Mark))
|
|||
|
|
throw VExcept.System("请求标识不正确", 100);
|
|||
|
|
//执行方法
|
|||
|
|
object res = Exec(letter);
|
|||
|
|
//计算耗时
|
|||
|
|
double span = ((TimeSpan)(DateTime.Now - time)).TotalMilliseconds;
|
|||
|
|
//
|
|||
|
|
//如果是分页数据
|
|||
|
|
if (res is ListResult)
|
|||
|
|
{
|
|||
|
|
ListResult list = (ListResult)res;
|
|||
|
|
list.ExecSpan = span;
|
|||
|
|
return list;
|
|||
|
|
}
|
|||
|
|
return new Song.ViewData.DataResult(res, span); //普通数据
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
//如果是自定义异常
|
|||
|
|
if (ex.InnerException is VExcept)
|
|||
|
|
{
|
|||
|
|
VExcept except = (VExcept)ex.InnerException;
|
|||
|
|
return new Song.ViewData.DataResult(except, time);
|
|||
|
|
}
|
|||
|
|
return new Song.ViewData.DataResult(ex, time);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 要执行的方法,根据方法名、参数数量
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="type">要调用的对象的类型</param>
|
|||
|
|
/// <param name="p"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
private static MethodInfo getMethod(Type type, Letter p)
|
|||
|
|
{
|
|||
|
|
//1、先判断方法是否存在
|
|||
|
|
List<MethodInfo> methods = new List<MethodInfo>();
|
|||
|
|
foreach (MethodInfo mi in type.GetMethods())
|
|||
|
|
{
|
|||
|
|
if (p.MethodName.Equals(mi.Name, StringComparison.CurrentCultureIgnoreCase))
|
|||
|
|
methods.Add(mi);
|
|||
|
|
}
|
|||
|
|
if (methods.Count < 1)
|
|||
|
|
throw new Exception(string.Format("调用方法'{0}.{1}'不存在", p.ClassName, p.MethodName));
|
|||
|
|
//2、判断方法的参数名称,是否与传递来的参数名称匹配,参数数量必须匹配
|
|||
|
|
//只有一个参数,且类型是Letter
|
|||
|
|
MethodInfo mbLetter = null;
|
|||
|
|
for (int i = 0; i < methods.Count; i++)
|
|||
|
|
{
|
|||
|
|
ParameterInfo[] pis = methods[i].GetParameters();
|
|||
|
|
if (pis.Length == 1 && p.GetType().FullName.Equals(pis[0].ParameterType.FullName))
|
|||
|
|
{
|
|||
|
|
mbLetter = methods[i];
|
|||
|
|
methods.Remove(methods[i]);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
MethodInfo method = null;
|
|||
|
|
foreach (MethodInfo mi in methods)
|
|||
|
|
{
|
|||
|
|
//2-1、判断参数个数是否相同
|
|||
|
|
int paraCount = 0;
|
|||
|
|
foreach (ParameterInfo pi in mi.GetParameters())
|
|||
|
|
{
|
|||
|
|
if (!pi.IsOut) paraCount++;
|
|||
|
|
}
|
|||
|
|
//方法的参数个数,和传参的参数个数,必须相等
|
|||
|
|
if (paraCount == p.Params.Count)
|
|||
|
|
{
|
|||
|
|
method = mi;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//2-2、判断参数名称与传递来的参数名称是否一致
|
|||
|
|
if (method != null)
|
|||
|
|
{
|
|||
|
|
bool ismatch = true; //是否匹配
|
|||
|
|
foreach (ParameterInfo pi in method.GetParameters())
|
|||
|
|
{
|
|||
|
|
//如果参数是Parameter类型,则跳过匹配
|
|||
|
|
if (p.GetType().FullName.Equals(pi.ParameterType.FullName)) continue;
|
|||
|
|
//只要有一个参数不匹配,即中断
|
|||
|
|
if (!p.ExistParameter(pi.Name))
|
|||
|
|
{
|
|||
|
|
ismatch = false;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (!ismatch) method = null;
|
|||
|
|
}
|
|||
|
|
if (method == null) method = mbLetter;
|
|||
|
|
if (method == null) throw new Exception(
|
|||
|
|
string.Format("所调用方法'{0}.{1}'的参数名称与实际传参不匹配;实际传参:{2}",
|
|||
|
|
type.FullName, p.MethodName,
|
|||
|
|
p.ToString() == string.Empty ? "null" : p.ToString()));
|
|||
|
|
return method;
|
|||
|
|
}
|
|||
|
|
/// <summary>
|
|||
|
|
/// 返回方法执行所需要的参数
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="method">要执行的方法</param>
|
|||
|
|
/// <param name="letter">传递来的参数</param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
private static object[] getInvokeParam(MethodInfo method, Letter letter)
|
|||
|
|
{
|
|||
|
|
ParameterInfo[] paramInfos = method.GetParameters();
|
|||
|
|
object[] objs = new object[paramInfos.Length];
|
|||
|
|
for (int i = 0; i < objs.Length; i++)
|
|||
|
|
{
|
|||
|
|
ParameterInfo pi = paramInfos[i];
|
|||
|
|
//如果参数是Letter类型,则直接赋值
|
|||
|
|
if (letter.GetType().FullName.Equals(pi.ParameterType.FullName))
|
|||
|
|
{
|
|||
|
|
objs[i] = letter;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
//接口方法的参数所对应的客户端传来的值
|
|||
|
|
string val = letter[pi.Name].String;
|
|||
|
|
//如果参数为输出型的,则不赋值(ViewData接口不允许此类参数)
|
|||
|
|
if (pi.IsOut || string.IsNullOrWhiteSpace(val))
|
|||
|
|
{
|
|||
|
|
objs[i] = null;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
//如果参数是数据实体
|
|||
|
|
if (pi.ParameterType.BaseType != null && pi.ParameterType.BaseType.FullName == "WeiSha.Data.Entity")
|
|||
|
|
{
|
|||
|
|
//创建实体
|
|||
|
|
object entity = pi.ParameterType.Assembly.CreateInstance(pi.ParameterType.FullName);
|
|||
|
|
Dictionary<string, string> dic = JsonConvert.DeserializeObject<Dictionary<string, string>>(val);
|
|||
|
|
PropertyInfo[] props = pi.ParameterType.GetProperties();
|
|||
|
|
for (int j = 0; j < props.Length; j++)
|
|||
|
|
{
|
|||
|
|
PropertyInfo opi = props[j]; //实体属性
|
|||
|
|
foreach (KeyValuePair<string, string> kv in dic)
|
|||
|
|
{
|
|||
|
|
if (kv.Key.Equals(opi.Name, StringComparison.CurrentCultureIgnoreCase))
|
|||
|
|
{
|
|||
|
|
//实体属性的值
|
|||
|
|
object tm = string.IsNullOrEmpty(kv.Value) ? null : WeiSha.Common.DataConvert.ChangeType(kv.Value, opi.PropertyType);
|
|||
|
|
opi.SetValue(entity, tm, null);
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
objs[i] = entity;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
//将客户端传来的参数,转换为方法对应的参数类型
|
|||
|
|
|
|||
|
|
if (!pi.ParameterType.Name.Equals("string", StringComparison.CurrentCultureIgnoreCase))
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrWhiteSpace(val)) throw new Exception("参数 " + pi.Name + " 的值为空");
|
|||
|
|
}
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
switch (pi.ParameterType.Name)
|
|||
|
|
{
|
|||
|
|
case "DateTime":
|
|||
|
|
if (val == null || string.IsNullOrWhiteSpace(val.ToString()))
|
|||
|
|
{
|
|||
|
|
objs[i] = DateTime.Now;
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
|
|||
|
|
long lTime = long.Parse(val + "0000");
|
|||
|
|
TimeSpan toNow = new TimeSpan(lTime);
|
|||
|
|
objs[i] = dt.Add(toNow);
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
objs[i] = Convert.ChangeType(val, pi.ParameterType);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
catch
|
|||
|
|
{
|
|||
|
|
throw new Exception("参数 " + pi.Name + " 的值,数据格式不正确");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
return objs;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|