vue-element-admin整合.net core web api项目开发点滴记录

1、创建ASP.NET Core Web API项目

创建好之后,IDE会自动为我们创建一个Controller,然后会自动引入Swagger。

2、配置ef框架

我们这里是使用mysql数据库。

(1)在上面我们创建项目之后,这里进行ef框架的配置。安装以下包。

(2)打开appsettings.json,然后添加数据库连接字符串。

(3)创建DbContext

public class MyContext : DbContext
{
    protected readonly IConfiguration Configuration;

    public DBEntities(DbContextOptions<DBEntities> options) : base(options)
    {

    }

    public DbSet<tc_user> tc_user_market { get; set; }
}

(4)注入DbContext

在Program.cs文件内添加代码。

builder.Services.AddDbContext<DBEntities>(options =>
  options.UseMySql(builder.Configuration.GetConnectionString("dbconn"), new MySqlServerVersion(new Version())));

(5)创建model、interface、service

model

[Table("tc_user", Schema = "dbo")]
    [Serializable]
    public class tc_user
    {
        public tc_user()
        {

        }
        ///<summary>
        /// 主键ID
        ///</summary>
        [Key]
        public int id { get; set; }

        /// <summary>
        /// 
        /// </summary>
        public string username { get; set; }


        public string password { get; set; }


        public long createtime { get; set; }


        public long updatetime { get; set; }



        public byte userflag { get; set; }

    }

接口

public interface Itc_user
    {

        #region 基础接口

        /// <summary>
        /// 添加信息
        /// </summary>
        /// <param name="model">实体</param>
        /// <returns></returns>
        bool Add(tc_user model);

        /// <summary>
        /// 删除信息
        /// </summary>
        /// <param name="id">主键</param>
        /// <returns></returns>
        bool Del(int id);

        /// <summary>
        /// 获得信息
        /// </summary>
        /// <param name="id">主键</param>
        /// <returns></returns>
        tc_user FindDefault(int id);

        /// <summary>
        /// 更新信息
        /// <param name="model">实体</param>
        /// </summary>
        /// <returns></returns>
        bool Update(tc_user model);

        /// <summary>
        /// 获得所有信息
        /// </summary>
        /// <returns></returns>
        List<tc_user> FindAll();

        #endregion 基础接口
    }

service

public class UserService : Itc_user
    {
        private DBEntities _context;

        public UserService(DBEntities context)
        {
            _context = context;
        }

        public bool Add(tc_user model)
        {
            _context.tc_user_market.Add(model);
            int recordsAffected;
            recordsAffected = _context.SaveChanges();
            return recordsAffected > 0;
        }

        public bool Del(int id)
        {
            var info = _context.tc_user_market.SingleOrDefault(m => m.id == id);
            if (info != null)
            {
                _context.tc_user_market.Remove(info);
                _context.SaveChanges();
            }
            else
            {
                return false;
            }
            return true;
        }

        public List<tc_user> FindAll()
        {
            return _context.tc_user_market.OrderByDescending(m => m.id).ToList();
        }

        public tc_user FindDefault(int id)
        {
            return _context.tc_user_market.Where(m => m.id == id).FirstOrDefault();
        }

        public bool Update(tc_user model)
        {
            var info = _context.tc_user_market.SingleOrDefault(m => m.id == model.id);

            info.id = model.id;//主键ID
            info.username = model.username;
            info.password = model.password;
            info.createtime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
            info.updatetime = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds();
            info.userflag = 0;

            int recordsAffected;
            recordsAffected = _context.SaveChanges();
            return recordsAffected > 0;
        }
    }

(6)注入Service

在Program.cs文件内添加代码。

//注册service
builder.Services.AddScoped<Itc_user, UserService>();

(7)在Controller内调用

[ApiController]
[Route("[controller]")]
public class UserLoginController : ControllerBase
{
    private Itc_user _userService;

    public UserLoginController(Itc_user userService)
    {
        _userService = userService;
    }

    [HttpGet(Name = "GetUserList")]
    public List<tc_user> GetUserList()
    {
        return _userService.FindAll();
    }
}

3、配置跨域

修改Program.cs文件,主要注意builder.Services.AddCors、app.UseCors等部分

using Microsoft.EntityFrameworkCore;
using TechAccumulat.Helpers;
using TechAccumulat.Interfaces;
using TechAccumulat.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

//配置数据库连接
builder.Services.AddDbContext<DBEntities>(options =>
  options.UseMySql(builder.Configuration.GetConnectionString("dbconn"), new MySqlServerVersion(new Version())));

//注册service
builder.Services.AddScoped<Itc_user, UserService>();

