初步思路:創建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="width: 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
