iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 18
2
Modern Web

輕量高效.NET Core開源Blog引擎:Miniblog.Core系列 第 18

18.IT鐵人賽文章同步更新Miniblog文章

延續昨天讀取IT鐵人賽文章的程式,今天要在miniblog同步IT鐵人賽的文章,遇到以下問題:

問題1:

.NET Core如何在非Controller、Action類別的方法取得依賴注射的service singleton物件?

舉例:
資料存儲都使用IBlogService類別,並且在ConfigureServices方法使用AddSingleton註冊到asp.net core web服務。

public void ConfigureServices(IServiceCollection services)
{
	services.AddSingleton<IBlogService, MSSqlBlogService>();
}

假設現在有一個IT鐵人賽Service類別,想要使用BlogService的功能,但是找不到接口取得該singleton物件。

解決方式
在S.O查到文章...Static Factory Class,發現可以在Startup類別Configure方法下,使用IApplicationBuilder物件呼叫ApplicationServices.GetService<注射類別>()取得singleton物件;

舉例
前面注射IBlogService類別,可以使用以下範例取得:

public void ConfigureServices(IServiceCollection services)
{
	services.AddSingleton<IBlogService, MSSqlBlogService>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
	var blogservice = app.ApplicationServices.GetService<IBlogService>();
}

問題2:

.NET Core如何取得依賴注射的設定檔物件?

跟service取得singleton方式不一樣,ApplicationServices沒有提供方法可以取得設定檔資料物件 ,需要換一個類別物件使用,就是Startup下的IConfiguration屬性
它會在Startup物件一開始建立的時候,就將appsettings.json從字串轉換成Configuration物件保存起來,我們只需要使用Get<設定檔類別>就可以取得設定檔物件。

    public class Startup
    {
    	public IConfiguration Configuration { get; }
        public Startup(IConfiguration configuration) => Configuration = configuration;
		public void ConfigureServices(IServiceCollection services)       
			services.Configure<BlogSettings>(section);

			/*取得Blog設定檔物件*/
			var sectionBlog = Configuration.GetSection("blog").Get<BlogSettings>();
		}
	}

程式邏輯部分

解決上述問題後,只需要應用昨天的例子,做成一個同步兩邊的API。

使用Azure Function API Code概念:"要使用API功能,需要有正確的Code參數值",建立"ITIronManKeyCode"屬性
2018-10-18.23.34.39-image.png
2018-10-18.23.37.53-image.png

假如沒有填寫正確code值返回400錯誤,不給使用功能。
2018-10-18.23.44.05-image.png

API Code:

        [Route("/api/ITIromManPostSync/")]
        public IActionResult ITIromManPostSync(string Code)
        {
            if (Code == null || Code != _settings.Value.ITIronManKeyCode)
                return new BadRequestResult();
            ITIronManSyncPostService.SyncPost();
            return View("~/Views/Blog/Index.cshtml");
        }

將想要查詢的文章系列連接以ITIronManArticleURI Json陣列保存起來,在Service foreach查詢網頁資料
2018-10-19.01.01.20-image.png

Service部分:
邏輯綜合昨天的程式,將讀取到的文章更新到miniblog的資料庫或是XML文件內

using AngleSharp.Parser.Html;
using Miniblog.Core.Helper;
using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;

namespace Miniblog.Core.Services
{
    public class ITIronManSyncPostService
    {
        private static readonly HtmlParser _parser = new HtmlParser();
        public IList<Post> Posts { get; set; } = new List<Post>();
        private string _url { get; set; }
        public static BlogSettings sectionBlog;
        public static IBlogService blogService;

        public static void SyncPost()
        {
            foreach (var item in sectionBlog.ITIronManArticleURI)
            {
                var lstITposts = ITIronManSyncPostService.GetITIronManPosts(item).Result;
                foreach (var itpost in lstITposts)
                {
                    //檢查有沒有資料,如果有資料更新動作
                    var id = itpost.link.Replace("https://ithelp.ithome.com.tw/articles/", "");
                    var post = blogService.GetPostById(id).Result;
                    if (post != null)
                    {
                        post.Content = itpost.Content;
                        post.Excerpt = $"{itpost.link}" + HtmlHelper.HtmlInnerText(itpost.Content);
                        post.Title = itpost.Title;
                        post.Categories = new string[] { itpost.Article };
                    }
                    //檢查有沒有資料,如果沒有資料做新增動作
                    else
                    {                      
                        post = new Miniblog.Core.Models.Post()
                        {
                            ID = id,
                            Categories = new string[] { itpost.Article },
                            Content = itpost.Content,
                            Excerpt = $"{itpost.link}" + HtmlHelper.HtmlInnerText(itpost.Content),
                            IsMarkDown = true,
                            PubDate = itpost.PubDate,
                            Slug = id,
                            IsPublished = true,
                            Title = itpost.Title
                        };
                    }
                    blogService.SavePost(post);
                };
            }
        }

        public async static Task<IList<Post>> GetITIronManPosts(string url)
        {
            var itironman = new ITIronManSyncPostService();
            itironman._url = url;
            await itironman.ExecuteAsync();
            return itironman.Posts;
        }

        private async Task ExecuteAsync()
        {
            //因為IT鐵人賽只需要三十篇文章,每頁10篇文章,抓取頁數取4頁就好
            for (int i = 1; i < 4; i++)
                await GetITIronManPostsAsync(_url + $"?page={i}");
        }

        private async Task GetITIronManPostsAsync(string url)
        {
            var htmlContent = (await GetAsync(url));
            var document = _parser.Parse(htmlContent);

            //獲取鐵人賽主題
            var article = document.QuerySelector(".qa-list__title--ironman");
            article.RemoveChild(article.QuerySelector("span"));/*移除系列文字*/
            var articleText = article.TextContent.Trim();

            //獲取鐵人賽:發布日期、標題、內容、連結
            var allpost = document.QuerySelectorAll(".profile-list__content");
            foreach (var postInfo in allpost)
            {
                var post = new Post();

                var titleAndLinkDom = postInfo.QuerySelector(".qa-list__title>a");
                post.Title = titleAndLinkDom.InnerHtml.Trim();
                post.link = titleAndLinkDom.GetAttribute("href").Trim();
                post.Content = GetPostContentAsync(post.link).Result.Trim();
                post.PubDate = DateTime.Parse(postInfo.QuerySelector(".qa-list__info>.qa-list__info-time").GetAttribute("title").Trim());
                post.Article = articleText;

                Posts.Add(post);
            }
        }

        private async Task<string> GetPostContentAsync(string posturl)
        {
            var htmlContent = (await GetAsync(posturl));
            var document = _parser.Parse(htmlContent);
            return document.QuerySelectorAll(".markdown__style").FirstOrDefault().InnerHtml;
        }

        public async Task<string> GetAsync(string uri)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
            request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

            using (HttpWebResponse response = (HttpWebResponse)await request.GetResponseAsync())
            using (Stream stream = response.GetResponseStream())
            using (StreamReader reader = new StreamReader(stream))
            {
                return await reader.ReadToEndAsync();
            }
        }

        public class Post
        {
            public string Title { get; set; }
            public string link { get; set; }
            public string Content { get; set; }
            public string Article { get; set; }
            public DateTime PubDate { get; set; }
        }
    }
}

效果圖

2018-10-18.23.51.15-image.png


參考資料:


上一篇
17.抓取用戶IT鐵人賽文章
下一篇
19.研究OutputCache、ResponseCache兩快取差異
系列文
輕量高效.NET Core開源Blog引擎:Miniblog.Core30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言