ASP.NET前后端分离,WebApi。Vue3+ElementPlus+Axios+Pinia全流程教程

发布于:2024-04-30 ⋅ 阅读:(28) ⋅ 点赞:(0)

前言

1、.net core 执行过程

img

2、中间件的执行过程

img

3、AOP切面编程

img

img

Swagger

添加Swagger注释

1、右击项目->选择属性->点击生成->输出,选中文档文件
在这里插入图片描述

2、配置服务

program.cs 文件里配置SwaggerUI

//增加项一
builder.Services.AddSwaggerGen(c=> {
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Web API", Version = "v1" });
    var xmlFile = $"{Assembly.GetEntryAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);
});
//增加项二
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
    }); 
}

3、在控制器的方法上加上注释即可在swagger网页上看到注释

在这里插入图片描述

在这里插入图片描述

4、添加实体类的Swagger注释

在这里插入图片描述

在这里插入图片描述

JWT

1、解析

1)客户端向授权服务系统发起请求,申请获取“令牌”。

2)授权服务根据用户身份,生成一张专属“令牌”,并将该“令牌”以JWT规范返回给客户端

3)客户端将获取到的“令牌”放到http请求的headers中后,向主服务系统发起请求。主服务系统收到请求后会从headers中获取“令牌”,并从“令牌”中解析出该用户的身份权限,然后做出相应的处理(同意或拒绝返回资源)

2、配置JWT

1、添加NuGet包Microsoft.AspNetCore.Authentication.JwtBearer

2、在appsettings.json中添加JWT配置节点

   "JWT": {
    "SecKey": "Jamin1127!#@$%@%^^&*(~Czmjklneafguvioszb%yuv&*6WVDf5dw#5dfw6f5w6faW%FW^f5wa65f^AWf56", //密钥
    "Issuer": "Jamin",  //发行者
    "ExpireSeconds": 7200 //过期时间
  }

3、在Program类里进行服务注册

#region JWT服务
// 注册JWT服务
builder.Services.AddSingleton(new JwtHelper(builder.Configuration));

builder.Services.AddAuthentication( JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true, //是否验证Issuer
        ValidIssuer = builder.Configuration["Jwt:Issuer"], //发行人Issuer
        ValidateAudience = false, //是否验证Audience      
        ValidateIssuerSigningKey = true, //是否验证SecurityKey
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecKey"])), //SecurityKey
        ValidateLifetime = true, //是否验证失效时间
        ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
        RequireExpirationTime = true,
    };
}
);
builder.Services.AddAuthorization(options =>
{
/***    "Client" 策略要求用户必须拥有 "Client" 角色才能访问相关资源。
"Admin" 策略要求用户必须拥有 "Admin" 角色才能访问相关资源。
"SystemOrAdmin" 策略要求用户必须拥有 "Admin" 或者 "System" 角色之一才能访问相关资源。***/
    options.AddPolicy("Client", policy => policy.RequireRole("Client").Build());
    options.AddPolicy("Admin", policy => policy.RequireRole("Admin").Build());
    options.AddPolicy("SystemOrAdmin", policy => policy.RequireRole("Admin", "System"));
});
#endregion
//swagger里添加JWT授权
    builder.Services.AddSwaggerGen(c=> {
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Web API", Version = "v1" });
    //开启注释
    var xmlFile = $"{Assembly.GetEntryAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath, true);
    // 配置 JWT Bearer 授权
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT Authorization header using the Bearer scheme",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.Http,
        Scheme = "bearer"
    });

    var securityScheme = new OpenApiSecurityScheme
    {
        Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
    };
    var securityRequirement = new OpenApiSecurityRequirement { { securityScheme, new string[] { } } };
    c.AddSecurityRequirement(securityRequirement);
});
//启用验证中间件
app.UseAuthentication();
app.UseAuthorization();

4、创建JWT类进行Token配置

