初步思路:创建WebApi编写接口,创建Vue,引用Element-UI,抽取WebApi数据展示
需要添加的包:
Autofac.Extensions.DependencyInjection
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Design
AutoMapper.Extensions.Microsoft.DependencyInjection
Microsoft.EntityFrameworkCore.Tools(DbFirst依据数据库生成实体需要使用)
Pomelo.EntityFrameworkCore.MySql
第一步:创建WebApi,VS2019创建项目不再描述。
Statup.cs
添加api 控制器、添加DbContext、添加AutoMapper、配置跨域访问(用于Vue调用)、添加AutoFac容器。
AutoMapper 用于api返回数据与数据库表机构之间转换,AutoFac配置Service和Repository的注入关系
using System; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Autofac; using AdminWebApi.Autofac; using Autofac.Extensions.DependencyInjection; using DataDAO; using Microsoft.EntityFrameworkCore; using AutoMapper; namespace AdminWebApi { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public ILifetimeScope AutofacContainer { get; private set; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddControllers(); //services.AddMvc(); services.AddDbContext<unified_usersContext>(options => options.UseMySql(Configuration.GetConnectionString("unified_users"), x => x.ServerVersion("5.6.21-mysql"))); services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); //必须appsettings.json中配置 string corsUrls = Configuration["CorsUrls"]; if (string.IsNullOrEmpty(corsUrls)) { throw new Exception("请配置跨请求的前端Url"); } //增加允许跨域配置 //services.AddCors(m => m.AddPolicy(name:"Any", a => a.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials()));//开放全部 //开放指定域名的访问 services.AddCors(options => { options.AddDefaultPolicy( builder => { builder.WithOrigins(corsUrls.Split(",")) //添加预检请求过期时间 .SetPreflightMaxAge(TimeSpan.FromSeconds(2520)) .AllowCredentials() .AllowAnyHeader() .AllowAnyMethod(); }); }); } public void ConfigureContainer(ContainerBuilder builder) { builder.RegisterModule(new AutofacMoule()); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } this.AutofacContainer = app.ApplicationServices.GetAutofacRoot(); //app.UseHttpsRedirection(); // app.UseCors(bulider => bulider.AllowAnyOrigin()); app.UseRouting(); //增加允许跨域配置,放在routing 之后 验证之前,顺序很重要 app.UseCors();//加载默认跨域配置 //app.UseCors("Any");//加载有命名的策略 app.UseAuthorization(); app.UseEndpoints(endpoints => { //endpoints.MapControllers(); endpoints.MapControllerRoute( name: "default", pattern: "api/{controller=Users}/{action=index}/{id?}" ); }); } } }
using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Autofac.Extensions.DependencyInjection; using System.IO; namespace AdminWebApi { public class Program { public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => //Host.CreateDefaultBuilder(args) // .ConfigureWebHostDefaults(webBuilder => // { // webBuilder.UseStartup<Startup>(); // }); Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseContentRoot(Directory.GetCurrentDirectory()); webBuilder.UseStartup<Startup>(); //webBuilder.UseUrls("http://localhost:8088"); }); } }
第二步:编写接口与数据库访问
添加IRepository和Repository,用于访问数据库,添加IService和Service用于接受处理请求。
EF Core 参考:https://www.cnblogs.com/zeran/p/11125309.html
using ModelDto; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using ModelDto.DtoParameters; using ModelDto.RUDDto; using ModelDto.ViewDto; using DataDAO; namespace AdminWebApi.IRepository { public interface IUsersRepository { Task<NtUsers> GetUsers(int UserId); Task<IEnumerable<NtUsers>> GetUsersList(UsersDtoParameter usersDtoParameter); Task<int> UpdateUser(AddUserDto addUser); } }
using AdminWebApi.IRepository; using DataDAO; using Microsoft.EntityFrameworkCore; using ModelDto; using ModelDto.DtoParameters; using ModelDto.RUDDto; using ModelDto.ViewDto; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace AdminWebApi.Repository { public class UsersRepository : IUsersRepository { public UsersRepository() { } readonly unified_usersContext _Context; public UsersRepository(unified_usersContext unifiedUserDbContext) { _Context = unifiedUserDbContext; } public async Task<NtUsers> GetUsers(int UserId) { return await _Context.NtUsers.Where(n => n.Id== UserId).FirstOrDefaultAsync(); } public async Task<IEnumerable<NtUsers>> GetUsersList(UsersDtoParameter usersDtoParameter) { var query_user = _Context.NtUsers as IQueryable<NtUsers>;//创建筛选条件, if (!string.IsNullOrWhiteSpace(usersDtoParameter.name))//筛选姓名 { usersDtoParameter.name = usersDtoParameter.name.Trim(); query_user=query_user.Where(n => n.Name.StartsWith(usersDtoParameter.name)); } return await query_user.Take(usersDtoParameter.pageCount).ToListAsync();//取数据 } public async Task<int> UpdateUser(AddUserDto addUser) { return 1; } } }
using DataDAO; using ModelDto; using ModelDto.DtoParameters; using ModelDto.RUDDto; using ModelDto.ViewDto; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace AdminWebApi.IServices { public interface IUserService { Task<UsersDto> GetUsers(int UserId); Task<IEnumerable<UsersDto>> GetUsersList(UsersDtoParameter usersDtoParameter); Task<int> UpdateUser(AddUserDto addUser); } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AdminWebApi.IRepository; using AdminWebApi.IServices; using AutoMapper; using DataDAO; using ModelDto; using ModelDto.DtoParameters; using ModelDto.RUDDto; using ModelDto.ViewDto; namespace AdminWebApi.Service { public class UserService : IUserService { readonly IUsersRepository _userpepository; private readonly IMapper _mapper; public UserService(IUsersRepository usersRepository,IMapper mapper) { _userpepository = usersRepository; this._mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); } public async Task<UsersDto> GetUsers(int UserId) { NtUsers ntusers= await _userpepository.GetUsers(UserId); return _mapper.Map<UsersDto>(ntusers);//表结构model转换为Dtomodel,Dto用于暴露在外部 } public async Task<IEnumerable<UsersDto>> GetUsersList(UsersDtoParameter usersDtoParameter) { return _mapper.Map<IEnumerable<UsersDto>>(await _userpepository.GetUsersList(usersDtoParameter)); } public async Task<int> UpdateUser(AddUserDto addUser) { return await _userpepository.UpdateUser(addUser); } } }
第2.1步:在编写这些代码期间,需要创建DbContext、AutoMapper、AutoFac等
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AdminWebApi.IServices; using AdminWebApi.Service; using Autofac; namespace AdminWebApi.Autofac { public class AutofacMoule:Module { protected override void Load(ContainerBuilder builder) { builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly())//注册整个程序集 .Where(n => n.Name.EndsWith("Service")).AsImplementedInterfaces()//注册程序集内以Service结尾的文件 .SingleInstance();//单例模式 //.InstancePerLifetimeScope(); builder.RegisterAssemblyTypes(System.Reflection.Assembly.GetExecutingAssembly()) .Where(n => n.Name.EndsWith("Repository")).AsImplementedInterfaces()//注册程序集内以Repository结尾的文件 .SingleInstance(); //builder.RegisterType<twoUserService>().As<IUserService>(); } } }
Statup.cs中引用AutoFac,添加函数
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new AutofacMoule());
}
Program.cs需要按照上面的Program.cs代码段修改
第2.2步:引用AutoMapper,并编写Dto类,并编写映射关系的UserProfile,AutoMapper 是按照名称进行映射,名称不同不能映射成功,可以在映射是对某个字段进行处理
using System; using System.Collections.Generic; using System.Text; using ModelDto.ViewDto; using ModelDto.RUDDto; using AutoMapper; namespace DataDAO.profiles { class UserProfile : Profile { public UserProfile() { CreateMap<NtUsers, UsersDto>() .ForMember( u => u.name, opt => opt.MapFrom( dto => dto.Name+dto.NickName ) ); } } }
添加后需要在Sturtup中的ConfigureService 添加
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
第三步:编写api接口,RESTful Web API是按照http动词(get/post/put/delete/options等)请求接口的,比如接口上增加[HttpPost]无论接口命名是什么只要是post请求都调用这个接口,
指定接口名称使用[HttpPost("getuserlist")]或者[HttpPost(nameof(接口名))]
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using ModelDto; using Autofac; using AdminWebApi.IServices; using ModelDto.ViewDto; using ModelDto.DtoParameters; using System.Net.Mime; using Microsoft.AspNetCore.Cors; namespace AdminWebApi.Controllers { [EnableCors]//加载默认跨域配置 //[EnableCors("Any")]//加载有命名的跨域策略 [ApiController] [Route("api/[controller]")] //[Produces(MediaTypeNames.Application.Json)] public class AdminUserController : ControllerBase { readonly IUserService _userservice; public AdminUserController(IUserService userService) { _userservice = userService; } [HttpGet("getuserByid")] public async Task<UsersDto> GetDtoAsync(int uid) { return await _userservice.GetUsers(uid); } [HttpPost("getuserlist")] [HttpOptions] public async Task<IEnumerable<UsersDto>> GetUserDtoAsync(UsersDtoParameter dtoParameter) { return await _userservice.GetUsersList(dtoParameter); } } }
这时使用postman 请求接口能够正常返回数据。postman不涉及跨域问题。
第四步:创建Vue项目并饮用Element-ui,VS2019可以创建Vue项目,没有的话先安装VS2019的Node.JS支持
Vue的启动文件是Main.js引用第三方js 可以在indexhtml的body之前
需要先在项目路径下安装 Element-ui,axios(用于请求后端数据)
npm i element-ui -S
npm install axios -S
import Vue from 'vue'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; import App from './App.vue'; import axios from 'axios';//引用axios 用于请求数据 import qs from 'qs'; Vue.config.productionTip = true; Vue.use(ElementUI);//注册组件 Vue.component(ElementUI); //Vue.use(axios);// Vue.prototype.$axios = axios;//相当于定义变量,没太搞清 Vue.prototype.$qs = qs; //跨域问题处理开始,实际没效果,后端开放跨域就可以 Vue.prototype.HOST = '/api'; var http = axios.create({ baseURL: 'http://127.0.0.1:5000/', timeout: 10000 }); axios.defaults.headers.post['Content-Type'] = 'application/json'; //axios.defaults.baseUrl = "/api"; Vue.http = http; //跨域问题处理 结束 new Vue({ el: '#app', render: h => h(App) }).$mount('#app');
<template> <div id="app"> <el-container> <el-header><Header /></el-header> <el-container> <el-aside width="200px"><Left_menu /></el-aside> <el-main><Home /></el-main> </el-container> </el-container> </div> </template> <script> import Home from './components/Home.vue'; import Header from './components/Header.vue'; import Left_menu from './components/Left_menu.vue'; export default { name: 'app', components: { Header, Left_menu, Home } }; </script> <style> @import url("//unpkg.com/element-ui@2.14.0/lib/theme-chalk/index.css"); .el-header, .el-footer { background-color: #B3C0D1; color: #333; text-align: center; line-height: 60px; } .el-aside { background-color: #D3DCE6; color: #333; text-align: center; line-height: 200px; } .el-main { background-color: #E9EEF3; color: #333; text-align: center; line-height: 160px; } body > .el-container { margin-bottom: 40px; } .el-container:nth-child(5) .el-aside, .el-container:nth-child(6) .el-aside { line-height: 260px; } .el-container:nth-child(7) .el-aside { line-height: 320px; } </style>
Left_menu、Header内容只是Element-ui的样式,不贴代码了
<template> <div class="home"> <template> <el-table :data="tableData2" style=" 100%" :key="Math.random()"> <el-table-column prop="login_name" label="登录名" width="180"> </el-table-column> <el-table-column prop="name" label="姓名" width="180"> </el-table-column> <el-table-column prop="cellphone" label="地址"> </el-table-column> </el-table> </template> </div> </template> <script> // var tabelData; export default { //data() 为函数,mounted methods 均为vue生命周期内的不同内容,写法与顺序 暂时没搞清 data() { return { tableData2: []//定义以及初始化数据 } }, mounted() {// 或者 mounted: function() { this.getData(); }, methods: { getData() { let params = new URLSearchParams() params.append("name", "学员"); //$axios 是引用 的axios 的组件,定义的变量 //后台接受参数是application/json 格式,需要指定content-type,不指定Content-type 或者不对应会报415 媒体类型不正确错误 //参数需要 JSON.stringify 对象转成字符串,不然会报400错误, this.$axios.post("http://127.0.0.1:5000/api/adminuser/getuserlist",//vue和后端webapi 会出现跨域情况,需要webapi内的startup 增加允许跨域配置,详见后端 JSON.stringify(params), { headers: { 'Content-Type': 'application/json;charset=UTF-8' } }).then(msg=> { this.tableData2 = msg.data;//为定义的数据赋值 //this.tabelData2 需要加this ,这里的this 是生命周期内的(要使用then(msg=>{})方式) //如果使用then(function(msg){}) 方式 this表示window内,会报错 tableData2未定义,解决方案: // 在vue生命周期内定义常量 const that=this; function 内 改为that.tableData2=msg.data; }); } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> </style>
Home.vue的逻辑只是动态给table赋值,请求了api接口,遇到的问题比较多。特地记录一下
1、vue生命周期问题,暂时没搞懂,exprot default{data(){return ……},mounted(){},methods:{}} 其中data和mounted是函数,menthods是属性,这些执行是有先后顺序的。首先需要在data中定义并初始化数据,在mounted内计算。
其中getData()函数内的$axios.post()错了很多次才调整好语法(还是语法不了解呀T_T)
this表示window,代表整个页面,使用msg=>{this.XXX} 这里边的this代表局部 | function(msg){this.XXX}这里的this表示window |
400错误和415错误
application/x-www-form-urlencoded
2、类型不正确会报415错误:API接口接受的 'Content-Type'类型是 'application/json;charset=UTF-8',所以需要加headers{ 'Content-Type': 'application/json;charset=UTF-8'},默认是
3、参数不被识别会报400错误:参数需要JSON.stringify转为字符串,我个人理解是传递的是对象,但是后端需要的是json字符串,需要转换,不然会报400错误。
重要的一点:跨域
一般Vue都是和后端分离的,所以涉及到跨域问题,所以需要后端设置
首先需要在appsettings.json中添加允许访问的域
"CorsUrls": "http://localhost:1337,http://127.0.0.1:1337,http://127.0.0.1:8081,http://127.0.0.1:8080,http://172.21.210.1:1337"
Startup.cs中的ConfigureServices增加跨域设置,可以设置全部,也可以设置指定的域
//必须appsettings.json中配置 string corsUrls = Configuration["CorsUrls"]; if (string.IsNullOrEmpty(corsUrls)) { throw new Exception("请配置跨请求的前端Url"); } //增加允许跨域配置 //services.AddCors(m => m.AddPolicy(name:"Any", a => a.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials()));//开放全部 //开放指定域名的访问 services.AddCors(options => { options.AddDefaultPolicy( builder => { builder.WithOrigins(corsUrls.Split(",")) //添加预检请求过期时间 .SetPreflightMaxAge(TimeSpan.FromSeconds(2520)) .AllowCredentials() .AllowAnyHeader() .AllowAnyMethod(); }); });
在Configure中需要在app.UseRoting()和app.UseAuthorization()之间添加:app.UseCors();
在Api的Controller上添加[EnableCors]
简单记录一下 配置带名称的Cors规则和default的默认规则,有些资源可以公开访问,有些资源只允许某些用户或域名访问,比如新闻是允许公开访问的,用户信息是限定的,就可以定义不同名称的Cors规则,Controller上使用[EnableCors("名称")]
代码位置:https://gitee.com/zeran/NetCore3.X-WebApi-Vue-AutoFac