.net core RESTful Api笔记②

.net core RESTful Api笔记①中

写了建立api的工程,和restfulapi介绍和http请求问题,以及内容协商

Entity Model 和面向外部的Model

 entity model:entity framework core使用entity model是用来表示数据库里面的记录。

面向外部model:面向外部的model表示传输的东西,这类model优势后叫dto,有时叫viewmodel

这样就更健壮,可靠,易于进化,同时也是页面最想获得的东西

 

 修改controller

        public async Task<IActionResult> GetCompanies()
        {
            var companies = await _companyRepository.GetCompaniesAsync();
            var companyDto = new List<CompanyDto>();
            foreach (var company in companies) 
            {
                companyDto.Add(new CompanyDto
                {
                    Id = company.Id,
                    Name=company.Name
                }); 
            }
            return Ok(companyDto);
        }

 

 IActionResult:也可以写成具体的,可以配合swagger使用

public async Task<ActionResult<IEnumerable<CompanyDto>>> GetCompanies()
        {
            var companies = await _companyRepository.GetCompaniesAsync();
            var companyDto = new List<CompanyDto>();
            foreach (var company in companies) 
            {
                companyDto.Add(new CompanyDto
                {
                    Id = company.Id,
                    Name=company.Name
                }); 
            }
            return companyDto;
        }

AutoMapper:对象映射器

安装:

 

 注册:

            //获取当前的assembly程序集
            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

创建profile用来映射,修改了之前的dto内容

 

 

 

namespace Rountine.API.Profiles
{
    public class CompanyProfile:Profile
    {
        public CompanyProfile()
        {
            //这里和注册的方式很像
            //里面的参数是为了在名称映射不相同的情况下手动映射的
            CreateMap<Company, CompanyDto>().ForMember(
                dest=>dest.CompanyName,
                opt=>opt.MapFrom(src=>src.Name));
        }
    }
}

最后修改controller内容

using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Rountine.API.Models;
using Rountine.API.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Rountine.API.Controllers
{
    [ApiController]
    [Route("api/companies")]
    public class CompainesControllercs : ControllerBase
    {
        private readonly ICompanyRepository _companyRepository;
        private readonly IMapper _mapper;

        public CompainesControllercs(ICompanyRepository companyRepository, IMapper mapper)
        {
            _companyRepository = companyRepository ?? throw new ArgumentException(nameof(companyRepository));
            _mapper = mapper ?? throw new ArgumentException(nameof(mapper));
        }
        [HttpGet]
        public async Task<ActionResult<IEnumerable<CompanyDto>>> GetCompanies()
        {
            var companies = await _companyRepository.GetCompaniesAsync();
            var companyDto = _mapper.Map<IEnumerable<CompanyDto>>(companies);
            
            return Ok(companyDto);
        }
        [HttpGet("{companyId}")]
        public async Task<ActionResult<CompanyDto>> GetCompanies(Guid companyId)
        {
            var company = await _companyRepository.GetCompanyAsync(companyId);
            if (company == null) {
                return NotFound();
            }
            var companyDto = _mapper.Map<CompanyDto>(company);
            return Ok(companyDto);
        }
    }
}

看下效果:

 

 父子资源获取方式:

对于employee的获取不能直接获取它的资源,要体现出公司和员工的关系

创建employee的model和他的controller

这是employDto:

 

 创建EmployeeProfile

using AutoMapper;
using Rountine.API.Models;
using Rountion.API.Eneities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Rountine.API.Profiles
{
    public class EmployeeProfile:Profile
    {
        public EmployeeProfile()
        {
            CreateMap<Employee, EmployeeDto>()
                .ForMember(
                    dest=>dest.Name,
                    opt=>opt.MapFrom(src=>$"{src.FirstName}{src.LastName}")
                )
                .ForMember(
                    dest=>dest.Gender,
                    opt=>opt.MapFrom(src=>src.Gender.ToString())
                )
                .ForMember(
                    dest=>dest.Age,
                    opt=> opt.MapFrom(src=>DateTime.Now.Year-src.DateOfBirth.Year)
                );
        }
    }
}