//注册跨域
builder.Services.AddCors(options =>
{
    options.AddPolicy("any", builder =>
    {
        builder.WithOrigins("http://localhost:9527") //允许任何来源的主机访问
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials();
    });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseRouting();

//使用
app.UseCors("any");

app.UseEndpoints(x => x.MapControllers());

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

然后在Controller,增加[EnableCors("any")]。

4、前后端请求和接收

后端

前端

FromForm

application/x-www-form-urlencoded

FromBody

application/json

FromQuery

url传值

5、Vue进行接口调用

vue中使用axios请求post接口,请求会发送两次。服务端设置了可跨域访问都会默认发送两次请求,第一次是预检请求,查询是否支持跨域,第二次才是真正的post提交。

浏览器分为简单请求以及非简单请求。非简单跨域请求都会请求两次。简单请求需要同时满足以下条件。

1. 只能是Get、Head、Post方法
2. 除了浏览器自己在Http头上加的信息(如Connection、User-Agent),开发者只能加这几个:Accept、Accept-Language、Content-Type、。。。。
3. Content-Type只能取这几个值:
application/x-www-form-urlencoded
multipart/form-data
text/plain

配置axios

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
  // withCredentials: true,     // send cookies when cross-domain requests
  timeout: 5000, // request timeout
  headers: {
    'Content-Type': 'application/json'
  }
})

创建请求方法

export function login(data) {
  return request({
    url : 'UserLogin/UserSignIn',
    method: 'post',
    data
  })
}

6、配置jwt

vue端,主要是“config.headers['Authorization'] = 'Bearer ' + getToken()“,这里的token就是获取jwt的时候存储起来的,请求其它需要授权的接口的时候带回去。

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url

  // withCredentials: true, // send cookies when cross-domain requests
  timeout: 5000, // request timeout
  headers: {
    'Content-Type': 'application/json'
    //'Content-Type': 'application/x-www-form-urlencoded'
  }
})

// request interceptor
service.interceptors.request.use(
  config => {
    // do something before request is sent

    if (store.getters.token) {
      // let each request carry token
      // ['X-Token'] is a custom headers key
      // please modify it according to the actual situation
      config.headers['Authorization'] = 'Bearer ' + getToken()
    }
    return config
  },
  error => {
    // do something with request error
    console.log(error) // for debug
    return Promise.reject(error)
  }
)

服务端

首先安装相应的包,注意与自己的目标框架一致。

Program.cs,注意builder.Services.AddSwaggerGen、#region JWT服务、app.UseAuthentication();、app.UseAuthorization();等需要正确填写,并且注意顺序。

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Reflection;
using System.Text;
using TechAccumulat.Helpers;
using TechAccumulat.Interfaces;
using TechAccumulat.Services;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

//配置数据库连接
builder.Services.AddDbContext<DBEntities>(options =>
  options.UseMySql(builder.Configuration.GetConnectionString("dbconn"), new MySqlServerVersion(new Version())));

//注册service
builder.Services.AddScoped<Itc_user, UserService>();

builder.Services.AddCors(options =>
{
    options.AddPolicy("any", builder =>
    {
        builder.WithOrigins("http://localhost:9527") //允许任何来源的主机访问
        .AllowAnyMethod()
        .AllowAnyHeader()
        .AllowCredentials();
        /* builder.AllowAnyOrigin() //允许任何来源的主机访问
         .AllowAnyMethod()
         .AllowAnyHeader()
         .AllowCredentials();//指定处理cookie*/
    });
});


#region sqagger
builder.Services.AddSwaggerGen(config =>
{
    config.CustomSchemaIds(c => c.FullName);
    //在 header中添加token,传递到后台


    config.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
            },
            new List<string>()
        }
    });

    //使用过滤器单独对某些API接口实施认证,可以自定义https://www.bbsmax.com/A/D8546LxvzE/
    //config.OperationFilter<SwaggerOperationFilter>();
    config.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",
        Name = "Authorization",//jwt默认的参数名称
        In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
        Type = SecuritySchemeType.ApiKey
    });

    /*var security = new Dictionary<string, IEnumerable<string>> { { "CoreAPI", new string[] { } }, };
    config.AddSecurityRequirement(security);
    c.AddSecurityDefinition("CoreAPI", new ApiKeyScheme
    {
        Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
        Name = "Authorization",//jwt默认的参数名称
        In = "header",//jwt默认存放Authorization信息的位置(请求头中)
        Type = "apiKey"
    });*/

});
#endregion


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

builder.Services.AddAuthentication("Bearer").AddJwtBearer(o =>
{
    o.TokenValidationParameters = new TokenValidationParameters()
    {
        ValidateIssuer = true, //是否验证Issuer
        ValidIssuer = builder.Configuration["Jwt:Issuer"], //发行人Issuer
        ValidateAudience = true, //是否验证Audience
        ValidAudience = builder.Configuration["Jwt:Audience"], //订阅人Audience
        ValidateIssuerSigningKey = true, //是否验证SecurityKey
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:SecretKey"])), //SecurityKey
        ValidateLifetime = true, //是否验证失效时间
        ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
        RequireExpirationTime = true,
    };
    o.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = context =>
        {
            // 如果过期,则把<是否过期>添加到,返回头信息中
            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
            {
                context.Response.Headers.Add("Token-Expired", "true");
            }
            return Task.CompletedTask;
        }
    };
});
#endregion


var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}


app.UseRouting();

app.UseCors("any");

//http跳转到https, 开发或不配置域名时就不开启
//app.UseHttpsRedirection();
app.UseAuthentication();

// 启用认证中间件
app.UseAuthorization();