using Microsoft.IdentityModel.Tokens;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace Blog.core.Common.Auth
{
    /// <summary>
    /// 授权JWT类
    /// </summary>
    public class JwtHelper
    {
        private readonly IConfiguration _configuration;

        /// <summary>
        /// Token配置
        /// </summary>
        /// <param name="configuration"></param>
        public JwtHelper(IConfiguration configuration)
        {
            _configuration = configuration;
        }
        
        /// <summary>
        /// 创建Token 这里面可以保存自己想要的信息
        /// </summary>
        /// <param name="username"></param>
        /// <param name="mobile"></param>
        /// <returns></returns>
        public string CreateToken(string username, string mobile)
        {
            try
            {
                // 1. 定义需要使用到的Claims
                var claims = new[]
                {
                    new Claim("username", username),
                    new Claim("mobile", mobile),
                    /* 可以保存自己想要信息,传参进来即可
                    new Claim("sex", "sex"),
                    new Claim("limit", "limit"),
                    new Claim("head_url", "xxxxx")
                    */
                };

                // 2. 从 appsettings.json 中读取SecretKey
                var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecKey"]));

                // 3. 选择加密算法
                var algorithm = SecurityAlgorithms.HmacSha256;

                // 4. 生成Credentials
                var signingCredentials = new SigningCredentials(secretKey, algorithm);

                // 5. 根据以上,生成token
                var jwtSecurityToken = new JwtSecurityToken(
                    _configuration["Jwt:Issuer"],    //Issuer
                    _configuration["Jwt:ExpireSeconds"],  //ExpireSeconds
                    claims,                          //Claims,
                    DateTime.Now,                    //notBefore
                    DateTime.Now.AddSeconds(30),     //expires
                    signingCredentials               //Credentials
                );

                // 6. 将token变为string
                var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);

                return token;
            }
            catch (Exception)
            {

                throw;
            }
           
        }
        /// <summary>
        /// 获取信息
        /// </summary>
        /// <param name="jwt"></param>
        /// <returns></returns>
        public static string ReaderToken(string jwt)
        {
            var str = string.Empty;
            try
            {
                //获取Token的三种方式
                //第一种直接用JwtSecurityTokenHandler提供的read方法
                var jwtHander = new JwtSecurityTokenHandler();
                JwtSecurityToken jwtSecurityToken = jwtHander.ReadJwtToken(jwt);
                str = jwtSecurityToken.ToString();
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
            return str;
        }

        /// <summary>
        /// 解密jwt
        /// </summary>
        /// <param name="jwt"></param>
        /// <returns></returns>
        public string JwtDecrypt(string jwt)
        {
           
            StringBuilder sb = new StringBuilder();
            try
            {
                JwtSecurityTokenHandler tokenHandler = new();
                TokenValidationParameters valParam = new();
                var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecKey"]));
                valParam.IssuerSigningKey = securityKey;
                valParam.ValidateIssuer = false;
                valParam.ValidateAudience = false;
                //解密
                ClaimsPrincipal claimsPrincipal = tokenHandler.ValidateToken(jwt,
                        valParam, out SecurityToken secToken);
                foreach (var claim in claimsPrincipal.Claims)
                {
                    sb.Append($"{claim.Type}={claim.Value}");
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
            return sb.ToString();
        }
    }

}

5、创建用户实体,进行用户密码的接收

using System.ComponentModel.DataAnnotations;

namespace Blog.core.Models
{
    public class UserInfo
    {
        /// <summary>
        /// 其中 [Required] 表示非空判断,其他自己研究百度
        /// </summary>
        [Required]
        public string UserName { get; set; }
        [Required]
        public string Password { get; set; }
        [Required]
        public string PhoneNumber { get; set; }

}
}

6、创建控制器,进行JWT的APi调用

using Blog.core.Common.Auth;
using Blog.core.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Blog.core.Controllers
{
   
        [Route("[controller]/[action]")]
        [ApiController]
        public class UserController : ControllerBase
        {
            private readonly JwtHelper _jwt;

            /// <summary>
            /// 初始化
            /// </summary>
            /// <param name="jwtHelper"></param>
            public UserController(JwtHelper jwtHelper)
            {
                _jwt = jwtHelper;
            }
            /// <summary>
            /// 获取Token
            /// </summary>
            /// <returns></returns>
            [HttpPost]
            public IActionResult GetToken(UserInfo user)
            {
                //参数验证等等....
                if (string.IsNullOrEmpty(user.UserName))
                {
                    return Ok("参数异常!");
                }

                //这里可以连接mysql数据库做账号密码验证


                //这里可以做Redis缓存验证等等


                //这里获取Token,当然,这里也可以选择传结构体过去
                var token = _jwt.CreateToken(user.UserName, user.PhoneNumber);
                  //解密后的Token
                  var PWToken = _jwt.JwtDecrypt( token);
                  return Ok(token+"解密后:"+PWToken);
     
            }

            /// <summary>
            /// 获取自己的详细信息,其中 [Authorize] 就表示要带Token才行
            /// </summary>
            /// <returns></returns>
            [HttpPost]
            [Authorize]
            public IActionResult GetSelfInfo()
            {
                //执行到这里,就表示已经验证授权通过了
                /*
                 * 这里返回个人信息有两种方式
                 * 第一种:从Header中的Token信息反向解析出用户账号,再从数据库中查找返回
                 * 第二种:从Header中的Token信息反向解析出用户账号信息直接返回,当然,在前面创建        Token时,要保存进使用到的Claims中。
                */
                return Ok("授权通过了!");
            }
        }  
}

7、用来验证权限的控制器

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Blog.core.Controllers
{
   
    [ApiController]
    [Route("[controller]")]
    [Authorize(Roles ="Admin")]
    [Authorize(Roles ="User")]//增加权限验证
    public class WeatherForecastController : ControllerBase 
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;    
        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }
        /// <summary>
        /// 天气
        /// </summary>
        /// <remark></remark>
        [HttpGet]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
       
    }
}

