决定先开发Web端试试。
新增Web应用:
选择ASP.NET Core Web Application,填写好Name和Location,然后点击OK。
注意红框标出来的,基于.NET Core 2.1版本。登录认证用了微软自带的Identity。Template选择Web Application,也即最新的Razor Pages试图引擎模式。
创建完毕,因为该Template自带的bootstrap还是3.3.7,所以从中文网站下载最新的bootstrap 4.x,同时还需要下载的是popper js插件(如果用到dropdown组件的话,不大明白为何新版Bootstrap不自带这个)。_Layout中的css和js引用也需要同步更新一下,还有就是Navbar菜单需要更新,因为3.x和4.x用的class样式完全不一样。
下面给出示例代码:
1 <nav class="navbar navbar-expand-lg navbar-light bg-light fixed-top"> 2 <div class="container"> 3 <a asp-page="/Index" class="navbar-brand">PTager Shelves</a> 4 <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"> 5 <span class="navbar-toggler-icon"></span> 6 </button> 7 8 <div class="collapse navbar-collapse" id="navbarSupportedContent"> 9 <ul class="navbar-nav mr-auto"> 10 <li class="nav-item active"> 11 <a class="nav-link" asp-page="/Index">Home</a> 12 </li> 13 <li class="nav-item"> 14 <a class="nav-link" asp-page="/About">About</a> 15 </li> 16 <li class="nav-item"> 17 <a class="nav-link" asp-page="/Contact">Contact</a> 18 </li> 19 </ul> 20 <partial name="_LoginPartial" /> 21 </div> 22 </div> 23 </nav>
到此,Web项目基本改造完成了。
当然,前面说到我还是用PTager的现有的登录DB做登录功能,很简单,直接修改appsettings.json文件中的DB连接字符串就好了(如果没有也没事,直接用本地的空DB,注册的时候会提示如何自己创建DB)。
1 { 2 "ConnectionStrings": { 3 "DefaultConnection": "Server=.\SQL2017;Database=PTager;Trusted_Connection=True;MultipleActiveResultSets=true" 4 }, 5 "Logging": { 6 "LogLevel": { 7 "Default": "Warning" 8 } 9 }, 10 "AllowedHosts": "*" 11 }
开始新增New Book功能。
先check豆瓣图书的api能不能正常调用。
在Pages文件夹下New Folder:Shelves,继续在Shelves文件夹New Item=>Razor Pages,Name直接改为:New。
为了接受豆瓣图书API获取的参数信息,先定义一个DoubanBookModel:
1 public class DoubanBookModel 2 { 3 public string title { get; set; } 4 public string subtitle { get; set; } 5 public IEnumerable<string> author { get; set; } 6 public IEnumerable<string> translator { get; set; } 7 public string isbn13 { get; set; } 8 public string isbn10 { get; set; } 9 public string author_intro { get; set; } 10 public string summary { get; set; } 11 public string publisher { get; set; } 12 public string binding { get; set; } 13 public string origin_title { get; set; } 14 public int pages { get; set; } 15 public string image { get; set; } 16 public string pubdate { get; set; } 17 public string catalog { get; set; } 18 public IEnumerable<TagItem> tags { get; set; } 19 public RatingItem rating { get; set; } 20 21 22 public sealed class TagItem 23 { 24 public int count { get; set; } 25 public string name { get; set; } 26 public string title { get; set; } 27 } 28 public sealed class SeriesItem 29 { 30 public int id { get; set; } 31 public string title { get; set; } 32 } 33 public sealed class RatingItem 34 { 35 public int max { get; set; } 36 public int min { get; set; } 37 public int numRaters { get; set; } 38 public string average { get; set; } 39 } 40 }
/Pages/Sheves/New.cshtml:
1 @page 2 @model NewModel 3 @{ 4 ViewData["Title"] = "New Book"; 5 } 6 <nav aria-label="breadcrumb"> 7 <ol class="breadcrumb"> 8 <li class="breadcrumb-item"><a asp-page="/Index">Home</a></li> 9 <li class="breadcrumb-item"><a asp-page="/My Books/Index">My Books</a></li> 10 <li class="breadcrumb-item active" aria-current="page">New Book</li> 11 </ol> 12 </nav> 13 <form method="get"> 14 <div class="input-group input-group-lg mb-3"> 15 <div class="input-group-prepend"> 16 <span class="input-group-text" id="basic-addon1">ISBN #</span> 17 </div> 18 <input name="isbn" class="form-control" autofocus autocomplete="off" placeholder="ISBN #"> 19 <div class="input-group-append"> 20 <button class="btn btn-secondary btn-lg" type="submit">Search</button> 21 </div> 22 </div> 23 </form> 24 <hr /> 25 @if (Model.DoubanBook != null) 26 { 27 var item = Model.DoubanBook; 28 <div class="media"> 29 @*<img class="align-self-start mr-3" src="@item.image" alt="Generic placeholder image">*@ 30 <div class="media-body"> 31 <h3 class="mt-0">@item.title<small class="ml-3">@item.subtitle</small></h3> 32 <p><strong>Origin Title: </strong> @item.origin_title</p> 33 <p><strong>Author: </strong> @string.Join("、", item.author)</p> 34 <p><strong>Translator: </strong> @string.Join("、", item.translator)</p> 35 <p> 36 <strong>Pubdate: </strong> @item.pubdate 37 <strong class="ml-3">Publisher: </strong> @item.publisher 38 <strong class="ml-3">Binding: </strong> @item.binding 39 <strong class="ml-3">Pages: </strong> @item.pages 40 </p> 41 <p> 42 <strong>ISBN: </strong> @item.isbn13 43 </p> 44 <p><strong class="mr-3">Author Intro:</strong> @item.author_intro</p> 45 <p><strong class="mr-3">Summary:</strong>@item.summary</p> 46 @foreach (var tag in item.tags) 47 { 48 <span class="badge badge-info">@tag.name</span> 49 } 50 </div> 51 </div> 52 <hr /> 53 <form method="post"> 54 <input type="hidden" asp-page="IsbnNbr"> 55 <button class="btn btn-warning" type="submit">Add To My Books</button> 56 </form> 57 }
/Pages/Sheves/New.cshtml.cs:
1 public class NewModel : PageModel 2 { 3 [BindProperty] 4 public string IsbnNbr { get; set; } 5 public DoubanBookModel DoubanBook { get; set; } 6 7 public async Task OnGetAsync(string isbn) 8 { 9 IsbnNbr = isbn?.Trim() ?? string.Empty; 10 if (validIsbnNbr(IsbnNbr)) 11 { 12 DoubanBook = await getDoubanBook(); 13 } 14 } 15 private async Task<DoubanBookModel> getDoubanBook() 16 { 17 var url = $"https://api.douban.com/v2/book/isbn/:{IsbnNbr}"; 18 var result = await HttpGetAsync(url); 19 return JsonConvert.DeserializeObject<DoubanBookModel>(result); 20 } 21 public async Task<string> HttpGetAsync(string url, Encoding encoding = null) 22 { 23 using (var httpClient = new HttpClient()) 24 { 25 return await httpClient.GetStringAsync(url); 26 } 27 } 28 29 private bool validIsbnNbr(string isbn) 30 => !string.IsNullOrEmpty(IsbnNbr) && (IsbnNbr.Length == 10 || IsbnNbr.Length == 13); 31 }
好了,现在可以直接从豆瓣图书API获取到返回结果了,并且能展示在页面: