红花还得绿叶陪衬。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 自托管部署

  1. dotnet myapp.dll

网上说,通过hosting.json

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

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

  1. public static void Main(string[] args)
  2. {
  3. var config = new ConfigurationBuilder()
  4. .SetBasePath(Directory.GetCurrentDirectory())
  5. .AddJsonFile("hosting.json", optional: true)
  6. .Build();
  7.  
  8. var host = new WebHostBuilder()
  9. .UseKestrel()
  10. .UseConfiguration(config)
  11. .UseContentRoot(Directory.GetCurrentDirectory())
  12. .UseIISIntegration()
  13. .UseStartup<Startup>()
  14. .Build();
  15.  
  16. host.Run();
  17. }

不过人家是说在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中可以编写异常处理的中间件,如下:

  1. using Microsoft.AspNetCore.Builder;
  2. using Microsoft.AspNetCore.Http;
  3. using Microsoft.Extensions.Logging;
  4. using Newtonsoft.Json;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.IO;
  8. using System.Linq;
  9. using System.Net;
  10. using System.Threading.Tasks;
  11. using System.Xml.Serialization;
  12.  
  13. namespace ElectronInfoApi.Business {
  14. public class GlobalExceptionMiddleware {
  15. private readonly RequestDelegate next;
  16. public GlobalExceptionMiddleware(RequestDelegate next) {
  17. this.next = next;
  18. }
  19.  
  20. public async Task Invoke(HttpContext context) {
  21. try {
  22. await next(context);
  23. }
  24. catch (Exception ex) {
  25. await HandleExceptionAsync(context, ex);
  26. }
  27. }
  28.  
  29. private async Task HandleExceptionAsync(HttpContext context, Exception exception) {
  30. if (exception == null)return;
  31. await WriteExceptionAsync(context, exception).ConfigureAwait(false);
  32. }
  33.  
  34. private async Task WriteExceptionAsync(HttpContext context, Exception exception) {
  35. //记录日志
  36. this.Log().Error($"系统发生了异常:{exception.Message}, {exception.StackTrace}");
  37. //返回友好的提示
  38. var response = context.Response;
  39.  
  40. //状态码
  41. if (exception is UnauthorizedAccessException)
  42. response.StatusCode = (int)HttpStatusCode.Unauthorized;
  43. else if (exception is Exception)
  44. response.StatusCode = (int)HttpStatusCode.BadRequest;
  45.  
  46. response.ContentType = context.Request.Headers["Accept"];
  47.  
  48. response.ContentType = "application/json";
  49. await response.WriteAsync(JsonConvert.SerializeObject(new {state=,message="出现未知异常"})).ConfigureAwait(false);
  50. }
  51.  
  52. }
  53.  
  54. public static class VisitLogMiddlewareExtensions
  55. {
  56. public static IApplicationBuilder UseGlobalException(this IApplicationBuilder builder)
  57. {
  58. return builder.UseMiddleware<GlobalExceptionMiddleware>();
  59. }
  60. }
  61. }

在startup>Configure中添加

  1. app.UseGlobalException();

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

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

3、安全验证

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

第一步,添加包引用:

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

第二步,配置:

  1. "Issuer": "ElectronInfo",
  2. "Audience": "ElectronInfo",
  3. "SecretKey": "ElectronInfo is a web of shanxi dianzi qingbao weiyuanhui"

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

  1. var jwtSettings = new JwtSettings(){
  2. Issuer=AppSetting.GetConfig("Issuer"),
  3. Audience=AppSetting.GetConfig("Audience"),
  4. SecretKey=AppSetting.GetConfig("SecretKey"),
  5. };
  6.  
  7. services.AddAuthentication(options => {
  8. options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
  9. options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
  10. })
  11. .AddJwtBearer(o => {
  12. o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters {
  13. ValidIssuer = jwtSettings.Issuer,
  14. ValidAudience = jwtSettings.Audience,
  15. IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey)),
  16.        ValidateIssuerSigningKey = true,
  17. ValidateIssuer = true,
  18.        ValidateLifetime = true,
  19.        ClockSkew = TimeSpan.Zero
  20. };
  21. });

第四步:在Startup>Configure中添加

  1. app.UseAuthentication();

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

  1. [Authorize]

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

  1. using System.IO;
  2. using Microsoft.Extensions.Configuration;
  3.  
  4. namespace ElectronInfoApi.Business {
  5. public class AppSetting {
  6. private static readonly object objLock = new object();
  7. private static AppSetting instance = null;
  8.  
  9. private IConfigurationRoot Config {get; }
  10.  
  11. private AppSetting() {
  12. var builder = new ConfigurationBuilder()
  13. .SetBasePath(Directory.GetCurrentDirectory())
  14. .AddJsonFile("appsettings.json", optional:false, reloadOnChange:true);
  15. Config = builder.Build();
  16. }
  17.  
  18. public static AppSetting GetInstance() {
  19. if (instance == null) {
  20. lock (objLock) {
  21. if (instance == null) {
  22. instance = new AppSetting();
  23. }
  24. }
  25. }
  26.  
  27. return instance;
  28. }
  29.  
  30. public static string GetConfig(string name) {
  31. return GetInstance().Config.GetSection(name).Value;
  32. }
  33. }}