在这里插入图片描述

**注:获取Token后在Swagger上输入token的value就可以进行接口的调用了 **

在这里插入图片描述

配置SqlSugar

0、引入SqlSugarCore

1、编写Context

  public static SqlSugarClient db = new SqlSugarClient(
                  new ConnectionConfig()
                  {
                      ConnectionString = "server = 127.0.0.1; Database = test; Uid = root; Pwd = root; AllowLoadLocalInfile = true;",
                      DbType = DbType.MySql,//设置数据库类型
                      IsAutoCloseConnection = true,//自动释放数据务,如果存在事务,在事务结束后释放
                  });

2、配置实体类

using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Blog.Core.Model.Models
{
    [SugarTable(tableName: "Person")]
    public class User
    {
        [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
        public int Id { get; set; }
        public int Age { get; set; }
        public string? Name { get; set; }

    }
}

3、创建Service服务类进行数据库的CRUD

using Blog.core.IRepository;
using Blog.Core.Model.Models;
using static Blog.Core.Common.DbContext;
namespace Blog.Core.Repository
{
    public class UserRepository : IUserRepository
    {
        public  int Add(User user)
        {
            var line = db.Insertable(user).ExecuteCommand();
            return line;
        }

        public int Delete(int UserId)
        {
            var line = db.Deleteable<User>(new User
            {
                Id = UserId
            }).ExecuteCommand();
            return line;
        }

        public List<User> GetUsers(int Id)
        {
            List<User> users;
            if (Id is not 0)
            {
                users = db.Queryable<User>().Where(it => it.Id == Id).ToList();
            }
            else
            {
                users = db.Queryable<User>().ToList();
            }
            return users;
        }

        public int Update(User user)
        {
            var res = db.Updateable<User>(user).ExecuteCommand();
            return res;
        }
    }
}

4、配置Controller进行路由

using Blog.core.Models;
using Blog.Core.Auth;
using Blog.Core.IServices;
using Blog.Core.Model.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Blog.Core.Services;
namespace Blog.core.Controllers
{

    [Route("[controller]/[action]")]
        [ApiController]
        public class UserController : ControllerBase
        {

            private readonly IUserService _userService;
            public UserController( IUserService userService)
                {
                    _userService = userService;
                }

                /// <summary>
                /// 增加
                /// </summary>
                /// <param name="user"></param>
                /// <returns></returns>
                [HttpPost]
                public int AddUser(User user)
                {
                    // User user = new User() { Id = 2024325, Name = "Czm", Age = 20 };

                    return _userService.Add(user);
                }
                /// <summary>
                /// 删除
                /// </summary>
                /// <param name="id"></param>
                /// <returns></returns>
                [HttpDelete]
                public int DeleteUser(int id)
                {
                    return _userService.Delete(id);
                }
                /// <summary>
                /// 更新
                /// </summary>
                /// <param name="user"></param>
                /// <returns></returns>
                [HttpPut]
                public int UpdateUsre(User user)
                {
                    return _userService.Update(user);
                }
                /// <summary>
                /// 获取数据
                /// </summary>
                /// <param name="id"></param>
                /// <returns></returns>
                [HttpGet]
                public List<User> GetUser(int id)
                {
                    return _userService.GetUsers(id);
                }
    }
}

依赖注入与IOC

IOC

IOC 是 Inversion of Control(控制反转)的缩写。在软件开发中,IOC 是一种设计模式,它改变了传统的程序设计流程,使得对象之间的依赖关系由代码本身控制变为由外部容器控制。

而采用IOC 设计模式后,对象之间的依赖关系由外部容器来管理和注入,对象本身不需要关心依赖的具体实现,只需要定义自己的接口或抽象类,并在外部容器中配置依赖关系。这样可以降低代码的耦合度,提高代码的灵活性、可维护性和可扩展性。

常见的IOC 容器包括 Spring Framework 中的 Spring IoC ,dotnet中的autofoc,它通过依赖注入(Dependency Injection)的方式来实现控制反转。通过IOC 容器,可以将对象之间的依赖关系集中管理,实现了代码的松耦合,使得程序更易于理解、扩展和维护。

依赖注入DI

1、继承接口并实现构造方法

 public class UserService : IUserService
 {
     private  IUserRepository _userService ;

     public UserService(IUserRepository userService)
     {
         _userService = userService;
     }
 }

2、在program里加上

builder.Services.AddTransient<IUserRepository, UserRepository>();
builder.Services.AddTransient<IUserService, UserService>();

.net提供了三种生命周期的容器

builder.Services.AddTransient<IOperationTransient, Operation>(); builder.Services.AddScoped<IOperationScoped, Operation>(); builder.Services.AddSingleton<IOperationSingleton, Operation>();

  • 暂时性对象始终不同。 IndexModel 和中间件中的临时 OperationId 值不同。
  • 范围内对象对给定请求而言是相同的,但在每个新请求之间不同。
  • 单一实例对象对于每个请求是相同的。

3、Controller层使用

 public class UserController : ControllerBase
 {

     private readonly IUserService _userService ;

     public UserController(IUserService userService)
     {
         _userService = userService;
     }
 }

Autofac轻量容器的使用

1、安装Nuget包Autofac.Extensions.DependencyInjectionAutofac.Extras.DynamicProxy

2、使用程序集注册,通过Model注册(这里只列这一种Auto官方文档Assembly Scanning — Autofac 7.0.0 documentation

**创建Model类 **

using Autofac;
using Blog.Core.IServices;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyModel;
using System.Reflection;
using System.Runtime.Loader;

namespace Blog.Core.Configuration.AutoModule
{
    public class ServiceModel: Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            // 自动对集成 IDependency 接口的类进行注册
            Type baseType = typeof(IUserService);
            var compilationLibrary = DependencyContext.Default.CompileLibraries.Where(x => !x.Serviceable && x.Type == "project").ToList();

            List<Assembly> assemblyList = new List<Assembly>();
            foreach (var _compilation in compilationLibrary)
            {
                try
                {
                    assemblyList.Add(AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(_compilation.Name)));
                }
                catch (Exception ex)
                {
                    Console.WriteLine(_compilation.Name + ex.Message);
                }
            }
            builder.RegisterAssemblyTypes(assemblyList.ToArray()).Where(type => baseType.IsAssignableFrom(type) && !type.IsAbstract)
                .AsSelf()
                .AsImplementedInterfaces()
                .PropertiesAutowired()
                .InstancePerLifetimeScope();

            var controllersTypesInAssembly = typeof(Program).Assembly.GetExportedTypes()
                .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray();

            builder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired();

        }
    }
}

