VS2019创建WebApi+Vue+Element-UI(入门)

初步思路:创建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?}"
                    );
            });

        }
    }
}
Statup.cs
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");
                });
    }
}
Program.cs

 第二步:编写接口与数据库访问

添加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);
    }
}
IRepository.cs
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;
        }
    }
}
Repository.cs
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);
    }
}
IServices
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);
        }
    }
}
UserService

第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>();
        }
    }
}
AutofacMoule.cs

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
                    )
                );
        }
    }
}
UserProfile.cs

添加后需要在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);
        }
    }
}
AdminUserController.cs

 这时使用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');
Main.js
<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>
App.vue

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

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错误
2、类型不正确会报415错误:API接口接受的 'Content-Type'类型是 'application/json;charset=UTF-8',所以需要加headers{ 'Content-Type': 'application/json;charset=UTF-8'},默认是
application/x-www-form-urlencoded
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();
                    });
            });
Cors配置
在Configure中需要在app.UseRoting()和app.UseAuthorization()之间添加:app.UseCors();
在Api的Controller上添加[EnableCors]
简单记录一下 配置带名称的Cors规则和default的默认规则,有些资源可以公开访问,有些资源只允许某些用户或域名访问,比如新闻是允许公开访问的,用户信息是限定的,就可以定义不同名称的Cors规则,Controller上使用[EnableCors("名称")]
代码位置:https://gitee.com/zeran/NetCore3.X-WebApi-Vue-AutoFac
posted @ 2020-11-12 19:10  zeran  阅读(2932)  评论(0编辑  收藏  举报