创建controller

using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Rountine.API.Models;
using Rountine.API.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Rountine.API.Controllers
{
    [ApiController]
    [Route("api/companies/{companyId}/employees")]
    public class EmployessController:ControllerBase
    {
        private readonly IMapper mapper;
        private readonly ICompanyRepository companyRepository;

        public EmployessController(IMapper mapper,ICompanyRepository companyRepository)
        {
            this.mapper = mapper ?? throw new ArgumentException(nameof(mapper));
            this.companyRepository = companyRepository ??throw new ArgumentNullException(nameof(companyRepository));
        }
        public async Task<ActionResult<IEnumerable<EmployeeDto>>> GetemployessFromCompany(Guid companyId) 
        {
            //判断公司是否存在
            if (!await this.companyRepository.CompanyExistsAsync(companyId)) 
            {
                return NotFound();
            }
            //这里直接将他们写道一块,本来不应该这样写的
            var employees = await this.companyRepository.GetEmployeesAsynce(companyId);
            //转成dto
            var employessdto = this.mapper.Map<IEnumerable<EmployeeDto>>(employees);
            return Ok(employessdto);
        }
    }
}

添加员工种子数据:

            modelBuilder.Entity<Employee>().HasData(
                new Employee {
                   Id= Guid.Parse("11CCF91A-5E6B-4397-A639-78A18853DDB1"),
                   CompanyId = Guid.Parse("E6573877-FB7D-4BBE-A40A-44D24D8807FA"),
                   EmployeeNo="182844" ,
                   FirstName="li",
                   LastName="ming",
                   Gender=Gender.男,
            DateOfBirth=new DateTime(2000,1,2) },
new Employee { Id = Guid.Parse("6917B0AA-626D-434A-BDEA-C6E4F9EE35CC"), CompanyId = Guid.Parse("E6573877-FB7D-4BBE-A40A-44D24D8807FA"), EmployeeNo = "182845", FirstName = "liu", LastName = "ming", Gender = Gender.男,
            DateOfBirth = new DateTime(1995, 1, 2) } );

运行PM>add-migration addEmployeeData

生成新的语句和快照文件,运行项目,我这里数据一直没生成成功,之后删除了库,重新生成了下数据。

 

 添加单个查询

        [HttpGet("{employeeId}")]
        public async Task<ActionResult<EmployeeDto>> GetemployesFromCompany(Guid companyId,Guid employeeId)
        {
            //判断公司是否存在
            if (!await this.companyRepository.CompanyExistsAsync(companyId))
            {
                return NotFound();
            }
            var employee = await this.companyRepository.GetEmployeeAsync(companyId, employeeId);
            if (employee == null) 
            {
                return NotFound();
            }
            var employeedto= this.mapper.Map<EmployeeDto>(employee);
           
            return Ok(employeedto);
        }

 处理故障