**在program.cs中解析Model **

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(i => i.RegisterModule<ServiceModel>());
builder.Services.AddControllers().AddControllersAsServices();

在Controller中使用

public class UserController : ControllerBase
{

    private readonly IUserService _userService ;

    public UserController(IUserService userService)
    {
        _userService = userService;
    }
}

使用Autofac完成AOP日志

1、编写AOP类

using Castle.DynamicProxy;
using Newtonsoft.Json;
using StackExchange.Profiling;
using System.Reflection;

namespace Blog.Core.AOP
{
    /// <summary>
    /// 拦截器BlogLogAOP 继承IInterceptor接口
    /// </summary>
    public class BlogLogAOP : IInterceptor
    { 

        /// <summary>
        /// 实例化IInterceptor唯一方法
        /// </summary>
        /// <param name="invocation">包含被拦截方法的信息</param>
        public void Intercept(IInvocation invocation)
        {
            string UserName = "Jamin";

            //记录被拦截方法信息的日志信息
            var dataIntercept = "" +
                $"【当前操作用户】:{UserName} \r\n" +
                $"【当前执行方法】:{invocation.Method.Name} \r\n" +
                $"【携带的参数有】: {string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray())} \r\n";

            try
            {
                MiniProfiler.Current.Step($"执行Service方法:{invocation.Method.Name}() -> ");
                //在被拦截的方法执行完毕后 继续执行当前方法,注意是被拦截的是异步的
                invocation.Proceed();


                // 异步获取异常,先执行
                if (IsAsyncMethod(invocation.Method))
                {

                    //Wait task execution and modify return value
                    if (invocation.Method.ReturnType == typeof(Task))
                    {
                        invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithPostActionAndFinally(
                            (Task)invocation.ReturnValue,
                            ex =>
                            {
                                LogEx(ex, ref dataIntercept);
                            });
                    }
                    else //Task<TResult>
                    {
                        invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult(
                         invocation.Method.ReturnType.GenericTypeArguments[0],
                         invocation.ReturnValue,
                         ex =>
                         {
                             LogEx(ex, ref dataIntercept);
                         });

                    }

                }
                else
                {// 同步1           
                 
                }
            }
            catch (Exception ex)// 同步2
            {
                LogEx(ex, ref dataIntercept);

            }

            var type = invocation.Method.ReturnType;
            if (typeof(Task).IsAssignableFrom(type))
            {
                var resultProperty = type.GetProperty("Result");
                dataIntercept += ($"【执行完成结果】:{JsonConvert.SerializeObject(resultProperty.GetValue(invocation.ReturnValue))}");
            }
            else
            {
                dataIntercept += ($"【执行完成结果】:{invocation.ReturnValue}");
            }

            // 你的日志记录 比如log4
            #region 输出到当前项目日志
            var path = Directory.GetCurrentDirectory() + @"\Log";
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            string fileName = path + $@"\InterceptLog-{DateTime.Now.ToString("yyyyMMddHHmmss")}.log";

            StreamWriter sw = File.AppendText(fileName);
            sw.WriteLine(dataIntercept);
            sw.Close();
            #endregion
        }