app.UseEndpoints(x => x.MapControllers());

app.MapControllers();

app.Run();

配置appsetting.json,添加相应设置

"Jwt": {
    "SecretKey": "u6u^Bdob@OJ&KF2RcAB%ybsoy&2S7jhP^SW!q!Z^FK7eB7F8CcxIHsIh4Ll3pL^#",
    "Issuer": "WebAppIssuer",
    "Audience": "WebAppAudience"
  },

创建一个JwtHelper.cs

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)
        {
            // 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:SecretKey"]));

            // 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:Audience"],  //Audience
                claims,                          //Claims,
                DateTime.Now,                    //notBefore
                DateTime.Now.AddSeconds(86400),     //expires
                signingCredentials               //Credentials
            );

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

            return token;
        }
    }

接着在controller的需要验证的方法上添加[Authorize]注解

7、文件上传/带参数

这里使用最基本的el-upload组件。如果需要校验需要传headers参数。

<el-upload
    class="upload-demo"
    name="files"
    headers="header"
    drag
    action="http://localhost:5290/baseApi/File/CreateFile"
    accept=".jpg,.jpeg,.png,.gif,.pdf,.doc,.zip"
    :multiple=true
    :file-list="fileList">
    <i class="el-icon-upload"></i>
    <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
    <div class="el-upload__tip" slot="tip">只能上传jpg/png/pdf/doc/zip等文件</div>
  </el-upload>

.net core web api后端

[HttpPost]
[Authorize]
public string CreateFile([FromForm] List<IFormFile> files)
{
    if (files != null && files.Count>0)
    {
        foreach (var file in files)
        {
            var fileDir = "files";
            string projectFileName = System.Guid.NewGuid().ToString("N") + '_' + file.FileName;
            string filePath = fileDir + $@"\{projectFileName}";
            using (FileStream fs = System.IO.File.Create(filePath))
            {
                file.CopyTo(fs);
                fs.Flush();
            }
        }
        
        //还有一些插入数据库的逻辑
    }
}

带参数的情况,后台这样接收

public class RequestFileModel
{
    public List<IFormFile> files { get; set; }

    public int type { get; set; }

    public int fid { get; set; }
}

[HttpPost]
[Authorize]
public string CreateFile([FromForm] RequestFileModel model)
{
    //这里是你的业务逻辑
}

8、tinymce整合

(1)本地化

1、将tinymce整合包下载到public文件夹内,语言文件的js也要下载,我这里用的4.9.3版本。下载地址https://www.tiny.cloud/get-tiny/self-hosted/

2、修改components下面的Tinymce内的index.vue文件,将CDN修改为本地路径

const tinymceCDN = '/tinymce/tinymce.min.js'

# 配置语言路径
language_url:"/tinymce/langs/zh_CN.js",
//language: 'zh_CN',

(2)上传处理

修改components下面的Tinymce内的components下面的EditorImage.vue

修改action,修改为你的路径

action="http://localhost:5290/baseApi/File/CreateFile"

修改handleSuccess方法,我这里习惯使用url的方式,实际也可以自行处理使用image base64,不过这样做就等于把图片存到数据库了,不是太好。

handleSuccess(response, file) {
      console.log(response)
      const uid = file.uid
      const objKeyArr = Object.keys(this.listObj)
      for (let i = 0, len = objKeyArr.length; i < len; i++) {
        if (this.listObj[objKeyArr[i]].uid === uid) {
          this.listObj[objKeyArr[i]].url = "http://localhost:8020/" + response.query[0].url
          this.listObj[objKeyArr[i]].hasSuccess = true
          return
        }
      }
    },

(3)赋值绑定

用setContent进行赋值,更加稳定

<tinymce ref="tinymceTxt" v-model="postForm.contactcontent" :value="postForm.contactcontent" :height="300" />

this.$refs.tinymceTxt.setContent(response.query.contactcontent)

9、Vue配置全局变量

定义,个人喜欢再App.vue中直接定义

<template>
  <div id="app">
    <router-view />
  </div>
</template>

<script>
const imgUpload = 'http://localhost:8080/uploadFile'
const imgDomain = 'http://localhost:8081/'

export default {
  imgUpload,
  imgDomain,
  name: 'App'
}
</script>

使用方式

this.GLOBAL.imgUpload

98、需要注意的重要问题

1、每个Vue的页面,export default { name : "千万不要用中文" }。

99、遇到的错误

(1)EF Core Error:Unable to cast object of type ‘System.DBNull‘ to type ‘System.String‘

原因1:可能在model里面未恰当使用了[Required]。

原因2:可能在项目的.csproj文件里面Nullable属性启动了,改成disable。

(2)上传文件到服务端程序停止

工具>选项>项目和解决方案>web项目>如图取消打钩,保存退出。重新运行

(3)el-dialog不显示的问题

这里是由于一个属性没有设置正确导致的问题。在el-dialog 标签内,添加如图红色矩形框内所示属性。

即可解决问题。

可以直接写 append-to-body

也可以写成 :append-to-body=“true”

猜你喜欢

转载自blog.csdn.net/bashendixie5/article/details/129094462