4、日志log4

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

第一步,添加包引用:

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

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

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <!-- This section contains the log4net configuration settings -->
  4. <log4net>
  5. <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
  6. <layout type="log4net.Layout.PatternLayout" value="%date [%thread] %-5level %logger - %message%newline" />
  7. </appender>
  8.  
  9. <!--<appender name="FileAppender" type="log4net.Appender.FileAppender">
  10. <file value="log-file.log" />
  11. <appendToFile value="true" />
  12. <layout type="log4net.Layout.PatternLayout">
  13. <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
  14. </layout>
  15. </appender> -->
  16.  
  17. <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
  18. <file value="logfile/" />
  19. <appendToFile value="true" />
  20. <rollingStyle value="Composite" />
  21. <staticLogFileName value="false" />
  22. <datePattern value="yyyyMMdd'.log'" />
  23. <maxSizeRollBackups value="10" />
  24. <maximumFileSize value="1MB" />
  25. <layout type="log4net.Layout.PatternLayout">
  26. <conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
  27. </layout>
  28. </appender>
  29.  
  30. <!-- Setup the root category, add the appenders and set the default level -->
  31. <root>
  32. <level value="ALL" />
  33. <appender-ref ref="ConsoleAppender" />
  34. <!--<appender-ref ref="FileAppender" />-->
  35. <appender-ref ref="RollingLogFileAppender" />
  36. </root>
  37.  
  38. </log4net>
  39. </configuration>

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