生产环境可以在管道中配置

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else 
            {
                app.UseExceptionHandler(appBuilder=> {
                    appBuilder.Run(async context=> {
                        context.Response.StatusCode = 500;
                        await context.Response.WriteAsync("unException error");
                    });
               

保证生产环境不暴露api细节。

Http Head

head和get几乎一样

只是有一点不同:head的api不应该返回相应body

head可以获取资源上获取一些信息

只用在[httpget]下面加上[httphead]就行了;

过滤和搜索

如何绑定api传递数据:

数据通过多种方式来传递给api。

binging source attributes 会告诉model绑定引擎从哪绑定的

[FromBody]:请求的body

[FromForm]:请求的body中的form

[FromHeader]:请求的header

[FromQuery]:querystring中的参数

[FromRoute]:当前请求路由的数据

[FromService]:作为action参数注入的服务

 

[ApiController]

默认情况下asp.net core会从complex object model binder,它会把数据从value providers那里提取出来,而value providers的顺序定义好。

但是我们构建api时会使用[apiController]这个属性,为了是适应api改变的规则。

[FromBody]:复杂的参数类型

[FromForm]:推断IFormFile和IFormFileCollextion类型action参数

[FromRoute]:用来推断Action参数名和路由参数一致情况

[FromService]:用来推断action参数

 

过滤:我们把某个字段名字以及向要让该字段匹配的值一起传递给api,并将这些作为返回集合的一部分。

例:返回类型是国有企业的的欧洲公司

get 、api/companies?type=state-owned&region=Europe

 

搜索:针对集合进行搜索是指预定义一些规则,把符合条件的数据添加到集合里

搜索实际超过过滤范围,搜索可能没有

例:get /api/companies?q=xxx

 这里对员工查询进行修改

 public async Task<IEnumerable<Employee>> GetEmployeesAsynce(Guid companyId,string gender,string q)
        {
            if (companyId == Guid.Empty) {
                throw new ArgumentNullException(nameof(companyId));
            }
            if (string.IsNullOrEmpty(gender) && string.IsNullOrEmpty(q)) 
            {
                return await _context.employees.Where(x => x.CompanyId == companyId).OrderBy(x=>x.EmployeeNo).ToListAsync();
            }
            var items = _context.employees.Where(x => x.CompanyId == companyId);
            if (!String.IsNullOrEmpty(gender)) 
            {
                gender = gender.Trim();
                items=items.Where(x => x.Gender == Enum.Parse<Gender>(gender));
            }
            if (!string.IsNullOrEmpty(q)) 
            {
                q = q.Trim();
                items = items.Where(x => x.EmployeeNo.Contains(q)
                                ||x.FirstName.Contains(q)
                                ||x.LastName.Contains(q));
            }
            
            return await items.OrderBy(x => x.EmployeeNo).ToListAsync();
        }

这里像拼接sql一样按存在拼接到sql上,在return时才去真正的查询。

 上面查询参数都是写在action里,但是会遇到查询条件很多,而且会经常改变需求的查询条件,这样写就很头疼

所以优化下查询条件

建立DtoPrameters文件夹下面放上查询类

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

namespace Rountine.API.DtoParameters
{
    public class CompanyDtoParameters
    {
        public string CompanyName { get; set; }

        public string searchTerm { get; set; }
    }
}

 

controller:这里是类传入,要指向querystring才可以,否者默认是body里这样会415错误

        public async Task<ActionResult<IEnumerable<CompanyDto>>> GetCompanies([FromQuery] CompanyDtoParameters parameters)
        {
            var companies = await _companyRepository.GetCompaniesAsync(parameters);
            var companyDto = _mapper.Map<IEnumerable<CompanyDto>>(companies);
            
            return Ok(companyDto);
        }

sevice:这个思想没变也是先判断都没的情况下,查询,再使用queryable表达式和参数存在情况下一个个判断

        public async Task<IEnumerable<Company>> GetCompaniesAsync(CompanyDtoParameters parameters)
        {
            if (parameters == null) 
            {
                throw new ArgumentException(nameof(parameters));
            }
            if (string.IsNullOrWhiteSpace(parameters.CompanyName) &&
                string.IsNullOrWhiteSpace(parameters.searchTerm)) 
            {
                 return await _context.companies.ToListAsync();
            }
            var queryExpression = _context.companies as IQueryable<Company>;
            if (!string.IsNullOrEmpty(parameters.CompanyName)) 
            {
                parameters.CompanyName = parameters.CompanyName.Trim();
                queryExpression = queryExpression.Where(x => parameters.CompanyName == x.Name);
            }
            if (!string.IsNullOrEmpty(parameters.searchTerm)) 
            {
                parameters.searchTerm = parameters.searchTerm.Trim();
                queryExpression = queryExpression.Where(x => x.introduction.Contains(parameters.searchTerm)
                                                         || x.Name.Contains(parameters.searchTerm));
            }
            return await queryExpression.ToListAsync();
        }

postman:

 

posted @ 2020-06-29 08:17  蔡老板2019  阅读(724)  评论(0编辑  收藏  举报