爱站网工具包,中装建设是做什么的,有记事本做简易网站,h5游戏大全在线玩系列文章使用 abp cli 搭建项目给项目瘦身#xff0c;让它跑起来完善与美化#xff0c;Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API#xff0c;包装返回模型再说Swagger#xff0c;分组、描述、小绿锁接入GitHub#xff0c;用JWT保护你的API异常处理和… 系列文章使用 abp cli 搭建项目给项目瘦身让它跑起来完善与美化Swagger登场数据访问和代码优先自定义仓储之增删改查统一规范API包装返回模型再说Swagger分组、描述、小绿锁接入GitHub用JWT保护你的API异常处理和日志记录使用Redis缓存数据集成Hangfire实现定时任务处理用AutoMapper搞定对象映射定时任务最佳实战一定时任务最佳实战二定时任务最佳实战三博客接口实战篇一博客接口实战篇二上篇文章完成了分类和标签页面相关的共6个接口本篇继续来写博客增删改查API的业务。供前端查询用的接口还剩下一个这里先补上。友链列表分析返回标题和对应的链接即可传输对象FriendLinkDto.cs。//FriendLinkDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{public class FriendLinkDto{/// summary/// 标题/// /summarypublic string Title { get; set; }/// summary/// 链接/// /summarypublic string LinkUrl { get; set; }}
}
添加查询友链列表接口和缓存接口。//IBlogService.FriendLink.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System.Collections.Generic;
using System.Threading.Tasks;namespace Meowv.Blog.Application.Blog
{public partial interface IBlogService{/// summary/// 查询友链列表/// /summary/// returns/returnsTaskServiceResultIEnumerableFriendLinkDto QueryFriendLinksAsync();}
}
//IBlogCacheService.FriendLink.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;namespace Meowv.Blog.Application.Caching.Blog
{public partial interface IBlogCacheService{/// summary/// 查询友链列表/// /summary/// param namefactory/param/// returns/returnsTaskServiceResultIEnumerableFriendLinkDto QueryFriendLinksAsync(FuncTaskServiceResultIEnumerableFriendLinkDto factory);}
}
接下来实现他们。//BlogCacheService.FriendLink.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using static Meowv.Blog.Domain.Shared.MeowvBlogConsts;namespace Meowv.Blog.Application.Caching.Blog.Impl
{public partial class BlogCacheService{private const string KEY_QueryFriendLinks Blog:FriendLink:QueryFriendLinks;/// summary/// 查询友链列表/// /summary/// param namefactory/param/// returns/returnspublic async TaskServiceResultIEnumerableFriendLinkDto QueryFriendLinksAsync(FuncTaskServiceResultIEnumerableFriendLinkDto factory){return await Cache.GetOrAddAsync(KEY_QueryFriendLinks, factory, CacheStrategy.ONE_DAY);}}
}
//BlogService.FriendLink.cs
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.Domain.Blog;
using Meowv.Blog.ToolKits.Base;
using System.Collections.Generic;
using System.Threading.Tasks;namespace Meowv.Blog.Application.Blog.Impl
{public partial class BlogService{/// summary/// 查询友链列表/// /summary/// returns/returnspublic async TaskServiceResultIEnumerableFriendLinkDto QueryFriendLinksAsync(){return await _blogCacheService.QueryFriendLinksAsync(async () {var result new ServiceResultIEnumerableFriendLinkDto();var friendLinks await _friendLinksRepository.GetListAsync();var list ObjectMapper.MapIEnumerableFriendLink, IEnumerableFriendLinkDto(friendLinks);result.IsSuccess(list);return result;});}}
}
直接查询所有的友链数据这里使用前面讲到的AutoMapper处理对象映射将IEnumerableFriendLink转换为IEnumerableFriendLinkDto。在MeowvBlogAutoMapperProfile.cs中添加一条配置CreateMapFriendLink, FriendLinkDto();在BlogController中添加API。/// summary
/// 查询友链列表
/// /summary
/// returns/returns
[HttpGet]
[Route(friendlinks)]
public async TaskServiceResultIEnumerableFriendLinkDto QueryFriendLinksAsync()
{return await _blogService.QueryFriendLinksAsync();
}
编译运行打开查询友链的API此时没数据手动添加几条数据进去再试试吧。文章管理后台文章管理包含文章列表、新增、更新、删除文章接下来依次完成这些接口。文章列表这里的文章列表和前台的文章列表差不多就是多了一个Id以供编辑和删除使用所以可以新建一个模型类QueryPostForAdminDto继承QueryPostDto添加PostBriefForAdminDto继承PostBriefDto同时新增一个字段主键Id。在QueryPostForAdminDto中隐藏基类成员Posts使用新的接收类型IEnumerablePostBriefForAdminDto。//PostBriefForAdminDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{public class PostBriefForAdminDto : PostBriefDto{/// summary/// 主键/// /summarypublic int Id { get; set; }}
}
//QueryPostForAdminDto.cs
using System.Collections.Generic;namespace Meowv.Blog.Application.Contracts.Blog
{public class QueryPostForAdminDto : QueryPostDto{/// summary/// Posts/// /summarypublic new IEnumerablePostBriefForAdminDto Posts { get; set; }}
}
添加分页查询文章列表的接口QueryPostsForAdminAsync()关于后台的一些接口就不添加缓存了。//IBlogService.Admin.cs
using Meowv.Blog.Application.Contracts;
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using System.Threading.Tasks;namespace Meowv.Blog.Application.Blog
{public partial interface IBlogService{/// summary/// 分页查询文章列表/// /summary/// param nameinput/param/// returns/returnsTaskServiceResultPagedListQueryPostForAdminDto QueryPostsForAdminAsync(PagingInput input);}
}
然后实现这个接口。//BlogService.Admin.cs
using Meowv.Blog.Application.Contracts;
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using Meowv.Blog.ToolKits.Extensions;
using System.Linq;
using System.Threading.Tasks;namespace Meowv.Blog.Application.Blog.Impl
{public partial class BlogService{/// summary/// 分页查询文章列表/// /summary/// param nameinput/param/// returns/returnspublic async TaskServiceResultPagedListQueryPostForAdminDto QueryPostsForAdminAsync(PagingInput input){var result new ServiceResultPagedListQueryPostForAdminDto();var count await _postRepository.GetCountAsync();var list _postRepository.OrderByDescending(x x.CreationTime).PageByIndex(input.Page, input.Limit).Select(x new PostBriefForAdminDto{Id x.Id,Title x.Title,Url x.Url,Year x.CreationTime.Year,CreationTime x.CreationTime.TryToDateTime()}).GroupBy(x x.Year).Select(x new QueryPostForAdminDto{Year x.Key,Posts x.ToList()}).ToList();result.IsSuccess(new PagedListQueryPostForAdminDto(count.TryToInt(), list));return result;}}
}
实现逻辑也非常简单和之前一样就是在Select的时候多了一个Id添加一个新的ControllerBlogController.Admin.cs添加这个接口。//BlogController.Admin.cs
using Meowv.Blog.Application.Contracts;
using Meowv.Blog.Application.Contracts.Blog;
using Meowv.Blog.ToolKits.Base;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using static Meowv.Blog.Domain.Shared.MeowvBlogConsts;namespace Meowv.Blog.HttpApi.Controllers
{public partial class BlogController{/// summary/// 分页查询文章列表/// /summary/// param nameinput/param/// returns/returns[HttpGet][Authorize][Route(admin/posts)][ApiExplorerSettings(GroupName Grouping.GroupName_v2)]public async TaskServiceResultPagedListQueryPostForAdminDto QueryPostsForAdminAsync([FromQuery] PagingInput input){return await _blogService.QueryPostsForAdminAsync(input);}}
}
因为是后台的接口所以加上AuthorizeAttribute指定接口组为GroupName_v2参数方式为[FromQuery]。当没有进行授权的时候是无法访问接口的。新增文章在做新增文章的时候要注意几点不是单纯的添加文章数据就结束了要指定文章分类添加文章的标签。添加标签我这里是从标签库中去取得数据只存标签Id所以添加标签的时候就可能存在添加了标签库中已有的标签。新建一个新增和更新文章的通用输出参数模型类起名EditPostInput继承PostDto然后添加标签Tags字段返回类型IEnumerablestring。//EditPostInput.cs
using System.Collections.Generic;namespace Meowv.Blog.Application.Contracts.Blog.Params
{public class EditPostInput : PostDto{/// summary/// 标签列表/// /summarypublic IEnumerablestring Tags { get; set; }}
}
添加新增文章的接口InsertPostAsync。/// summary
/// 新增文章
/// /summary
/// param nameinput/param
/// returns/returns
TaskServiceResult InsertPostAsync(EditPostInput input);
然后去实现这个接口实现之前配置AutoMapper实体映射。CreateMapEditPostInput, Post().ForMember(x x.Id, opt opt.Ignore());
将EditPostInput转换为Post并且忽略Id字段。/// summary
/// 新增文章
/// /summary
/// param nameinput/param
/// returns/returns
public async TaskServiceResult InsertPostAsync(EditPostInput input)
{var result new ServiceResult();var post ObjectMapper.MapEditPostInput, Post(input);post.Url ${post.CreationTime.ToString( yyyy MM dd ).Replace( , /)}{post.Url}/;await _postRepository.InsertAsync(post);var tags await _tagRepository.GetListAsync();var newTags input.Tags.Where(item !tags.Any(x x.TagName.Equals(item))).Select(item new Tag{TagName item,DisplayName item});await _tagRepository.BulkInsertAsync(newTags);var postTags input.Tags.Select(item new PostTag{PostId post.Id,TagId _tagRepository.FirstOrDefault(x x.TagName item).Id});await _postTagRepository.BulkInsertAsync(postTags);result.IsSuccess(ResponseText.INSERT_SUCCESS);return result;
}
URL字段根据创建时间按照yyyy/MM/dd/name/格式拼接。然后找出是否有新标签有的话批量添加至标签表。再根据input.Tags构建PostTag列表也进行批量保存这样才算是新增好一篇文章最后输出ResponseText.INSERT_SUCCESS常量提示成功。在BlogController.Admin.cs添加API。/// summary
/// 新增文章
/// /summary
/// param nameinput/param
/// returns/returns
[HttpPost]
[Authorize]
[Route(post)]
[ApiExplorerSettings(GroupName Grouping.GroupName_v2)]
public async TaskServiceResult InsertPostAsync([FromBody] EditPostInput input)
{return await _blogService.InsertPostAsync(input);
}
更新文章更新操作和新增操作输入参数一样只新增一个Id用来标识更新那篇文章添加UpdatePostAsync更新文章接口。/// summary
/// 更新文章
/// /summary
/// param nameid/param
/// param nameinput/param
/// returns/returns
TaskServiceResult UpdatePostAsync(int id, EditPostInput input);
同样的实现这个接口。/// summary
/// 更新文章
/// /summary
/// param nameid/param
/// param nameinput/param
/// returns/returns
public async TaskServiceResult UpdatePostAsync(int id, EditPostInput input)
{var result new ServiceResult();var post await _postRepository.GetAsync(id);post.Title input.Title;post.Author input.Author;post.Url ${input.CreationTime.ToString( yyyy MM dd ).Replace( , /)}{input.Url}/;post.Html input.Html;post.Markdown input.Markdown;post.CreationTime input.CreationTime;post.CategoryId input.CategoryId;await _postRepository.UpdateAsync(post);var tags await _tagRepository.GetListAsync();var oldPostTags from post_tags in await _postTagRepository.GetListAsync()join tag in await _tagRepository.GetListAsync()on post_tags.TagId equals tag.Idwhere post_tags.PostId.Equals(post.Id)select new{post_tags.Id,tag.TagName};var removedIds oldPostTags.Where(item !input.Tags.Any(x x item.TagName) tags.Any(t t.TagName item.TagName)).Select(item item.Id);await _postTagRepository.DeleteAsync(x removedIds.Contains(x.Id));var newTags input.Tags.Where(item !tags.Any(x x.TagName item)).Select(item new Tag{TagName item,DisplayName item});await _tagRepository.BulkInsertAsync(newTags);var postTags input.Tags.Where(item !oldPostTags.Any(x x.TagName item)).Select(item new PostTag{PostId id,TagId _tagRepository.FirstOrDefault(x x.TagName item).Id});await _postTagRepository.BulkInsertAsync(postTags);result.IsSuccess(ResponseText.UPDATE_SUCCESS);return result;
}
ResponseText.UPDATE_SUCCESS是常量更新成功。先根据Id查询到数据库中的这篇文章数据然后根据input参数修改需要修改的数据最后保存。注意的是如果修改的时候修改了标签有可能新增也有可能删除也许会又有新增又有删除。这时候就需要注意这里做了一个比较通用的方法找到数据库中当前文章Id的所有Tags然后根据参数input.Tags可以找出被删掉的标签的PostTags的Id调用删除方法删掉即可同时也可以获取到新增的标签批量进行保存。完成上面操作后才保存新加标签与文章对应的数据最后提示更新成功在BlogController.Admin添加API。/// summary
/// 更新文章
/// /summary
/// param nameid/param
/// param nameinput/param
/// returns/returns
[HttpPut]
[Authorize]
[Route(post)]
[ApiExplorerSettings(GroupName Grouping.GroupName_v2)]
public async TaskServiceResult UpdatePostAsync([Required] int id, [FromBody] EditPostInput input)
{return await _blogService.UpdatePostAsync(id, input);
}
[HttpPut]指定请求方式为put请求一般需要修改用put添加用post。[Required]指定参数id必填且是FromQuery的方式input为[FromBody]。更新一下上面新增的数据试试。删除文章删除相对来说就非常简单了一般删除都会做逻辑删除就是避免某些手残删除了有找回的余地我们这里就直接Delete了也没什么重要数据。添加接口DeletePostAsync。/// summary
/// 删除文章
/// /summary
/// param nameid/param
/// returns/returns
TaskServiceResult DeletePostAsync(int id);
实现接口。/// summary
/// 删除文章
/// /summary
/// param nameid/param
/// returns/returns
public async TaskServiceResult DeletePostAsync(int id)
{var result new ServiceResult();var post await _postRepository.GetAsync(id);if (null post){result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith(Id, id));return result;}await _postRepository.DeleteAsync(id);await _postTagRepository.DeleteAsync(x x.PostId id);result.IsSuccess(ResponseText.DELETE_SUCCESS);return result;
}
删除的时候同样去查询一下数据来判断是否存在。ResponseText.DELETE_SUCCESS是添加的常量删除成功删除成功同时也要将post_tags表的标签对应关系也干掉才算完整在BlogController.Admin添加API。/// summary
/// 删除文章
/// /summary
/// param nameid/param
/// returns/returns
[HttpDelete]
[Authorize]
[Route(post)]
[ApiExplorerSettings(GroupName Grouping.GroupName_v2)]
public async TaskServiceResult DeletePostAsync([Required] int id)
{return await _blogService.DeletePostAsync(id);
}
[HttpDelete]指定请求方式是删除资源[Required]指定参数Id必填。删掉上面添加的文章看看效果。至此完成了博客文章的增删改接口未完待续...开源地址https://github.com/Meowv/Blog/tree/blog_tutorial搭配下方课程学习更佳 ↓ ↓ ↓