• vue前端开发那些事——后端接口.net core web api


      红花还得绿叶陪衬。vue前端开发离不开数据,这数据正来源于请求web api。为什么采用.net core web api呢?因为考虑到跨平台部署的问题。即使眼下部署到window平台,那以后也可以部署到Linux下。

      .net core web api与mvc的web api类似。我把遇到的问题归纳下:

    1、部署问题

    都说.net core web api,后面我简称api。它有两种部署方式,一个是在iis上部署,另外一个是自托管,类似控制台,通过dotnet  run 命令启动的。

     1.1 自托管部署

    dotnet myapp.dll

    网上说,通过hosting.json

    {
      "server.urls": "http://localhost:60000;http://localhost:60001"
    }

    这种方式有个问题,在配置了urls,并没有走配置。

    public static void Main(string[] args)
    {
        var config = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("hosting.json", optional: true)
            .Build();
    
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseConfiguration(config)
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();
    
        host.Run();
    }

    不过人家是说在Linux环境下的部署,我在window下测试是不行的,不知道是哪的问题,后面可以再研究。

    1.2、iis上部署

    必须首先安装AspNetCoreModule,搜索这个模块,它的描述如下:

    The ASP.NET Core Module allows ASP.NET Core apps to run in an IIS worker process (in-process) or behind IIS in a reverse proxy configuration (out-of-process). IIS provides advanced web app security and manageability features. 

    这句话大意:api有两种运行模式,一种是运行在iis工作进程中(In-process hosting model),另外一种是通过反向代理配置,运行在外(Out-of-process hosting model)。具体,可参考官方文档

    这是文档中 In-process hosting model图,我们可以看出,http请求首先到达kernel-mode HTTP.sys driver,http监听器,监听器把请求给iis,首先是Asp.NET Core Module接受,然后传递给IISHttpServer,它把请求转换为托管代码,进入.net core middelware pipline,最后才是我们的api代码。换句话说,Asp.NET Core Module类似中间件的作用,它先处理的一部分事情。这是我们项目中采取的部署方案,另外一种模式可能比较复杂,大家阅读官方文档。

    2、全局异常处理

    我们知道mvc中,有两种异常处理:

    使用Global.asax的Application_Error事件进行全局异常处理以及使用HandleErrorAttribute特性捕获全局异常

    .net core api中可以编写异常处理的中间件,如下:

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.Logging;
    using Newtonsoft.Json; 
    using System; 
    using System.Collections.Generic; 
    using System.IO; 
    using System.Linq; 
    using System.Net; 
    using System.Threading.Tasks; 
    using System.Xml.Serialization; 
    
    namespace ElectronInfoApi.Business {
        public class GlobalExceptionMiddleware {
            private  readonly RequestDelegate next;
            public GlobalExceptionMiddleware(RequestDelegate next) {
                this.next = next; 
            }
    
            public async Task Invoke(HttpContext context) {
                try {
                    await next(context); 
                }
                catch (Exception ex) {
                    await HandleExceptionAsync(context, ex); 
                }
            }
    
            private  async Task HandleExceptionAsync(HttpContext context, Exception exception) {
                if (exception == null)return; 
                await WriteExceptionAsync(context, exception).ConfigureAwait(false); 
            }
    
            private  async Task WriteExceptionAsync(HttpContext context, Exception exception) {
                //记录日志
                 this.Log().Error($"系统发生了异常:{exception.Message}, {exception.StackTrace}");
                //返回友好的提示
                var response = context.Response; 
    
                //状态码
                if (exception is UnauthorizedAccessException)
                    response.StatusCode = (int)HttpStatusCode.Unauthorized; 
                else if (exception is Exception)
                    response.StatusCode = (int)HttpStatusCode.BadRequest; 
    
                response.ContentType = context.Request.Headers["Accept"];
    
                response.ContentType = "application/json"; 
                await response.WriteAsync(JsonConvert.SerializeObject(new {state=400,message="出现未知异常"})).ConfigureAwait(false); 
            }
    
        }
    
         public static class VisitLogMiddlewareExtensions
        {
            public static IApplicationBuilder UseGlobalException(this IApplicationBuilder builder)
            {
                return builder.UseMiddleware<GlobalExceptionMiddleware>();
            }
        }
    }
    View Code

    在startup>Configure中添加

    app.UseGlobalException();

    官网有文档,是这么定义中间件的:

    Middleware is software that's assembled into an app pipeline to handle requests and responses

    3、安全验证

    接口验证,是为了安全性考虑,采用Jwt(Json web token)。

    第一步,添加包引用:

     <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.2" />

    第二步,配置:

     "Issuer": "ElectronInfo",
     "Audience": "ElectronInfo",
     "SecretKey": "ElectronInfo is a web of shanxi dianzi qingbao weiyuanhui"

    第三步,在Startup>ConfigureServices中添加授权服务:

      var jwtSettings = new JwtSettings(){
                    Issuer=AppSetting.GetConfig("Issuer"),
                    Audience=AppSetting.GetConfig("Audience"),
                    SecretKey=AppSetting.GetConfig("SecretKey"),
                };
    
          services.AddAuthentication(options =>  {
              options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 
              options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 
             })
            .AddJwtBearer(o =>  {
               o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters {
               ValidIssuer = jwtSettings.Issuer, 
               ValidAudience = jwtSettings.Audience, 
               IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey)),
           ValidateIssuerSigningKey = true, 
               ValidateIssuer = true,
           ValidateLifetime = true, 
           ClockSkew = TimeSpan.Zero
               }; 
            }); 

    第四步:在Startup>Configure中添加

      app.UseAuthentication(); 

    第五步:给整个Controller或者需要接口验证的action中添加

     [Authorize]

    附:AppSetting类,读取appsettings.json,如下:

    using System.IO; 
    using Microsoft.Extensions.Configuration; 
    
    namespace ElectronInfoApi.Business {
    public class AppSetting {
        private static readonly object objLock = new object(); 
        private static AppSetting instance = null; 
    
        private IConfigurationRoot Config {get; }
    
        private AppSetting() {
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional:false, reloadOnChange:true); 
            Config = builder.Build(); 
        }
    
        public static AppSetting GetInstance() {
            if (instance == null) {
                lock (objLock) {
                    if (instance == null) {
                        instance = new AppSetting(); 
                    }
                }
            }
    
            return instance; 
        }
    
        public static string GetConfig(string name) {
            return GetInstance().Config.GetSection(name).Value; 
        }
    }}
    View Code

    4、日志log4

        .net core中本来就支持console输出日志。不过今天我要说的是log4,在传统的.net中普遍使用。

        第一步,添加包引用:

     <PackageReference Include="log4net" Version="2.0.8" />

      第二步,添加配置文件log4net.config:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <!-- This section contains the log4net configuration settings -->
      <log4net>
        <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
          <layout type="log4net.Layout.PatternLayout" value="%date [%thread] %-5level %logger - %message%newline" />
        </appender>
        
        <!--<appender name="FileAppender" type="log4net.Appender.FileAppender">
          <file value="log-file.log" />
          <appendToFile value="true" />
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
          </layout>
        </appender> -->
    
        <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
          <file value="logfile/" />
          <appendToFile value="true" />
          <rollingStyle value="Composite" />
          <staticLogFileName value="false" />
          <datePattern value="yyyyMMdd'.log'" />
          <maxSizeRollBackups value="10" />
          <maximumFileSize value="1MB" />
          <layout type="log4net.Layout.PatternLayout">
            <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
          </layout>
        </appender>
    
        <!-- Setup the root category, add the appenders and set the default level -->
        <root>
          <level value="ALL" />
          <appender-ref ref="ConsoleAppender" />
          <!--<appender-ref ref="FileAppender" />-->
          <appender-ref ref="RollingLogFileAppender" />
        </root>
    
      </log4net>
    </configuration>
    View Code

    第三步,包装以及扩展log4,为了更方便使用:

    首先定义一个接口IMLog:

    using System; 
    
    namespace ElectronInfoApi.Business {
    public interface IMLog {
        // Methods
        void Debug(string message); 
        void Error(string message, Exception exception); 
        void Error(string message); 
        void Fatal(string message); 
        void Info(string message); 
        void Warn(string message); 
    }
    public interface IMLog < T >  {
        
    }
    View Code

    再定义包装器Log4NetWapper:

    using System; 
    using log4net; 
    using log4net.Core; 
    
    namespace ElectronInfoApi.Business {
        public class Log4NetWapper:IMLog, IMLog < Log4NetWapper >  {
    
             private ILog  _logger; 
    
               public Log4NetWapper(string loggerName) {
            this._logger = LogManager.GetLogger(Startup.repository.Name, loggerName); 
        }
    
            public void Debug(string message) {
                _logger.Debug(message); 
            }
    
    
            public void Error(string message, Exception exception) {
                _logger.Error(message, exception); 
            }
    
            public void Error(string message) {
                _logger.Error(message); 
            }
    
            public void Fatal(string message) {
                _logger.Fatal(message); 
            }
    
            public void Info(string message) {
                _logger.Info(message); 
            }
    
            public void Warn(string message) {
                _logger.Warn(message); 
            }
        }
    
    
    }
    View Code

    最后定义扩展方法 LogExtensions:

    using System.Collections.Concurrent; 
    
    namespace ElectronInfoApi.Business {
    public static class LogExtensions {
        // Fields
        private static readonly ConcurrentDictionary < string, IMLog > _dictionary = new ConcurrentDictionary < string, IMLog > (); 
    
        // Methods
        public static IMLog Log(this string objectName) {
            if ( ! _dictionary.ContainsKey(objectName)) {
                IMLog log = new Log4NetWapper(objectName); 
                _dictionary.TryAdd(objectName, log); 
            }
            return _dictionary[objectName]; 
        }
    
        public static IMLog Log < T > (this T type) {
            return typeof(T).FullName.Log(); 
        }
    }}
    View Code

    第四步,在Startup中使用:

     public static ILoggerRepository repository {get; set; }
    
     public Startup(IConfiguration configuration) {
         repository = LogManager.CreateRepository("NETCoreRepository"); 
         XmlConfigurator.Configure(repository, new FileInfo("log4net.config"));   
         Configuration = configuration; 
     }
     public IConfiguration Configuration {get; }

    5、.对net core中startup理解,见官方文档

    好了,关于.net core api也是第一次正式使用,就总结到这里。

  • 相关阅读:
    Loadrunner 9.5_webservice(SOAP)性能测试
    oracle分层查询中的start with和connect by(树结构查询)
    解析Nginx负载均衡
    Nginx+tomcat配置集群负载均衡
    基于Nginx反向代理及负载均衡
    什么是反向代理,如何区别反向与正向代理
    软件测试策略
    软件测试策略的制定过程
    php 模拟get和post提交方法[解决ajax跨域问题]
    解决ajax跨域问题的多种方法
  • 原文地址:https://www.cnblogs.com/wangqiang3311/p/10093370.html
Copyright © 2020-2023  润新知