第 4 章 资源操作
4.1 项目创建
从本章起,我们将创建一个在线图书馆项目,通过这个 Web API 应用程序来实际地熟悉并掌握如何使用 ASP.NET Core 创建 RESTful API 应用
这个项目由两个实体,作者和图书组成,我们将使用数据传输对象(DTO)来表示这两种资源
新建一个 ASP.NET Core 项目 Library.API,项目模板选择 API
创建两个 DTO
namespace Library.API.Models
{
public class AuthorDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
}
namespace Library.API.Models
{
public class BookDto
{
public Guid Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int Pages { get; set; }
public Guid AuthorId { get; set; }
}
}
4.2 使用内存数据
创建内存数据源 LibraryMockData,专门用于提供模拟数据
namespace Library.API.Data
{
public class LibraryMockData
{
// 获取 LibraryMockData 实例
public static LibraryMockData Current { get; } = new LibraryMockData();
public List<AuthorDto> Authors { get; set; }
public List<BookDto> Books { get; set; }
public LibraryMockData()
{
var authorId1 = Guid.NewGuid();
var authorId2 = Guid.NewGuid();
Authors = new List<AuthorDto>
{
new AuthorDto
{
Id = authorId1,
Name = "Author 1",
Age = 46,
Email = "Author.com",
},
new AuthorDto
{
Id = authorId2,
Name = "Author 2",
Age = 38,
Email = "Author2.com",
}
};
Books = new List<BookDto>
{
new BookDto
{
Id = Guid.NewGuid(),
Title = "Book 1",
Description = "Description of Book 1",
Pages = 281,
AuthorId = authorId1,
}, new BookDto
{
Id = Guid.NewGuid(),
Title = "Book 2",
Description = "Description of Book 2",
Pages = 370,
AuthorId = authorId2,
}
};
}
}
}
接下来,我们使用仓储模式来访问 LibraryMockData 类中的数据
仓储模式作为领域动态设计(DDD)的一部分,用于解耦业务逻辑层与数据访问层
实现仓储模式的方法有许多种,最简单的一种是对每一个与数据库交互的业务对象创建一个仓储接口以及实现,还有一种就是创建一个通用的仓储接口,所有其他仓储接口都继承这个接口,下面的例子就是通用的仓储接口
namespace Library.API.Services
{
public interface IRepositoryBase<T>
{
IEnumerable<T> FindAll();
IEnumerable<T> FindByCondition(Expression<Func<T, bool>> expression);
void Create(T entity);
void Update(T entity);
void Delete(T entity);
void Save();
}
}
本章使用第一种方法来实现仓储模式,创建仓储接口
namespace Library.API.Services
{
public interface IAuthorRepository
{
IEnumerable<AuthorDto> GetAuthors();
AuthorDto GetAuthor(Guid authorId);
bool IsAuthorExists(Guid authorId);
}
}
namespace Library.API.Services
{
public interface IBookRepository
{
IEnumerable<BookDto> GetBooksForAuthor(Guid authorId);
BookDto GetBookForAuthor(Guid authorId, Guid bookId);
}
}
接下来创建接口的具体仓储实现
namespace Library.API.Services
{
public class AuthorMockRepository : IAuthorRepository
{
public IEnumerable<AuthorDto> GetAuthors()
{
return LibraryMockData.Current.Authors;
}
public AuthorDto GetAuthor(Guid authorId)
{
var author = LibraryMockData.Current.Authors.FirstOrDefault(au => au.Id == authorId);
return author;
}
public bool IsAuthorExists(Guid authorId)
{
return LibraryMockData.Current.Authors.Any(au => au.Id == authorId);
}
}
}
namespace Library.API.Services
{
public class BookMockRepository : IBookRepository
{
public IEnumerable<BookDto> GetBooksForAuthor(Guid authorId)
{
return LibraryMockData.Current.Books.Where(b => b.AuthorId == authorId).ToList();
}
public BookDto GetBookForAuthor(Guid authorId, Guid bookId)
{
return LibraryMockData.Current.Books.FirstOrDefault(b => b.AuthorId == authorId && b.Id == bookId);
}
}
}
为了在程序中使用上述两个仓储接口,还需要在 ConfigureServices 方法中注入
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddScoped<IAuthorRepository, AuthorMockRepository>();
services.AddScoped<IBookRepository, BookMockRepository>();
}
4.3 创建控制器
首先从依赖注入容器中获取之前定义的仓储接口
namespace Library.API.Controllers
{
[Route("api/authors")]
[ApiController]
public class AuthorController : ControllerBase
{
public IAuthorRepository AuthorRepository { get; }
public AuthorController(IAuthorRepository authorRepository)
{
AuthorRepository = authorRepository;
}
}
}
4.4 获取资源
获取集合
[HttpGet]
public ActionResult<List<AuthorDto>> GetAuthors()
{
return AuthorRepository.GetAuthors().ToList();
}
获取单个资源
[HttpGet("{authorId}")]
public ActionResult<AuthorDto> GetAuthor(Guid authorId)
{
var author = AuthorRepository.GetAuthor(authorId);
if (author == null)
{
return NotFound();
}
else
{
return author;
}
}
获取父/子形式资源
namespace Library.API.Controllers
{
[Route("api/authors/{authorId}/books")]
[ApiController]
public class BookController : ControllerBase
{
public IAuthorRepository AuthorRepository { get; }
public IBookRepository BookRepository { get; }
public BookController(IAuthorRepository authorRepository, IBookRepository bookRepository)
{
AuthorRepository = authorRepository;
BookRepository = bookRepository;
}
[HttpGet]
public ActionResult<List<BookDto>> GetBooks(Guid authorId)
{
if (!AuthorRepository.IsAuthorExists(authorId))
{
return NotFound();
}
return BookRepository.GetBooksForAuthor(authorId).ToList();
}
}
}
获取某一个具体的子级资源
[HttpGet("{bookId}")]
public ActionResult<BookDto> GetBook(Guid authorId, Guid bookId)
{
if (!AuthorRepository.IsAuthorExists(authorId))
{
return NotFound();
}
var targetBook = BookRepository.GetBookForAuthor(authorId, bookId);
if (targetBook == null)
{
return NotFound();
}
return targetBook;
}