        private void LogEx(Exception ex, ref string dataIntercept)
        {
            if (ex != null)
            {
                //执行的 service 中,收录异常
                MiniProfiler.Current.CustomTiming("Errors:", ex.Message);
                //执行的 service 中,捕获异常
                dataIntercept += ($"方法执行中出现异常:{ex.Message + ex.InnerException}\r\n");
            }
        }

        public static bool IsAsyncMethod(MethodInfo method)
        {
            return (
                method.ReturnType == typeof(Task) ||
                (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
                );
        }

    }

    internal static class InternalAsyncHelper
    {
        public static async Task AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Action<Exception> finalAction)
        {
            Exception exception = null;

            try
            {
                await actualReturnValue;
            }
            catch (Exception ex)
            {
                exception = ex;
            }
            finally
            {
                finalAction(exception);
            }
        }

        public static async Task<T> AwaitTaskWithPostActionAndFinallyAndGetResult<T>(Task<T> actualReturnValue, Func<Task> postAction, Action<Exception> finalAction)
        {
            Exception exception = null;

            try
            {
                var result = await actualReturnValue;
                await postAction();
                return result;
            }
            catch (Exception ex)
            {
                exception = ex;
                throw;
            }
            finally
            {
                finalAction(exception);
            }
        }

        public static object CallAwaitTaskWithPostActionAndFinallyAndGetResult(Type taskReturnType, object actualReturnValue, Action<Exception> finalAction)
        {
            return typeof(InternalAsyncHelper)
                .GetMethod("AwaitTaskWithPostActionAndFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static)
                .MakeGenericMethod(taskReturnType)
                .Invoke(null, [actualReturnValue, finalAction]);
        }
    }
}

2、进行服务注册

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>(build =>
{
    // AOP 
    var cacheType = new List<Type>();
    build.RegisterType<BlogLogAOP>();    
    cacheType.Add(typeof(BlogLogAOP));

    // 获取 Service.dll 程序集服务,并注册
    var assemblysServices = Assembly.LoadFrom(servicesDllFile);
    build.RegisterAssemblyTypes(assemblysServices)
                .AsImplementedInterfaces()
                .InstancePerDependency()
                .EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy;
                .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。

    // 获取 Repository.dll 程序集服务,并注册
    var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
    build.RegisterAssemblyTypes(assemblysRepository)
            .AsImplementedInterfaces()
            .InstancePerDependency();

   
});

Webapi的操作返回值和方法参数

返回值ActionResult

ASP.NET Core Web API中的操作方法的返回值如果是普通数据类型,那么返回值就会默认被序列化为JSON格式的响应报文体返回。

1  [HttpGet("{id}")] 
2  public ActionResult<Person> GetPerson(int id) 
3  { 
4     if (id <= 0) 
5     return person;
10    else 
11       return NotFound(new ErrorInfo(2,"人员不存在")); 
12 }

**注:**一般是创建一个ResInfo类{code,message}作为返回的实体。符合Restful开发

操作方法的参数

我们在给服务器端传递参数的时候,有URL、QueryString、请求报文体3种方式。

URL

如果GetAll方法的参数中有同名的参数,那么这个参数就会被自动赋值。如果捕捉的占位符的名字和参数名一致,那么我们就不需要为参数添加[FromRoute];如果占位符的名字和参数名不一致,我们就需要为参数添加[FromRoute],并且通过[FromRoute]的Name属性来设置匹配的占位符的名字,比如一个名字为classNum的参数要想获得占位符中{classNo}的值,那么我们就要为classNum参数添加[FromRoute(Name=“classNo”)]

1  [HttpGet("school/{schoolName}/class/{classNo}")] 
2  public ActionResult<Student[]> GetAll(string schoolName, 
3                       [FromRoute(Name ="classNo")]string classNum)

QueryString

对于通过QueryString传递的参数,我们使用[FromQuery]来获取值。如果操作方法的参数的名字和我们要获取的QueryString的名字一致,我们只要为参数添加[FromQuery]即可;如果操作方法的参数的名字和要获取的QueryString的名字不一致,我们就要为设定【FromQuery】的Name属性指定和QueryString中一样的名字。

1  public ActionResult<Student[]> GetAll([FromQuery]string pageNum, 
2                             [FromQuery(Name = "pSize")]int pageSize)

URL和QueryString混用

1  [HttpGet("school/{schoolName}/class/{classNo}")] 
2  public ActionResult<Student[]> GetAll(string schoolName, 
3          [FromRoute(Name ="classNo")]string classNum, 
4           [FromQuery]string pageNum,[FromQuery(Name = "pSize")]int pageSize)

请求报文体

参数为实体类,在请求报文体(Body)里发送相应的Json数据就可以直接接收

1  [HttpPost] 
2  public ActionResult AddNew(Student s)

总结

对于GET、DELETE等请求,我们尽量从URL或者QueryString中获取数据;对于PUT、POST等请求,我们尽量通过JSON格式的报文体获取数据,当然我们一定要设定请求报文头中的Content-Type的值为application/json。

VUE项目结构

project-name/
│
├── public/
│   ├── index.html         # 主HTML文件
│   └── ...
│
├── src/
│   ├── assets/            # 静态资源,如图片、字体等
│   ├── components/        # Vue组件
│   ├── views/             # 页面级组件
│   ├── router/            # 路由配置
│   ├── store/             # Vuex状态管理相关文件
│   ├── utils/             # 工具函数、帮助类等
│   ├── styles/            # 全局样式文件
│   ├── App.vue            # 根组件
│   └── main.js            # 入口文件
│
├── tests/                 # 测试文件夹
│
├── node_modules/          # 依赖的第三方包
│
├── .gitignore             # Git忽略文件配置
├── babel.config.js        # Babel配置文件
├── package.json           # 项目配置及依赖管理文件
└── README.md              # 项目说明文件
    

public 目录包含了静态资源,例如 HTML 入口文件和网站图标。
src 目录是主要的工作目录,包含了所有的源代码。
assets 存放应用中使用的静态资源,例如图片、字体等。
components 包含了应用中的 Vue 组件。
views 包含了页面级别的 Vue 组件。
router 包含了 Vue Router 的路由配置。
store 包含了 Vuex 的状态管理相关文件。
styles 包含了全局样式文件。
utils 包含了应用中可能用到的工具函数。
App.vue 是根组件,通常包含应用的整体结构和布局。
main.js 是应用的入口文件,用于初始化 Vue 实例和加载其他组件。
tests 目录包含应用的测试文件。
.gitignore 是 Git 忽略文件配置,指定不需要被版本控制的文件和目录。
babel.config.js 是 Babel 的配置文件,用于指定 JavaScript 代码的转换规则。
package.json 是项目的配置文件,包含了项目的依赖和一些脚本命令。
README.md 是项目的说明文件,通常包含了项目的介绍、安装方法和使用说明等。
vue.config.js 是 Vue CLI 的配置文件,用于配置构建工具的行为,如 webpack、babel 等(这是可选的,只有在需要自定义配置时才添加)。

主要文件

