asp.net webapi 结合Autofac实现程序集动态注入(framework版本)
背景
- FX版本:4.5.2
Nuget
Autofac:4.2.1
Autofac.WebApi2:4.2.0
log4net:2.0.15项目结构
代码
IUniversalService
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IUniversalService
{
public interface ISendMsg
{
/// <summary>
/// 发送消息接口
/// </summary>
/// <param name="msg"></param>
string Send(object msg);
}
}
UniversalServiceTest
using IUniversalService;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UniversalServiceTest
{
public class SendMsg : ISendMsg
{
public string Send(object msg)
{
return("消息发送被调用了..."+msg);
}
}
}
UniversalGateway
using Autofac;
using System.IO;
using System;
using System.Reflection;
using System.Web.Http;
using System.Web.Mvc;
using IUniversalService;
using System.Linq;
using Autofac.Integration.WebApi;
using Autofac.Core;
public class AutofacConfig
{
/// <summary>
/// 负责调用autofac框架实现业务逻辑层和数据仓储层程序集中的类型对象的创建
/// 负责创建MVC控制器类的对象(调用控制器中的有参构造函数),接管DefaultControllerFactory的工作
/// </summary>
public static void Register()
{
var builder = new ContainerBuilder();
// Get your HttpConfiguration.
var config = GlobalConfiguration.Configuration;
// Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL: Register the Autofac filter provider.
builder.RegisterWebApiFilterProvider(config);
// OPTIONAL: Register the Autofac model binder provider.
builder.RegisterWebApiModelBinderProvider();
//一次性注册所有实现了IDependency接口的类
Type baseType = typeof(ISendMsg);
Assembly[] assemblies =
Directory.GetFiles(AppDomain.CurrentDomain.RelativeSearchPath + "\\Plugs", "*.dll").Select(Assembly.LoadFrom).ToArray();
builder.RegisterAssemblyTypes(assemblies)
.Where(type => baseType.IsAssignableFrom(type) && !type.IsAbstract)
.AsSelf().AsImplementedInterfaces().PropertiesAutowired()
.InstancePerLifetimeScope();
// RegisterType方式:
//builder.RegisterType<SendMsg>().As<ISendMsg>().InstancePerRequest(); ;
// Set the dependency resolver to be Autofac.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
注意:由于我使用到了不同的文件夹来存储定制软件的dll,需要在web.config或者app.config里面进行如下配置
web.config
<assemblyBinding>
...
<!--<publisherPolicy apply="yes" />这句不要也是可以的-->
<probing privatePath="Plugs;Plugs/2" />
</assemblyBinding>
WebApi通用配置项
项目结构图
ApiResultAttribute.cs
using Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web;
using System.Web.Http.Filters;
using System.Web.Mvc;
namespace Common
{
public class ApiResultAttribute : System.Web.Http.Filters.ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
// 不包裹返回值
var noPackage = actionExecutedContext.ActionContext.ActionDescriptor.GetCustomAttributes<NoPackageResult>();
if (!noPackage.Any())
{
ApiResultModel result = new ApiResultModel();
//先捕获API内部的异常,无论代码能否完整执行,客户端都能得到正确的响应格式
if (actionExecutedContext.Exception != null)
{
throw actionExecutedContext.Exception;
//result.StatusCode = System.Net.HttpStatusCode.InternalServerError;
//result.IsSuccess = false;
//result.ErrorMessage = actionExecutedContext.Exception.Message;
}
else
{
// 取得由 API 返回的状态代码
result.StatusCode = actionExecutedContext.ActionContext.Response.StatusCode;
var a = actionExecutedContext.ActionContext.Response.Content.ReadAsAsync<object>();
if (!a.IsFaulted)
{
// 取得由 API 返回的资料
result.Data = actionExecutedContext.ActionContext.Response.Content.ReadAsAsync<object>().Result;
}
//请求是否成功
result.IsSuccess = actionExecutedContext.ActionContext.Response.IsSuccessStatusCode;
}
//结果转为自定义消息格式
HttpResponseMessage httpResponseMessage = JsonHelper.toJson(result);
// 重新封装回传格式
actionExecutedContext.Response = httpResponseMessage;
}
}
}
}
WebApiConfig.cs
using Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace UniversalGateway
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API 配置和服务
// Web API 路由
config.MapHttpAttributeRoutes();
config.Filters.Add(new ApiResultAttribute());//重新包装结果
config.Filters.Add(new ExceptionFilter());//重新包装异常处理过滤器
config.Routes.MapHttpRoute(
name: "DefaultApi2",
routeTemplate: "api/{controller}/{action}",
defaults: new { action = "Index" }
);
//config.Routes.MapHttpRoute(
// name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
// defaults: new { id = RouteParameter.Optional }
//);
}
}
}
调整内容
ExceptionFilter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http.Filters;
namespace Common
{
/// <summary>
/// 全局异常过滤器
/// </summary>
/// <!-- 2019-8-22 16:18:10 添加 -->
public class ExceptionFilter : System.Web.Http.Filters.IExceptionFilter
{
public bool AllowMultiple => true;
public Task ExecuteExceptionFilterAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken)
{
var ex = actionExecutedContext.Exception.InnerException ?? actionExecutedContext.Exception;
return Task.Run(() =>
{
//异常记录日志到本地 2019-8-28 13:55:45 添加
//Common.LogHelpter.AddLog(ex.ToString());
LogHelper.ErrorLog(ex);//写入错误日志
string msg = ex.Message;
ApiResultModel result = new ApiResultModel();
result.IsSuccess = false;
result.StatusCode = System.Net.HttpStatusCode.OK;
result.ErrorMessage = msg;
//结果转为自定义消息格式
HttpResponseMessage httpResponseMessage = JsonHelper.toJson(result);
// 重新封装回传格式
actionExecutedContext.Response = httpResponseMessage;
});
}
}
}
ApiResultModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web;
namespace Common
{
/// <summary>
/// 返回类型
/// </summary>
public class ApiResultModel
{
private HttpStatusCode statusCode;
private object data;
private string errorMessage;
private bool isSuccess;
/// <summary>
/// 状态代码
/// </summary>
public HttpStatusCode StatusCode
{
get { return statusCode; }
set { statusCode = value; }
}
/// <summary>
/// 返回的数据
/// </summary>
public object Data
{
get { return data; }
set { data = value; }
}
/// <summary>
/// 错误消息
/// </summary>
public string ErrorMessage
{
get { return errorMessage; }
set { errorMessage = value; }
}
/// <summary>
/// 是否成功
/// </summary>
public bool IsSuccess
{
get { return isSuccess; }
set { isSuccess = value; }
}
}
}
JsonHelper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Script.Serialization;
namespace Common
{
public class JsonHelper
{
public static HttpResponseMessage toJson(Object obj)
{
String str;
if (obj is String || obj is Char)//如果是字符串或字符直接返回
{
str = obj.ToString();
}
else//否则序列为json字串
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
str = serializer.Serialize(obj);
}
HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str, Encoding.GetEncoding("UTF-8"), "application/json") };
return result;
}
}
}
LogHelper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
namespace Common
{
public class LogHelper
{
#region
/// <summary>
/// 记录普通日志
/// </summary>
/// <param name="data"></param>
public static void Info(string data)
{
log4net.ILog log = log4net.LogManager.GetLogger("loginfo");
Task.Run(() => log.Info(data));
}
/// <summary>
///
/// </summary>
/// <param name="msg"></param>
public static void ErrorLog(object msg)
{
log4net.ILog log = log4net.LogManager.GetLogger("logerror");
Task.Run(() => log.Error(msg)); //异步
// Task.Factory.StartNew(() =>log.Error(msg));// 这种异步也可以
//log.Error(msg); //这种也行跟你需要,性能越好,越强大,我还是使用异步方式
}
/// <summary>
///
/// </summary>
/// <param name="ex"></param>
public static void ErrorLog(Exception ex)
{
log4net.ILog log = log4net.LogManager.GetLogger("logerror");
Task.Run(() => log.Error(ex.Message.ToString() + "/r/n" + ex.Source.ToString() + "/r/n" + ex.TargetSite.ToString() + "/r/n" + ex.StackTrace.ToString()));
}
/// <summary>
///
/// </summary>
/// <param name="msg"></param>
/// <param name="ex"></param>
public static void ErrorLog(object msg, Exception ex)
{
log4net.ILog log = log4net.LogManager.GetLogger("logerror");
if (ex != null)
{
Task.Run(() => log.Error(msg, ex)); //异步
}
else
{
Task.Run(() => log.Error(msg)); //异步
}
}
#endregion
}
}
NoPackageResult .cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Common
{
public class NoPackageResult : Attribute
{
}
}
log4net.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5.2" />
<httpRuntime targetFramework="4.5.2" />
</system.web>
<configSections>
<!--添加配置节点-->
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<log4net>
<!--Info 日志-->
<appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
<file value="App_Data\\Logs\\" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<datePattern value="yyyy-MM-dd\\'info.log'" />
<staticLogFileName value="false" />
<param name="MaxSizeRollBackups" value="100"/>
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<param name="Encoding" value="utf-8" />
<maximumFileSize value="5MB"/>
<param name="PreserveLogFileNameExtension" value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%newline %nDATE: %date %nTHREAD: [%thread] %n%message %newline %n" />
</layout>
</appender>
<!--Debug 日志-->
<appender name="DebugAppender" type="log4net.Appender.RollingFileAppender">
<file value="App_Data\\Logs\\" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<datePattern value="yyyy-MM-dd\\'debug.log'" />
<staticLogFileName value="false" />
<param name="MaxSizeRollBackups" value="100" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<param name="Encoding" value="utf-8" />
<maximumFileSize value="5MB"/>
<param name="PreserveLogFileNameExtension" value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%newline %nDATE: %date %nTHREAD: [%thread] %n%message %newline %n" />
</layout>
</appender>
<!--Warn 日志-->
<appender name="WarnAppender" type="log4net.Appender.RollingFileAppender">
<file value="App_Data\\Logs\\" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<datePattern value="yyyy-MM-dd\\'warn.log'" />
<staticLogFileName value="false" />
<param name="MaxSizeRollBackups" value="100" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<param name="Encoding" value="utf-8" />
<maximumFileSize value="5MB"/>
<param name="PreserveLogFileNameExtension" value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%newline %nDATE: %date %nTHREAD: [%thread] %n%message %newline %n" />
</layout>
</appender>
<!--Error 日志-->
<appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
<file value="App_Data\\Logs\\" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<datePattern value="yyyy-MM-dd\\'error.log'" />
<staticLogFileName value="false" />
<param name="MaxSizeRollBackups" value="100" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<param name="Encoding" value="utf-8" />
<maximumFileSize value="5MB"/>
<param name="PreserveLogFileNameExtension" value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%newline %nDATE: %date %nTHREAD: [%thread] level: %-5level %n%message %newline %n" />
</layout>
</appender>
<logger name="logerror">
<level value="ERROR" />
<appender-ref ref="ErrorAppender" />
</logger>
<logger name="logwarn">
<level value="WARN" />
<appender-ref ref="WarnAppender" />
</logger>
<logger name="loginfo">
<level value="INFO" />
<appender-ref ref="InfoAppender" />
</logger>
<logger name="logdebug">
<level value="DEBUG" />
<appender-ref ref="DebugAppender" />
</logger>
<logger name="logmonitor">
<level value="Monitor" />
<appender-ref ref="MonitorAppender" />
</logger>
<logger name="logsocketsend">
<level value="INFO" />
<appender-ref ref="SocketSendAppender" />
</logger>
<logger name="logsocketrec">
<level value="INFO" />
<appender-ref ref="SocketRecAppender" />
</logger>
<logger name="logweather">
<level value="INFO" />
<appender-ref ref="WeatherAppender" />
</logger>
<logger name="logcheckin">
<level value="INFO" />
<appender-ref ref="CheckInAppender" />
</logger>
</log4net>
</configuration>
AssemblyInfo.cs 重点就是最后一行代码
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的常规信息是通过以下项进行控制的
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("UniversalGateway")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("UniversalGateway")]
[assembly: AssemblyCopyright("版权所有(C) 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 将使此程序集中的类型
// 对 COM 组件不可见。如果需要
// COM,在该类型上将 ComVisible 属性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于 typelib 的 ID
[assembly: Guid("a948378f-b639-48e6-a6de-d52c915c9f96")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 内部版本号
// 修订号
//
// 你可以指定所有值,也可以让修订版本和内部版本号采用默认值,
// 方法是按如下所示使用 "*":
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", Watch = true)]