首先定义一个接口IMLog:

  1. using System;
  2.  
  3. namespace ElectronInfoApi.Business {
  4. public interface IMLog {
  5. // Methods
  6. void Debug(string message);
  7. void Error(string message, Exception exception);
  8. void Error(string message);
  9. void Fatal(string message);
  10. void Info(string message);
  11. void Warn(string message);
  12. }
  13. public interface IMLog < T > {
  14.  
  15. }

再定义包装器Log4NetWapper:

  1. using System;
  2. using log4net;
  3. using log4net.Core;
  4.  
  5. namespace ElectronInfoApi.Business {
  6. public class Log4NetWapper:IMLog, IMLog < Log4NetWapper > {
  7.  
  8. private ILog _logger;
  9.  
  10. public Log4NetWapper(string loggerName) {
  11. this._logger = LogManager.GetLogger(Startup.repository.Name, loggerName);
  12. }
  13.  
  14. public void Debug(string message) {
  15. _logger.Debug(message);
  16. }
  17.  
  18. public void Error(string message, Exception exception) {
  19. _logger.Error(message, exception);
  20. }
  21.  
  22. public void Error(string message) {
  23. _logger.Error(message);
  24. }
  25.  
  26. public void Fatal(string message) {
  27. _logger.Fatal(message);
  28. }
  29.  
  30. public void Info(string message) {
  31. _logger.Info(message);
  32. }
  33.  
  34. public void Warn(string message) {
  35. _logger.Warn(message);
  36. }
  37. }
  38.  
  39. }

最后定义扩展方法 LogExtensions:

  1. using System.Collections.Concurrent;
  2.  
  3. namespace ElectronInfoApi.Business {
  4. public static class LogExtensions {
  5. // Fields
  6. private static readonly ConcurrentDictionary < string, IMLog > _dictionary = new ConcurrentDictionary < string, IMLog > ();
  7.  
  8. // Methods
  9. public static IMLog Log(this string objectName) {
  10. if ( ! _dictionary.ContainsKey(objectName)) {
  11. IMLog log = new Log4NetWapper(objectName);
  12. _dictionary.TryAdd(objectName, log);
  13. }
  14. return _dictionary[objectName];
  15. }
  16.  
  17. public static IMLog Log < T > (this T type) {
  18. return typeof(T).FullName.Log();
  19. }
  20. }}

第四步,在Startup中使用:

  1. public static ILoggerRepository repository {get; set; }
  2.  
  3. public Startup(IConfiguration configuration) {
  4. repository = LogManager.CreateRepository("NETCoreRepository");
  5. XmlConfigurator.Configure(repository, new FileInfo("log4net.config"));
  6. Configuration = configuration;
  7. }
  8. public IConfiguration Configuration {get; }

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

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

vue前端开发那些事——后端接口.net core web api的更多相关文章

  1. vue前端开发那些事——vue组件开发

    vue的学习曲线不是很陡(相比其它框架,如anglarjs),官方文档比较全面,分为基础篇和高级篇.我们刚开始学习的时候,肯定像引用jquery那样,先把vue的js引进来,然后学习基础内容.如果仅仅 ...

  2. vue前端开发那些事——vue开发遇到的问题

    vue web开发并不是孤立的.它需要众多插件的配合以及其它js框架的支持.本篇想把vue web开发的一些问题,拿出来讨论下.  1.web界面采用哪个UI框架?项目中引用了layui框架.引入框架 ...

  3. vue前端开发那些事——前言

    如上图所示,用vue开发一个小型网站所涉及到的知识点.这只是前端部分已经这么多了.接下来我分解开来说. 1.Node 当我们开发vue项目的时候,首先要安装Node.js,那么我们即使当时不理解为什么 ...

  4. vue前端开发那些事(1)

    如上图所示,用vue开发一个小型网站所涉及到的知识点.这只是前端部分已经这么多了.接下来我分解开来说. 1.Node 当我们开发vue项目的时候,首先要安装Node.js,那么我们即使当时不理解为什么 ...

  5. List多个字段标识过滤 IIS发布.net core mvc web站点 ASP.NET Core 实战:构建带有版本控制的 API 接口 ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目 Using AutoFac

    List多个字段标识过滤 class Program{  public static void Main(string[] args) { List<T> list = new List& ...

  6. ASP.NET Core 实战:使用 ASP.NET Core Web API 和 Vue.js 搭建前后端分离项目

    一.前言 这几年前端的发展速度就像坐上了火箭,各种的框架一个接一个的出现,需要学习的东西越来越多,分工也越来越细,作为一个 .NET Web 程序猿,多了解了解行业的发展,让自己扩展出新的技能树,对自 ...

  7. C#开发微信门户及应用(47) - 整合Web API、微信后台管理及前端微信小程序的应用方案

    在微信开发中,我一直强调需要建立一个比较统一的Web API接口体系,以便实现数据的集中化,这样我们在常规的Web业务系统,Winform业务系统.微信应用.微信小程序.APP等方面,都可以直接调用基 ...

  8. 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入

    使用react全家桶制作博客后台管理系统   前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...

  9. 购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(2)--前端,以及前后端Session

    原文:购物车Demo,前端使用AngularJS,后端使用ASP.NET Web API(2)--前端,以及前后端Session chsakell分享了前端使用AngularJS,后端使用ASP.NE ...

随机推荐

  1. launch 文件解析

    roslaunch工具是ros中python实现的程序启动工具,通过读取launch文件中的参数配置.属性配置等来启动一系列节点: 很多ROS包或源码包中都有launch文件,一般为该程序包能够运行起 ...

  2. Zabbix JVM 安装

    Zabbix 服务端安装插件 系统:centos 7.4 x64 环境:zabbix 3.0.16 yum源:rpm -ivh http://repo.zabbix.com/zabbix/3.0/rh ...

  3. CSS3 文本常用属性

    CSS 常用属性 text-shadow属性文字阴影:第一个值背景相对原本文字居左的距离,第二个值据当前文本上方的距离,第三个值清晰度(越小越清晰),第四个值颜色 word-wrap:自动换行,如果是 ...

  4. HTML5抽奖转盘

    在线演示 本地下载

  5. 20165101刘天野 2017-2018-2 《Java程序设计》 结对编程练习_四则运算(第二周)

    20165101刘天野 2017-2018-2 <Java程序设计> 结对编程练习_四则运算(第二周) 一.需求分析 能随机生成n道四则运算题目,n由使用者输入 支持分数运算 支持多运算符 ...

  6. libc.so.6(GLIBC_2.14)(64bit) is needed by MySQL

    记一次粗心大意!解决办法在最下面! rpm安装MySQL时提升如下: warning: MySQL-client-5.6.41-1.el7.x86_64.rpm: Header V3 DSA/SHA1 ...

  7. NumPy算数运算

    NumPy - 算数运算 用于执行算术运算(如add(),subtract(),multiply()和divide())的输入数组必须具有相同的形状或符合数组广播规则. 示例 import numpy ...

  8. 判断浏览器是否支持某一个CSS3属性

    判断浏览器是否支持某一个CSS3属性 function supportCss3(style) { var prefix = ['webkit', 'Moz', 'ms', 'o'], i, humpS ...

  9. 全国城市部分js

    var areaJson22 = { "id": "0", "name": "全国", "parentId&q ...

  10. legend---十二、js中的js语句和函数和ready函数的关系是什么

    legend---十二.js中的js语句和函数和ready函数的关系是什么 一.总结 一句话总结: 函数和全局变量不必放到ready函数中 语句(调用函数和全局变量)的必须放到ready函数中 1.在 ...