  • main.ts : 进行vue的注入、路由的注入、ui组件的注入
  • App.vue : 项目的主界面,其他组件进行插入到这个界面显示视图
  • router/index.ts :进行页面路由
  • components : 其他页面的视图,用来插入到App.vue中

项目运行流程

vite 项目的运行流程
在工程化的项目中,vue 要做的事情很单纯:通过 main.js 把 App.vue 渲染到 index.html 的指定区域中
其中:
① App.vue 用来编写待渲染的 模板结构
② index.html 中需要预留一个 el 区域
③ main.js 把 App.vue 渲染到了 index.html 所预留的区域中

添加Element-ui、AXIOS

注:elementui在VUE3进行了更改 npm i element-plus

Axios:npm install --save axios

**注:**Nodejs下载完成后需要换源npm config set registry https://registry.npm.taobao.org

Axios与pinia

AXIOS

Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。

使用npm等包管理工具下载axios

npm i axios

创建axios实例、封装get、post请求方法

import axios from "axios";
import { user } from "@/store/Store";
const requestUrl = "http://localhost:5250";
const userStore = user();
//创建实例
const axiosInstance = axios.create({
  baseURL: requestUrl,
  // timeout: 3000,
});

//请求拦截器,请求前
axiosInstance.interceptors.request.use(
  (config) => {
    if (userStore.data) {
      // console.log("请求头toekn=====>", userStore.data.token);
      // 设置请求头
      // config.headers['token'] = useToken.token;
      config.headers.Authorization = `Bearer ${userStore.data.token}`;
    }

    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);
//请求拦截器,请求后
axiosInstance.interceptors.response.use(
  (response) => {
    if (response.status === 200) return response.data;
  },
  (error) => {
    console.log(error);
    const status: number = error.response.status;
    switch (status) {
      case 404:
        console.error("资源不存在");
        break;
      case 401:
        console.error("没有权限");
        break;
      case 500:
      case 501:
      case 502:
        console.error("没有权限");
        break;
      case 400:
        console.error(error.response.data.msg + "参数错误");
        break;
      default:
        console.log("无法识别");
        break;
    }
    return Promise.reject(error);
  }
);

interface ActionResult<T> {
  data: T;
  msg: string;
  error: any;
  code: number;
}
//get请求
export const get = <T>(
  url: string,
  params?: Object
): Promise<ActionResult<T>> => {
  return axiosInstance.get(url, { params });
};
//post请求
export const post = <T>(
  url: string,
  data?: Object
): Promise<ActionResult<T>> => {
  return axiosInstance.post(url, data);
};
//put请求
export const put = <T>(
  url: string,
  data?: Object
): Promise<ActionResult<T>> => {
  return axiosInstance.put(url, data);
};
//delete请求
export const delete1 = <T>(
  url: string,
  params?: Object
): Promise<ActionResult<T>> => {
  return axiosInstance.delete(url, { params });
};

封装api接口调用方法

import { get, post, delete1, put } from "./request";
import { user } from "@/types/index";
export const loginApi = (data: user) => {
  return post<any>("/api/Login/login", data);
};
export const getUserInfo = (ID: string) => {
  return get<any>("/api/Index/UserInfo", { ID });
};

在页面中调用api

import { loginApi } from "@/common/api";
const emailLoginApi = async () => {
  phoneLoding.value = true;
  try {
    const res = await loginApi({
      EMAIL: emailNumber.value,
      PASSWORD: PassWard.value,
    });
    // localStorage.setItem("userInfo", JSON.stringify(res.data.token));
    // Cookies.set("token", res.data.token);
    console.log(res);
    userStore.setData(res.data);
    console.log(userStore.data);
    $router.push("/index");
    phoneLoding.value = false;
  } catch (error) {
    phoneLoding.value = false;
  }
};

pinia使用

使用npm下载pinia

npm i pinia

创建Store文件进行状态存储

import { defineStore } from "pinia";
export const user = defineStore("userInfo", {
  state: () => ({
    data: { id: "", Name: "", token: "", Email: "" },
  }),
  actions: {
    setData(payload: any) {
      this.data = payload;
    },
  },
});

在页面组件中实例化状态并赋值

import { user } from "@/store/Store";
const userStore = user(); // 实例化 store
userStore.setData(res.data);

网站公告

今日签到

点亮在社区的每一天
去签到