詳解SpringBoot(2.3)應用製作Docker鏡像(官方方案)

關於《SpringBoot-2.3容器化技術》系列

《SpringBoot-2.3容器化技術》系列,旨在和大家一起學習實踐2.3版本帶來的最新容器化技術,讓咱們的Java應用更加適應容器化環境,在雲計算時代依舊緊跟主流,保持競爭力;

全系列文章分為主題和輔助兩部分,主題部分如下:

  1. 《體驗SpringBoot(2.3)應用製作Docker鏡像(官方方案)》;
  2. 《詳解SpringBoot(2.3)應用製作Docker鏡像(官方方案)》;
  3. 《掌握SpringBoot-2.3的容器探針:基礎篇》;
  4. 《掌握SpringBoot-2.3的容器探針:深入篇》;
  5. 《掌握SpringBoot-2.3的容器探針:實戰篇》;

輔助部分是一些參考資料和備忘總結,如下:

  1. 《SpringBoot-2.3鏡像方案為什麼要做多個layer》;
  2. 《設置非root賬號不用sudo直接執行docker命令》;
  3. 《開發階段,將SpringBoot應用快速部署到K8S》;

本篇簡介

在前文,咱們快速體驗了官方推薦的docker鏡像製作方案,但也產生了幾個疑問:

  1. SpringBoot-2.3版本推薦的鏡像構建方案和舊版本比有什麼不同?
  2. pom.xml中spring-boot-maven-plugin插件新增的參數,到底做了什麼?
  3. Dockerfile中,java -Djarmode=layertools -jar application.jar extract這個操作啥意思?

本篇的目標就是解答上述問題,在尋找答案的過程中不斷補全知識點,提升自己;

關鍵知識點:鏡像layer

前文多次提到的鏡像layer到底是什麼,為什麼會有多層layer?有必要先把這個知識點夯實了,請參考文章《SpringBoot-2.3鏡像方案為什麼要做多個layer》

老版本SpringBoot的官方方案

SpringBoot-2.2.0.RELEASE版本為例,官方文檔(
https://docs.spring.io/spring-boot/docs/2.2.0.RELEASE/reference/pdf/spring-boot-reference.pdf)給出的做法如下:

  1. 將SpringBoot工程編譯構建,在target目錄得到jar;
  2. 在target目錄新建dependency文件夾;
  3. 將jar解壓到dependency文件夾;
  4. 編寫Dockerfile文件,內容如下:
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.MyApplication"]
  1. 可見,官方推薦的做法是將整個jar文件解壓,在Dockerfile中多次用COPY命令分別複製,這樣做的好處顯而易見:多個layer,如果鏡像的新版本中只修改了應用代碼,那麼下載鏡像時只會下載/app這個layer,其他部分直接使用本地緩存,這是docker鏡像的常規優化手段;
  2. 上述方案有個小問題:麻煩!!!
  3. 於是2.3.0.RELEASE版本做了些優化,讓事情變得簡單些;

2.3.0.RELEASE版本方案和舊版的區別

2.3.0.RELEASE版本構建Docker的步驟如下:

  1. pom.xml中的spring-boot-maven-plugin插件增加一個配置項;
    2.編譯構建生成jar;
  2. 編寫Dockerfile,裏面用到了多階段構建(multi-stage builds),用工具從jar中提取拆分后,再多次執行COPY命令將拆分后的內容放入鏡像,達到多個layer的目的;

因此,2.3.0.RELEASE版本和舊版本相比有如下變化:

  1. pom.xml中多了個參數;
  2. 構建好jar后,無需自己解壓jar;
  3. Dockefile內容不一樣,舊版是手動解壓jar,再在Dockerfile分別複製,2.3.0.RELEASE是通過java命令從jar中提取出各部分內容

搞清楚了新舊版本的區別,咱們繼續研究下一個問題吧;

pom.xml中spring-boot-maven-plugin插件新增的參數

  1. pring-boot-maven-plugin插件新增參數如下圖所示:

2. 上述參數有啥用?我這邊編譯構建了兩次jar,第一次有上述參數,第二次沒有,將兩次生成的jar解壓后對比,發現用了上述參數后,生成的jar會多出下圖紅框中的兩個文件:

  1. 看看layers.idx文件的內容,如下圖:
  1. 上圖中的內容分別是什麼意思呢?官方已給出了詳細解釋,如下圖紅框:
  1. 綜上所述,layers.idx文件是個清單,裏面記錄了所有要被複制到鏡像中的信息,接下來看看如何使用layers.idx文件,這就涉及到jar包中新增的另一個文件:spring-boot-jarmode-layertools-2.3.0.RELEASE.jar

spring-boot-jarmode-layertools工具

  1. 前面已經介紹過jar中除了layers.idx,還多了個文件:spring-boot-jarmode-layertools-2.3.0.RELEASE.jar ,來看看這個文件的用處;
  2. 進入工程的target目錄,這裏面是編譯后的jar文件(我這裏文件名為dockerlayerdemo-0.0.1-SNAPSHOT.jar),注意此時的spring-boot-maven-plugin插件是帶上了下圖紅框中的參數的:
  1. 執行以下命令:
java -Djarmode=layertools -jar dockerlayerdemo-0.0.1-SNAPSHOT.jar list
  1. 得到結果如下圖所示,是layers.idx文件的內容:
  1. 來看看官方對這個layertools的解釋,list參數的作用上面我們已經體驗過了,重點是紅框中的extract參數,它的作用是從jar中提取構建鏡像所需的內容:
  1. 看到這裏,您是否想到了《體驗SpringBoot(2.3)應用製作Docker鏡像(官方方案)》中Dockerfile的內容,請看下圖的紅框和紅字,是否有種恍然大悟的感覺:jar構建生成清單layers.idx,Dockerfile中根據清單從jar提取文件放入鏡像:

至此,三個問題都已經找到了答案,小結一下:

SpringBoot-2.3.0.RELEASE推薦的鏡像構建方案和舊版本相比有什麼不同

  1. pom.xml中的spring-boot-maven-plugin插件增加一個配置項;
  2. 構建好jar后,舊版本要自己解壓jar,新版不需要;
  3. 新版本的jar中,多了個文件清單layers.idx和鏡像文件處理工具spring-boot-jarmode-layertools-2.3.0.RELEASE.jar
  4. 舊版的Dockefile內容:因為前面解壓好了,所有在Dockerfile里直接複製前面解壓的內容,這裏就有個風險:前一步解壓和當前複製的文件位置要保證一致;
  5. 新版的Dockerfile內容:使用工具spring-boot-jarmode-layertools-2.3.0.RELEASE.jar,根據的layers.idx內容從jar中提取文件,複製到鏡像中;
  6. 新版的Dockerfile中,由於使用了分階段構建,因此從jar提取文件的操作不會保存到鏡像的layer中;

pom.xml中spring-boot-maven-plugin插件新增的參數,到底做了什麼

spring-boot-maven-plugin插件新增的參數,使得編譯構建得到jar中多了兩個文件,如下圖所示:

Dockerfile中,java -Djarmode=layertools -jar application.jar extract這個操作啥意思

  1. java -Djarmode=layertools -jar application.jar extract的作用是從jar中提取文件,這些文件是docker鏡像的一部分;
  2. 上述操作的參數是extract,另外還有兩個參數,官方解釋它們的作用如下:

至此,問題已全部澄清,相信您對SpringBoot-2.3.0.RELEASE官方的鏡像構建方案也足夠了解了,最後是我根據自己的認識畫的流程圖,幫助您快速理解整個構建流程:

歡迎訪問我的GitHub

  • 地址:https://github.com/zq2599/blog_demos
  • 內容:原創文章分類匯總,及配套源碼,涉及Java、Docker、K8S、DevOPS等

歡迎關注我的公眾號:程序員欣宸

https://github.com/zq2599/blog_demos

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※超省錢租車方案

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

※推薦台中搬家公司優質服務,可到府估價

小師妹學JavaIO之:文件系統和WatchService

目錄

  • 簡介
  • 監控的痛點
  • WatchService和文件系統
  • WatchSerice的使用和實現本質
  • 總結

簡介

小師妹這次遇到了監控文件變化的問題,F師兄給小師妹介紹了JDK7 nio中引入的WatchService,沒想到又順道普及了一下文件系統的概念,萬萬沒想到。

監控的痛點

小師妹:F師兄最近你有沒有感覺到呼吸有點困難,后領有點涼颼颼的,說話有點不順暢的那種?

沒有啊小師妹,你是不是秋衣穿反了?

小師妹:不是的F師兄,我講的是心裏的感覺,那種莫須有的壓力,還有一絲悸動纏繞在心。

別繞彎子了小師妹,是不是又遇到問題了。

更多精彩內容且看:

  • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
  • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
  • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
  • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

更多內容請訪問www.flydean.com

小師妹:還是F師兄懂我,這不上次的Properties文件用得非常上手,每次修改Properties文件都要重啟java應用程序,真的是很痛苦。有沒有什麼其他的辦法呢?

辦法當然有,最基礎的辦法就是開一個線程定時去監控屬性文件的最後修改時間,如果修改了就重新加載,這樣不就行了。

小師妹:寫線程啊,這麼麻煩,有沒有什麼更簡單的辦法呢?

就知道你要這樣問,還好我準備的比較充分,今天給你介紹一個JDK7在nio中引入的類WatchService。

WatchService和文件系統

WatchService是JDK7在nio中引入的接口:

監控的服務叫做WatchService,被監控的對象叫做Watchable:

WatchKey register(WatchService watcher,
                      WatchEvent.Kind<?>[] events,
                      WatchEvent.Modifier... modifiers)
        throws IOException;
WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events)
        throws IOException;

Watchable通過register將該對象的WatchEvent註冊到WatchService上。從此只要有WatchEvent發生在Watchable對象上,就會通知WatchService。

WatchEvent有四種類型:

  1. ENTRY_CREATE 目標被創建
  2. ENTRY_DELETE 目標被刪除
  3. ENTRY_MODIFY 目標被修改
  4. OVERFLOW 一個特殊的Event,表示Event被放棄或者丟失

register返回的WatchKey就是監聽到的WatchEvent的集合。

現在來看WatchService的4個方法:

  1. close 關閉watchService
  2. poll 獲取下一個watchKey,如果沒有則返回null
  3. 帶時間參數的poll 在等待的一定時間內獲取下一個watchKey
  4. take 獲取下一個watchKey,如果沒有則一直等待

小師妹:F師兄,那怎麼才能構建一個WatchService呢?

上次文章中說的文件系統,小師妹還記得吧,FileSystem中就有一個獲取WatchService的方法:

public abstract WatchService newWatchService() throws IOException;

我們看下FileSystem的結構圖:

在我的mac系統上,FileSystem可以分為三大類,UnixFileSystem,JrtFileSystem和ZipFileSystem。我猜在windows上面應該還有對應的windows相關的文件系統。小師妹你要是有興趣可以去看一下。

小師妹:UnixFileSystem用來處理Unix下面的文件,ZipFileSystem用來處理zip文件。那JrtFileSystem是用來做什麼的?

哎呀,這就又要扯遠了,為什麼每次問問題都要扯到天邊….

從前當JDK還是9的時候,做了一個非常大的改動叫做模塊化JPMS(Java Platform Module System),這個Jrt就是為了給模塊化系統用的,我們來舉個例子:

public void useJRTFileSystem(){
        String resource = "java/lang/Object.class";
        URL url = ClassLoader.getSystemResource(resource);
        log.info("{}",url);
    }

上面一段代碼我們獲取到了Object這個class的url,我們看下如果是在JDK8中,輸出是什麼:

jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_171.jdk/Contents/Home/jre/lib/rt.jar!/java/lang/Object.class

輸出結果是jar:file表示這個Object class是放在jar文件中的,後面是jar文件的路徑。

如果是在JDK9之後:

jrt:/java.base/java/lang/Object.class

結果是jrt開頭的,java.base是模塊的名字,後面是Object的路徑。看起來是不是比傳統的jar路徑更加簡潔明了。

有了文件系統,我們就可以在獲取系統默認的文件系統的同時,獲取到相應的WatchService:

WatchService watchService = FileSystems.getDefault().newWatchService();

WatchSerice的使用和實現本質

小師妹:F師兄,WatchSerice是咋實現的呀?這麼神奇,為我們省了這麼多工作。

其實JDK提供了這麼多類的目的就是為了不讓我們重複造輪子,之前跟你講監控文件的最簡單辦法就是開一個獨立的線程來監控文件變化嗎?其實…..WatchService就是這樣做的!

PollingWatchService() {
        // TBD: Make the number of threads configurable
        scheduledExecutor = Executors
            .newSingleThreadScheduledExecutor(new ThreadFactory() {
                 @Override
                 public Thread newThread(Runnable r) {
                     Thread t = new Thread(null, r, "FileSystemWatcher", 0, false);
                     t.setDaemon(true);
                     return t;
                 }});
    }

上面的方法就是生成WatchService的方法,小師妹看到沒有,它的本質就是開啟了一個daemon的線程,用來接收監控任務。

下面看下怎麼把一個文件註冊到WatchService上面:

private void startWatcher(String dirPath, String file) throws IOException {
        WatchService watchService = FileSystems.getDefault().newWatchService();
        Path path = Paths.get(dirPath);
        path.register(watchService, ENTRY_MODIFY);

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                watchService.close();
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }));

        WatchKey key = null;
        while (true) {
            try {
                key = watchService.take();
                for (WatchEvent<?> event : key.pollEvents()) {
                    if (event.context().toString().equals(fileName)) {
                        loadConfig(dirPath + file);
                    }
                }
                boolean reset = key.reset();
                if (!reset) {
                    log.info("該文件無法重置");
                    break;
                }
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
    }

上面的關鍵方法就是path.register,其中Path是一個Watchable對象。

然後使用watchService.take來獲取生成的WatchEvent,最後根據WatchEvent來處理文件。

總結

道生一,一生二,二生三,三生萬物。一個簡簡單單的功能其實背後隱藏着…道德經,哦,不對,背後隱藏着道的哲學。

本文的例子https://github.com/ddean2009/learn-java-io-nio

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/java-io-file-watchservice/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※回頭車貨運收費標準

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

這些國產車都用寶馬發動機了!賣不好因為我們不識貨?

還是那句話,用什麼發動機並不是決定銷量好壞的首要因素。有“東瀛寶馬”之稱的馬自達,之所以被不少車迷們津津樂道,除了擁有媲美寶馬的操控,更是因為馬自達掌握不少令“黑科技”眾多的本田都羡慕的技術。創馳藍天(SKYACTIV Technology)則是其中的代表。

叫獸常說選車主要看三大件 – 發動機、變速箱和底盤。作為汽車的“心臟”,發動機的重要性不言而喻,如今各品牌之間都會選擇共享發動機的戰略,以實現共贏。

不少自主品牌車型搭載了其它合資品牌發動機,甚至還有用上寶馬發動機的,但大部分只是空有一顆“最強心”,銷量並沒有達到預期,一起來看看吧。

華頌,這個非常陌生的名字,是華晨集團旗下一個新的自主品牌,其首款車型是進軍中高檔市場的MpV – 華頌7。這款車搭載的引擎相信每一位汽車愛好者都不會陌生,來自BMW非常經典的N20系列發動機,如今居然“下放”到一款國產MpV身上,的確出乎意料。

在這裏就不過多介紹N20的技術亮點,大家只需要知道這款曾榮獲“2011年全球十佳引擎”的發動機近幾年為寶馬立下了汗馬功勞,不過從去年開始BMW的新車型開始陸續換裝全新的“B系列”發動機。

華頌7能搭載BMW的發動機無疑是令華頌非常得意的宣傳點,但在一個全新的國產品牌面前,23.77 – 28.77萬的指導價顯然是蒼白無力的,這也註定了其銷量慘淡的命運。

叫獸說

既然有這樣的好背景,更應該腳踏實地從基礎做起,飯得一口一口的吃,路要一步一步的走。一個全新的國產品牌想憑一台發動機走天下未免有些天真。

說到BMW發動機,就不得不提到由pSA集團(標緻雪鐵龍)與BMW共同研發的THp發動機。

雪鐵龍C4L、標緻408以及MINI等車型均搭載了這款1.6THp發動機,似乎是裝上了有BMW基因的發動機令雪鐵龍很自信,宣傳C4L的時候喜歡弄“百公里加速”、“汽車拔河”這些營銷手段,眼球是賺到了,但對銷量和口碑似乎並沒有起到太大作用。

合作都是為了實現共贏,這款發動機的確融合了兩大集團眾多技術優勢。站在市場角度,BMW減少了研發入門發動機的成本,而能攀上BMW這個“高枝兒”對於標緻雪鐵龍自然是件求之不得的事情。

叫獸說

豪華品牌與普通品牌之間合作本是一件互補的事情,但例如C4L,有一個勁吹噓發動機的功夫,倒不如在其它地方多花心思,以做到全面發展從而贏得銷量。還是那句話,用什麼發動機並不是決定銷量好壞的首要因素。

有“東瀛寶馬”之稱的馬自達,之所以被不少車迷們津津樂道,除了擁有媲美寶馬的操控,更是因為馬自達掌握不少令“黑科技”眾多的本田都羡慕的技術。創馳藍天(SKYACTIV Technology)則是其中的代表。

來自一汽的奔騰便與馬自達有着扯不斷的關係,估計你很難想到奔騰B70這款毫無特色的車居然與非常經典的馬自達6同平台打造。除了發動機之外(非創馳藍天),變速箱、底盤等等均出自馬6,可以說B70就是一款換殼換標的馬6。但事實上從行駛質感到做工品質以及油耗等方面B70並沒有表現出馬6的水準,在不少一線自主品牌對手面前,與馬6同平台打造這個“老掉牙”的賣點也顯得底氣不足。

叫獸說

作為自主品牌里資源最豐富的一汽,如果能少些“腐氣”,多些用心造車的士氣,未來在競爭中自然更有底氣。

說到一汽,不免想到另一位“皇城下”的北汽。北汽紳寶高價收購了瀕臨破產的薩博的大部分技術,隨後推出了自主品牌中又一款進軍中高級車市場的紳寶D70。

D70全系搭載的均是渦輪增壓發動機,從動力參數上看在眾多自主品牌競品里算得上是優秀水平,但隨之而來較高的油耗是網友普遍反映的問題。除此外,較高的售價、無特色的外觀和較為粗糙的做工註定了這款車的命運。

叫獸說

看看捷豹路虎之於奇瑞、沃爾沃之於吉利,反觀薩博之於紳寶,自主品牌在收購國外品牌之後究竟應該怎樣將價值發揮到最大是北汽需要深思並解決的問題。

除了上述幾個品牌外,還有不少自主車型直接向國外品牌採購發動機的例子。來自瀋陽航天三菱發動機出品的“4A”系列和“4G”系列發動機普遍搭載於自主品牌車型上。

(搭載於陸風X5上的三菱4G63S4T發動機)

不僅有陸風、眾泰、海馬、東南等等品牌車型採購過三菱發動機外,哈弗H6是最典型也是最成功的例子。憑藉中庸耐看的外觀、合理的售價以及較可靠的品質,H6成為月銷超5萬的“神車”。為何同樣都是“三菱心”,命運卻有如此大的差別,這是其它品牌需要思考和學習的地方。

希望中國品牌能早日研發出真正屬於自己的優秀髮動機,屆時掌核心技術后,才能挺直了腰板說一聲“Made in China”而不是“OEM in China”。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※教你寫出一流的銷售文案?

※別再煩惱如何寫文案,掌握八大原則!

買SUV就圖個高大帥氣!20萬要買SUV你怎麼也得考慮這幾款

廣汽菲克Jeep-自由光 指導價:20。98-31。58萬如果要數國產化之後表現會比進口版還要優秀的車子,叫獸相信廣汽菲克Jeep自由光算是一輛。國產的自由光在動力匹配和車廂隔音這兩個方面都比進口版的自由光要優秀,實際上在國產中型SUV市場上,自由光也算是一位表現出色的傢伙。

前段時間,二胎政策的放鬆,不少家用車消費者一下子都把目光投向了七座車上面,然而實際上坐滿七個座位的情況真的不多,而且很多時候七座SUV的第三排都是雞肋,成年人坐進去就會很憋屈。所以何苦為難自己和乘客?一輛大五座的汽車其實更加適合我們的日常使用,這甚至已經逐漸變成一種潮流。今天就和看看有哪些大五座的SUV值得我們關注吧。

上汽通用別克-昂科威 指導價:20.99-34.99萬元

昂科威是比較早期的“大五座”SUV,其實以昂科威的車身尺寸,要做七座車型並不是不可以,但它偏偏以座位布置上的差異與同價位的競爭對手漢蘭達錯位競爭,為自己創造出更大的生存空間,而後來火爆的銷量也說明了昂科威的路線是正確的。

外觀上,昂科威的造型飽滿修長又不乏時尚感,大氣得來又不會太老土,這樣的風格符合大部分消費者的審美。飽滿的前臉毫無疑問的採用了別克家族的網瀑式格柵,頗顯霸氣,大燈為氙氣光源並帶透鏡和LED燈帶,大燈造型和車身的匹配也毫無違和感。至於側面和車尾就以簡潔為主了,但是前後的線條在布局上互相溝通,整體感很強。

別克在車廂內飾豪華感的營造上可謂是業界的老手了,用料方面,上方的搪塑材料、中間的木紋飾板以及包圍的真皮面料,絲毫沒有怠慢;而設計上也不低調,懷抱式的布局加寬大的扶手,非對稱式的中控台,按鍵的布局也很合理,既有層次感又實用。多媒體方面,搭載了安吉星系統,支持手機互聯和導航等,功能豐富。

空間方面,調整好座椅以後,身高178cm的體驗者在駕駛位能取得一拳的頭部空間,在後排則有三指的頭部和一拳四指的腿部空間。值得一提的是昂科威的後排地板是全平的,而且後排座椅靠背也可以調節,只是可惜在頭部空間方面稍顯局促。

動力方面,昂科威搭載1.5T+七速雙離合或者2.0T+6AT的動力系統,最大馬力分別是169ps和260ps,1.5T車型的話動力還是稍失從容,畢竟馬力和車重都擺在那,需要拉高轉速來維持較理想的動力輸出,雙離合的低速頓挫則依舊存在,且對急加速的響應不夠迅速。相對來說,2.0T版本無論是動力強度還是變速箱的表現都要從容穩定更多,動力輸出流暢有後勁,所以更推薦2.0T的昂科威。

廣汽本田-冠道 指導價:26.98-32.98萬元

冠道這款車還未上市的時候就已經賺足眼球了,畢竟它是本田在國內的第一款中型SUV,但同時,冠道身上又集合了很多新技術,如2.0T的地球夢發動機,以及ZF的9AT變速箱,都是第一次出現在國內的本田車上,所以該車自然就吸引了不少的眼球了。

冠道的外觀硬朗且有肌肉感,雖然車身的高度略矮,但給人的感覺還是很壯碩的。中網寬大的鍍鉻飾條和車身造型很匹配,共同營造出冠道的“大塊頭”形象。細節處也頗具心思,如LED大燈、LED日行燈等的加入,側面的三條曲線更凸顯了車身的肌肉感,至於車尾的設計就稍顯普通,但是LED尾燈和雙排氣的設計還是不輸氣勢的。

內飾方面,冠道採用了懸浮式的中控屏和按鍵式排擋設計,科技感十足,同時用料方面也不會十分寒酸,中控台上方採用搪塑+縫線的設計,中間也加入了仿木紋啞光飾板,檔次感更強。而配置方面,座椅加熱和通風、手機互聯、HUD、車道偏離預警等功能都一應俱全,頗有豪華車的風範。

至於空間就是冠道的一大優勢了,前排調整好坐姿之後,身高182cm的體驗者在駕駛座仍有一拳一指的頭部空間,至於他去到後排之後,則有兩拳有餘的腿部和四指的頭部空間,空間表現十分寬裕,只是後排座椅靠背角度不支持調節,這有點可惜。

動力方面前面也提過,2.0T的發動機搭配采埃孚的9AT變速箱,最大馬力272ps,動力輸出比較線性,雖然不是很有爆發力的輸出,但信心還是很充足的,所以動力方面無需擔心。冠道底盤的舒適性也很值得肯定,濾震效果很徹底,但是時速60以上過彎的話,車輛的循跡性就會明顯下降,另外冠道對於高速行駛時遇到的顛簸還是處理得不夠從容,除此以外,冠道可以說本田有史以來隔音做得最好的車子了。

廣汽菲克Jeep-自由光 指導價:20.98-31.58萬

如果要數國產化之後表現會比進口版還要優秀的車子,叫獸相信廣汽菲克Jeep自由光算是一輛。國產的自由光在動力匹配和車廂隔音這兩個方面都比進口版的自由光要優秀,實際上在國產中型SUV市場上,自由光也算是一位表現出色的傢伙。

外觀尺寸方面,自由光比昂科威、冠道等同級對手要稍微小一點。但這也絲毫掩飾不了它的硬漢外形,全車身的黑色下包圍和七孔的豎狀中網,都凸顯了自由光作為一輛Jeep汽車該有的運動氣息,頗具特色的是七孔的中網還使用了曲折式的設計,比傳統的Jeep中網更有個性。

內飾方面,自由光並不像其他的Jeep車型那樣走硬朗的越野風,更多的是體現出一輛城市該有的細膩和豪華。中控台採用了軟質材料,而且上方有雙縫線工藝,提升了檔次感,真皮方向盤很粗壯,握感飽滿。多媒體方面,8.4英寸的Uconnect屏幕包含了導航、倒車影像等功能,功能上不會落後。

座椅的填充厚實,而且前排兩張座椅都有電動調節,算是順應了國內消費者的需求。空間方面,調整好坐姿之後,身高174cm的體驗者在前排有四指的頭部空間,在後排則有兩拳的腿部和兩指的頭部空間,可惜後排的地板凸起比較高,不利於中間位置的乘坐,所以自由光的後排空間表現只能算是中規中矩。

國產自由光的動力系統有兩種,分別是2.0L和2.4L的發動機,搭配9AT的變速箱,最大馬力分別是155ps和175ps,採用了全自然吸氣的班底,算是現時少有的了。兩個排量的自由光都不是以動力為賣點,而且變速箱的降擋反應並不积極,僅能滿足日常的駕駛,所以2.0L的自由光並不適合激烈的駕駛。底盤則偏向於城市SUV的調校,偏向舒適且濾震效果不錯。

上汽大眾斯柯達-柯迪亞克預計上市時間:2017年夏季

斯柯達和大眾之間有着一些微妙的關係,一般這兩個品牌同一時期的同級別同平台車輛的話,先上市的一般是斯柯達的車,速派和邁騰就是這種關係。而在中型SUV領域,柯迪亞克和新途觀之間也是類似的這種微妙關係。至於个中原因,就是大眾決策層的經營策略了,我們也不好猜測。

還是先把注意力放回柯迪亞克身上吧,從“Kodiaq”的名字我們就會知道這輛車的體型不會小,而且外觀風格也會比較硬朗,實際也是如此。前臉雖然依舊是斯柯達家族式的中網,但線條會更加硬朗,而中網兩邊的大燈也是斯柯達首次使用的LED大燈,科技感十足。側面造型修長且硬朗,尾部造型則和速派旅行版相仿,家族氣息更濃,辨識度也不低。

來到內飾,熟悉MQB平台車輛(高爾夫7、速派)的人都不會感到陌生了,平直的線條、對稱式的布局,嚴謹的設計風格。但柯迪亞克的內飾還是比其他大眾系的車輛要顯得開揚一點,簡單說就是更大氣了。配置方面,常規B級車的駕駛輔助和舒適性方面的配置都沒有或缺,另外要說的是該車的儲物空間挺豐富的,而且加入了全景天窗,比較對消費者的胃口。

部分車型(估計是高配)座椅面料為Alcantara加真皮,看着顯檔次,坐着也舒適,比較喜歡這種座椅。空間方面,前排調整好之後,身高185cm的體驗者還能有一拳的頭部空間,而他去到後排則有一拳有餘的腿部和四指的頭部空間,這個表現對於普通身材的成年人來說已經足夠寬敞,不用擔心空間問題。

目前已知柯迪亞克會推出兩種動力系統,分別是1.8T和2.0T的排量,搭配7速DSG變速箱,最大馬力為180ps和220ps,動力表現和速派這類大眾車類似,起步時油門會稍顯慵懶,但隨着油門深度的加大,動力的充沛感會愈加明顯。底盤則維持了德系車紮實質感的基礎上會稍偏硬,但舒適性並不差,只是這種車體型大,方向盤也很輕,駕駛感不太明顯。

總結:越來越多的廠商重視“大五座”SUV這個細分市場,事實上大部分消費者在預算足夠的情況下都更願意買大點的車,至於七座與否,似乎並不是最重要的點。相對緊湊型車,這個級別有更多的空間來提升車輛的產品力,所以這也是建立品牌口碑的一個很好的途徑,相信中型SUV市場的競爭會越來越激烈,更多的好車也會不斷湧出。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

明年6月才國產!全新5系值得等嗎?

0T直六發動機,搭配8AT變速箱。從數據上看,動力單元已經足夠能滿足大部分用戶。除了一貫的后驅,新5系還能選裝四驅系統,依然能保留寶馬的操控特性。而新5系最大的亮點是在車鑰匙加入了一系列的軟件功能,中控觸摸屏也加入了手勢功能。

今年寶馬5系迎來了代號為G30的換代車型。雖然現在只放出了540i一款車型的信息,但新5系的進化已經足以讓買家耐心去等待了。

新5系的造型已經改得很像小一號的7系,頭尾燈的造型也有了變化,加上雙腰線的處理,新5系的顏值還是有所提升。

而視頻里的試駕車540i,是造裝了M系列套件,新5系將搭載2.0T和3.0T直六發動機,搭配8AT變速箱。從數據上看,動力單元已經足夠能滿足大部分用戶。除了一貫的后驅,新5系還能選裝四驅系統,依然能保留寶馬的操控特性。

而新5系最大的亮點是在車鑰匙加入了一系列的軟件功能,中控觸摸屏也加入了手勢功能。這些改變雖然無足輕重,但對於車主的體驗來說一定會更上一個檔次。

據聞新5系明年就會國產,作為一款在國內知名度足夠高的豪華轎車,它的上市一定會吸引到不少的買家。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

網頁設計最專業,超強功能平台可客製化

※別再煩惱如何寫文案,掌握八大原則!

又大又硬!每個男人都想擁有的硬派SUV

G-class擁有的超強非鋪裝行駛能力,折服了不少喜歡越野但又想買買菜的買家。前中后三把電控差速鎖,在野外擁有了全時四驅,你唯一需要擔心的就是自己的車技了。從軍車轉型至民用車,G-class一共發展了三代車型,而換代車型也即將在明年亮相。

全車方方正正,車燈也是極其簡單的圓形,價格不是一般消費者能承擔得起的,擁有三把差速鎖,超強的越野能力,整車所帶來硬派的風格,卻是它最大的魅力。

沒錯,要說的就是奔馳G-class。字母G本義是德語Gelndewagen的縮寫,也就是越野車的意思。從1979年誕生到現在,G-class還是一副方盒子的模樣。模樣沒變,其非承載式車身和梯形大梁的設定也沒變。

G-class擁有的超強非鋪裝行駛能力,折服了不少喜歡越野但又想買買菜的買家。前中后三把電控差速鎖,在野外擁有了全時四驅,你唯一需要擔心的就是自己的車技了。

從軍車轉型至民用車,G-class一共發展了三代車型,而換代車型也即將在明年亮相。沒錯,還是那個模樣,但整車車重減輕了200kg,動力系統除了六缸機外,還會提供4.0L V8雙渦輪增壓發動機。

G-class硬派的風格着實讓不少車迷痴迷,要是能買上一輛,基本就是人生贏家的存在了。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

※教你寫出一流的銷售文案?

※超省錢租車方案

※回頭車貨運收費標準

3萬起 實用/油耗低/自主品牌家轎 完爆陽光威馳

時尚的外觀,售價偏低的V7顯然是將目光瞄準了年輕消費者。內飾犹如外觀一樣,能給人驚喜,啞光色裝飾板與鍍鉻裝飾條的點綴增加不少內飾時尚感,雖然都大面積使用了硬塑料材質,但視覺效果不錯,並沒有廉價的感覺,物理按鍵布局整齊,好用,車廂做工細節很到位。

前言

三、四線城市普通家庭代步工具更多是摩托車或者電動車,隨着經濟發展,普通家庭經濟水平不斷提高,他們對出行工具也有一定需求,購買一輛便宜實用家轎,至於不會日晒雨淋,能遮風擋雨。自主品牌在這家橋市場做的相對出色,不僅價格實惠,配置豐富,而且造車品質並不會比十幾萬車型差,也相當符合三、四線城市經濟水平。

有位遠房親戚,在外打工回來,由於很久沒回來沒交通出行工具,正好手上有點閑錢,想買一款便宜低油耗的車型,就諮詢編者,推薦幾款好用便宜車型給他,於是,給他推薦以下幾款綜合表現都不錯的車型。大家也可以參考下!

上汽通用五菱-寶駿310

(以下簡稱:310)

指導價:3.68-4.98萬

推薦理由:便宜實用、超值之選

平均油耗:5.2L/100km

310外觀設計還是沿用潮流“套臉”式風格,延續了寶駿家族化設計,310更像是寶駿730縮小版一樣,前臉視覺效果並不會給人小氣的感覺,雙腰線設計非常動感,310作為兩廂車型, 散發出年輕動感氣息,也非常符合現階段年輕人審美觀。

雖然310起售價為3.68萬起,其內飾設計反而不會給人廉價視覺感受,而更多的是精緻感,中控設計層次感分明,搭配了木紋飾板與鋼琴漆點綴,視覺效果完全是越級感受。

310車身尺寸為4032*1680*1450mm,軸距為2550mm,空間表現相當出色,符合家庭用車標準,即使是一米八大漢,坐進去也不會顯得擁擠。

310搭載1.2L自然吸氣發動機,傳動系統配備5擋手動變速箱,手動變速箱帶來的是相對優秀經濟燃油性,動力輸出表現只要捨得給油,初斷還是挺輕盈,底盤調教偏向舒適,即使是顛簸路面也能過濾大多數震多,如遇到不平整起伏路面時,後排乘坐還是能明顯感受到拋跳的感覺。但整體來說,寶駿310自身綜合能力還是比較強!

長安汽車-悅翔V7

(以下簡稱:V7)

指導價:5.99-8.79萬

推薦理由:外觀時尚 底盤功底深厚

平均油耗:6.7L/100km

V7外觀就是一大亮點,前臉採用鋼琴漆黑色中網,硬朗凌厲車身線條,渲染着一種朝氣青春氣息,微微上翹尾部設計,突顯運動感。時尚的外觀,售價偏低的V7顯然是將目光瞄準了年輕消費者。

內飾犹如外觀一樣,能給人驚喜,啞光色裝飾板與鍍鉻裝飾條的點綴增加不少內飾時尚感,雖然都大面積使用了硬塑料材質,但視覺效果不錯,並沒有廉價的感覺,物理按鍵布局整齊,好用,車廂做工細節很到位。

V7車身尺寸為4530*1745*1498mm,軸距為2610,車內空間表現不錯,無論是膝部頭部空間都有足夠的余量。

V7搭載1.0T以及1.6L動力總成,傳動系統配備5擋手動、6擋手動、以及4擋手自一體,編者推薦手動擋車型,因為4AT技術也比較老,不僅沒有經濟油耗,反而動力輸出也沒那麼直接,底盤調教偏舒適,能過濾大部分震動,除了隔音方面不是很理想,悅翔V7行駛品質並不會比合資車型差,整體來說,悅翔綜合表現還是不錯,時尚外觀,值得購買。

東風裕隆-銳3

(以下簡稱:銳3)

指導價:5.98-9.68萬

推薦理由:科技感不足 配置豐富。

平均油耗:6.2L/100km

銳3也是延續了納智捷家族化設計,前臉採用大嘴式進氣格柵和凌厲線條設計,視覺效果上並不誇張,而更多是前衛時尚感。

銳3的內飾就是四平八穩的風格,視覺感受上還是很時尚,車內做工用料上,可以說是同級別里最好!中控台使用一部分軟質材料,手感不錯,雖然大部分依然是硬質材質,但給人視覺感受很精緻,物超所值。

車身尺寸為4551*1783*1545mm,軸距為2620,車內空間表現比入門級緊湊車陽光還大,腿部空間很寬敞。

銳3搭載1.6L自然吸氣發動機,傳動系統配備5擋手動以及CVT變速箱,CVT變速箱輸出線性而平順,油耗表現也不錯,如果想激進一些,則选手動擋車型,而且也更有駕駛樂趣。底盤調校很紮實穩健,懸架對路面顛簸過濾得很透徹,過減速砍也很柔和,並沒什麼過多的拋跳,整體表現舒適性不錯。

編者總結:

總體來說,這三款車綜合表現能力都是非常不錯,非常符合三四線城市經濟水平,不僅價格實惠,而且在做工用料上很厚道。外形設計一點也不馬虎,視覺感受根本就不像是幾萬的家轎,這個必須給自主品牌點贊。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

※回頭車貨運收費標準

1.5T+CVT的SUV只賣9.78萬 它能比哈弗更值得買嗎?

加上方向盤的助力輕盈,所以瑞虎5是一輛舒適好開的SUV車型,雖然它搭載功率較高的1。5T發動機,但是開起來不會讓人有太多激烈駕駛的慾望。瑞虎5油耗高嗎。1。5T手動擋車型的車主口碑油耗:8。5L/100km1。5T CVT車型的車主口碑油耗:9。

說起奇瑞瑞虎5,大家都不會太過陌生。它外觀敦實大氣、空間夠大、定位為緊湊型SUV,而且8.88-15.19萬的售價也比較親民。

2017款的瑞虎5全系採用了一款代號為SQRE4T15的1.5T渦輪增壓發動機,最大功率152馬力,最大扭矩205牛米/2000-4000轉。

這樣的發動機功率表現比較不錯,而採用這款1.5T發動機配合CVT變速箱實際開起來的表現怎樣?

駕駛體驗如何?

進入到車內,較高的坐姿,讓它的視野廣闊,方便察看前方的車流動向。

瑞虎5搭載的CVT變速箱帶有模擬七速的功能,並且提供ECO/SpORT兩種駕駛模式。這兩種駕駛模式差異明顯,在SpORT模式下發動機轉速會明顯提高來提升動力輸出。

在實際開起來的時候,瑞虎5的油門響應比較沉穩,踩下油門時,發動機的轉速會平穩地攀升,加速過程缺少一些激情,開起來有條不紊,但動力還是屬於“夠用”的類型。

底盤採用前麥弗遜式獨立懸架、后雙連桿獨立懸架的結構,它的調校以舒適性為主,所以路面上的大小震動都被過濾得比較徹底。

加上方向盤的助力輕盈,所以瑞虎5是一輛舒適好開的SUV車型,雖然它搭載功率較高的1.5T發動機,但是開起來不會讓人有太多激烈駕駛的慾望。

瑞虎5油耗高嗎?

1.5T手動擋車型的車主口碑油耗:8.5L/100km

1.5T CVT車型的車主口碑油耗:9.3L/100km

瑞虎5的車重在1.5噸左右,並且是一輛緊湊型SUV,所以這樣的油耗表現屬於比較合理的。

競爭對手

長城汽車-哈弗H2s

指導價:8.38-10.28萬

哈弗H2S和瑞虎5的售價區間部分重疊,雖然它們定位有些差別,但是仍然形成了激烈的競爭關係。而哈弗H2S採用的是7擋濕式雙離合變速箱,升擋、降擋都比較利索,所以動力響應性更好。

總結:

瑞虎5是一輛平順舒適的SUV車型,它的空間和舒適性都比較到位,如果你正考慮買一輛10萬左右的SUV,那麼它應該是一輛表現均衡的選擇之一。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

※教你寫出一流的銷售文案?

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

500萬一台的勞斯萊斯SUV要來了 有什麼吸引土豪的新配方?

少於四百萬的話,對於這個級別的SUV來說,可能都算得上是便宜的了。

現在市面上售價最昂貴的SUV車型是賓利添越,398-480萬的售價可謂之天價,如此高昂的售價不僅僅是普通人可望不可及,也會讓眾多傳統意義上的豪華品牌SUV在賓利添越面前顯得黯然失色。

而玩豪華玩奢侈的汽車品牌不僅僅只有賓利一家,汽車業界著名的奢華品牌勞斯萊斯近日公布了兩張路試諜照,而主角正是可能成為勞斯萊斯旗下首款SUV車型,——項目代號Cullinan。

首先要說明的是,Culinan並不是該款SUV正式的名稱,而是勞斯萊斯品牌開發SUV車型項目的代號,該款車型具體名稱暫時不得而知。

從諜照中可以看出,儘管車身上覆蓋著厚重的偽裝,但是前臉是類似現在勞斯萊斯家族式設計,依然是靈感源自帕提農神廟的直瀑式進氣格柵,其他細節處設計暫未公布,但是不妨來看看國外媒體所曝光的渲染圖。

這些圖片僅僅是假想圖,真實程度並不高,但我們不妨可以做一個猜想,在賓利添越配備6.0T W12渦輪增壓發動機的前提下,作為與賓利添越抗衡的一款奢華級SUV,勞斯萊斯SUV也將會匹配一款12缸數的渦輪增壓引擎。

【賓利添越的W12發動機】

而賓利添越作為一款奢華級的SUV,在道路適應性方面同樣有着出色的性能表現,如此,也可以確定勞斯萊斯SUV將會在全球多種地區、多種複雜路況下對其進行嚴苛性較強的測試,以致力於生產出一款性能卓越的車型。

全文總結:無論是勞斯萊斯SUV還是賓利添越,用四五百萬去買一台汽車對於普羅大眾來說終究不太現實,但是作為愛車人士,看看熱鬧過過眼癮也是不錯的選擇,根據目前公布的路試諜照推測,如果進度正常,這款SUV將有可能在2018年左右發布,至於售價嘛……少於四百萬的話,對於這個級別的SUV來說,可能都算得上是便宜的了……本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※回頭車貨運收費標準

台中搬家公司費用怎麼算?

基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(五)

系列文章

  1. 基於 abp vNext 和 .NET Core 開發博客項目 – 使用 abp cli 搭建項目
  2. 基於 abp vNext 和 .NET Core 開發博客項目 – 給項目瘦身,讓它跑起來
  3. 基於 abp vNext 和 .NET Core 開發博客項目 – 完善與美化,Swagger登場
  4. 基於 abp vNext 和 .NET Core 開發博客項目 – 數據訪問和代碼優先
  5. 基於 abp vNext 和 .NET Core 開發博客項目 – 自定義倉儲之增刪改查
  6. 基於 abp vNext 和 .NET Core 開發博客項目 – 統一規範API,包裝返回模型
  7. 基於 abp vNext 和 .NET Core 開發博客項目 – 再說Swagger,分組、描述、小綠鎖
  8. 基於 abp vNext 和 .NET Core 開發博客項目 – 接入GitHub,用JWT保護你的API
  9. 基於 abp vNext 和 .NET Core 開發博客項目 – 異常處理和日誌記錄
  10. 基於 abp vNext 和 .NET Core 開發博客項目 – 使用Redis緩存數據
  11. 基於 abp vNext 和 .NET Core 開發博客項目 – 集成Hangfire實現定時任務處理
  12. 基於 abp vNext 和 .NET Core 開發博客項目 – 用AutoMapper搞定對象映射
  13. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(一)
  14. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(二)
  15. 基於 abp vNext 和 .NET Core 開發博客項目 – 定時任務最佳實戰(三)
  16. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(一)
  17. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(二)
  18. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(三)
  19. 基於 abp vNext 和 .NET Core 開發博客項目 – 博客接口實戰篇(四)

上篇文章完成了文章詳情頁數據查詢和清除緩存的功能。

本篇繼續完成分類、標籤、友情鏈接的後台操作接口,還是那句話,這些純CRUD的內容,建議還是自己動手完成比較好,本篇將不再啰嗦,直接貼代碼,以供參考。

分類

添加接口:查詢分類列表QueryCategoriesForAdminAsync()、新增分類InsertCategoryAsync(...)、更新分類UpdateCategoryAsync(...)、刪除分類DeleteCategoryAsync(...)

#region Categories

/// <summary>
/// 查詢分類列表
/// </summary>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> QueryCategoriesForAdminAsync();

/// <summary>
/// 新增分類
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<ServiceResult> InsertCategoryAsync(EditCategoryInput input);

/// <summary>
/// 更新分類
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
Task<ServiceResult> UpdateCategoryAsync(int id, EditCategoryInput input);

/// <summary>
/// 刪除分類
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<ServiceResult> DeleteCategoryAsync(int id);

#endregion Categories

查詢分類列表需要返回的模型類QueryCategoryForAdminDto.cs

//QueryCategoryForAdminDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{
    public class QueryCategoryForAdminDto : QueryCategoryDto
    {
        /// <summary>
        /// 主鍵
        /// </summary>
        public int Id { get; set; }
    }
}

新增分類和更新分類需要的輸入參數EditCategoryInput.cs,直接繼承CategoryDto即可。

//EditCategoryInput.cs
namespace Meowv.Blog.Application.Contracts.Blog.Params
{
    public class EditCategoryInput : CategoryDto
    {
    }
}

分別實現這幾個接口。

/// <summary>
/// 查詢分類列表
/// </summary>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> QueryCategoriesForAdminAsync()
{
    var result = new ServiceResult<IEnumerable<QueryCategoryForAdminDto>>();

    var posts = await _postRepository.GetListAsync();

    var categories = _categoryRepository.GetListAsync().Result.Select(x => new QueryCategoryForAdminDto
    {
        Id = x.Id,
        CategoryName = x.CategoryName,
        DisplayName = x.DisplayName,
        Count = posts.Count(p => p.CategoryId == x.Id)
    });

    result.IsSuccess(categories);
    return result;
}
/// <summary>
/// 新增分類
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ServiceResult> InsertCategoryAsync(EditCategoryInput input)
{
    var result = new ServiceResult();

    var category = ObjectMapper.Map<EditCategoryInput, Category>(input);
    await _categoryRepository.InsertAsync(category);

    result.IsSuccess(ResponseText.INSERT_SUCCESS);
    return result;
}

這裏需要一條AutoMapper配置,將EditCategoryInput轉換為Category,忽略Id字段。

CreateMap<EditCategoryInput, Category>().ForMember(x => x.Id, opt => opt.Ignore());
/// <summary>
/// 更新分類
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ServiceResult> UpdateCategoryAsync(int id, EditCategoryInput input)
{
    var result = new ServiceResult();

    var category = await _categoryRepository.GetAsync(id);
    category.CategoryName = input.CategoryName;
    category.DisplayName = input.DisplayName;

    await _categoryRepository.UpdateAsync(category);

    result.IsSuccess(ResponseText.UPDATE_SUCCESS);
    return result;
}
/// <summary>
/// 刪除分類
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<ServiceResult> DeleteCategoryAsync(int id)
{
    var result = new ServiceResult();

    var category = await _categoryRepository.FindAsync(id);
    if (null == category)
    {
        result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("Id", id));
        return result;
    }

    await _categoryRepository.DeleteAsync(id);

    result.IsSuccess(ResponseText.DELETE_SUCCESS);
    return result;
}

BlogController.Admin.cs中添加接口。

#region Categories

/// <summary>
/// 查詢分類列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize]
[Route("admin/categories")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult<IEnumerable<QueryCategoryForAdminDto>>> QueryCategoriesForAdminAsync()
{
    return await _blogService.QueryCategoriesForAdminAsync();
}

/// <summary>
/// 新增分類
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
[Authorize]
[Route("category")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> InsertCategoryAsync([FromBody] EditCategoryInput input)
{
    return await _blogService.InsertCategoryAsync(input);
}

/// <summary>
/// 更新分類
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
[HttpPut]
[Authorize]
[Route("category")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> UpdateCategoryAsync([Required] int id, [FromBody] EditCategoryInput input)
{
    return await _blogService.UpdateCategoryAsync(id, input);
}

/// <summary>
/// 刪除分類
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
[Authorize]
[Route("category")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> DeleteCategoryAsync([Required] int id)
{
    return await _blogService.DeleteCategoryAsync(id);
}

#endregion Categories

標籤

添加接口:查詢標籤列表QueryTagsForAdminAsync()、新增標籤InsertTagAsync(...)、更新標籤UpdateTagAsync(...)、刪除標籤DeleteTagAsync(...)

#region Tags

/// <summary>
/// 查詢標籤列表
/// </summary>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryTagForAdminDto>>> QueryTagsForAdminAsync();

/// <summary>
/// 新增標籤
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<ServiceResult> InsertTagAsync(EditTagInput input);

/// <summary>
/// 更新標籤
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
Task<ServiceResult> UpdateTagAsync(int id, EditTagInput input);

/// <summary>
/// 刪除標籤
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<ServiceResult> DeleteTagAsync(int id);

#endregion Tags

查詢標籤列表需要返回的模型類QueryTagForAdminDto.cs

//QueryTagForAdminDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{
    public class QueryTagForAdminDto : QueryTagDto
    {
        /// <summary>
        /// 主鍵
        /// </summary>
        public int Id { get; set; }
    }
}

新增標籤和更新標籤需要的輸入參數EditTagInput.cs,直接繼承TagDto即可。

//EditTagInput.cs
namespace Meowv.Blog.Application.Contracts.Blog.Params
{
    public class EditTagInput : TagDto
    {
    }
}

分別實現這幾個接口。

/// <summary>
/// 查詢標籤列表
/// </summary>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryTagForAdminDto>>> QueryTagsForAdminAsync()
{
    var result = new ServiceResult<IEnumerable<QueryTagForAdminDto>>();

    var post_tags = await _postTagRepository.GetListAsync();

    var tags = _tagRepository.GetListAsync().Result.Select(x => new QueryTagForAdminDto
    {
        Id = x.Id,
        TagName = x.TagName,
        DisplayName = x.DisplayName,
        Count = post_tags.Count(p => p.TagId == x.Id)
    });

    result.IsSuccess(tags);
    return result;
}
/// <summary>
/// 新增標籤
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<ServiceResult> InsertTagAsync(EditTagInput input)
{
    var result = new ServiceResult();

    var tag = ObjectMapper.Map<EditTagInput, Tag>(input);
    await _tagRepository.InsertAsync(tag);

    result.IsSuccess(ResponseText.INSERT_SUCCESS);
    return result;
}

這裏需要一條AutoMapper配置,將EditCategoryInput轉換為Tag,忽略Id字段。

CreateMap<EditTagInput, Tag>().ForMember(x => x.Id, opt => opt.Ignore());
/// <summary>
/// 更新標籤
/// </summary>
/// <param name="id"></param>
/// <param name="dto"></param>
/// <returns></returns>
public async Task<ServiceResult> UpdateTagAsync(int id, EditTagInput input)
{
    var result = new ServiceResult();

    var tag = await _tagRepository.GetAsync(id);
    tag.TagName = input.TagName;
    tag.DisplayName = input.DisplayName;

    await _tagRepository.UpdateAsync(tag);

    result.IsSuccess(ResponseText.UPDATE_SUCCESS);
    return result;
}
/// <summary>
/// 刪除標籤
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<ServiceResult> DeleteTagAsync(int id)
{
    var result = new ServiceResult();

    var tag = await _tagRepository.FindAsync(id);
    if (null == tag)
    {
        result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("Id", id));
        return result;
    }

    await _tagRepository.DeleteAsync(id);
    await _postTagRepository.DeleteAsync(x => x.TagId == id);

    result.IsSuccess(ResponseText.DELETE_SUCCESS);
    return result;
}

BlogController.Admin.cs中添加接口。

#region Tags

/// <summary>
/// 查詢標籤列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize]
[Route("admin/tags")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult<IEnumerable<QueryTagForAdminDto>>> QueryTagsForAdminAsync()
{
    return await _blogService.QueryTagsForAdminAsync();
}

/// <summary>
/// 新增標籤
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
[Authorize]
[Route("tag")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> InsertTagAsync([FromBody] EditTagInput input)
{
    return await _blogService.InsertTagAsync(input);
}

/// <summary>
/// 更新標籤
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
[HttpPut]
[Authorize]
[Route("tag")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> UpdateTagAsync([Required] int id, [FromBody] EditTagInput input)
{
    return await _blogService.UpdateTagAsync(id, input);
}

/// <summary>
/// 刪除標籤
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
[Authorize]
[Route("tag")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> DeleteTagAsync([Required] int id)
{
    return await _blogService.DeleteTagAsync(id);
}

#endregion Tags

友鏈

添加接口:查詢友鏈列表QueryFriendLinksForAdminAsync()、新增友鏈InsertFriendLinkAsync(...)、更新友鏈UpdateFriendLinkAsync(...)、刪除友鏈DeleteFriendLinkAsync(...)

#region FriendLinks

/// <summary>
/// 查詢友鏈列表
/// </summary>
/// <returns></returns>
Task<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>> QueryFriendLinksForAdminAsync();

/// <summary>
/// 新增友鏈
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
Task<ServiceResult> InsertFriendLinkAsync(EditFriendLinkInput input);

/// <summary>
/// 更新友鏈
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
Task<ServiceResult> UpdateFriendLinkAsync(int id, EditFriendLinkInput input);

/// <summary>
/// 刪除友鏈
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
Task<ServiceResult> DeleteFriendLinkAsync(int id);

#endregion FriendLinks

查詢友鏈列表需要返回的模型類QueryFriendLinkForAdminDto.cs

//QueryFriendLinkForAdminDto.cs
namespace Meowv.Blog.Application.Contracts.Blog
{
    public class QueryFriendLinkForAdminDto : FriendLinkDto
    {
        /// <summary>
        /// 主鍵
        /// </summary>
        public int Id { get; set; }
    }
}

新增友鏈和更新友鏈需要的輸入參數EditFriendLinkInput.cs,直接繼承FriendLinkDto即可。

//EditFriendLinkInput .cs
namespace Meowv.Blog.Application.Contracts.Blog.Params
{
    public class EditFriendLinkInput : FriendLinkDto
    {
    }
}

分別實現這幾個接口。

/// <summary>
/// 查詢友鏈列表
/// </summary>
/// <returns></returns>
public async Task<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>> QueryFriendLinksForAdminAsync()
{
    var result = new ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>();

    var friendLinks = await _friendLinksRepository.GetListAsync();

    var dto = ObjectMapper.Map<List<FriendLink>, IEnumerable<QueryFriendLinkForAdminDto>>(friendLinks);

    result.IsSuccess(dto);
    return result;
}
/// <summary>
/// 新增友鏈
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ServiceResult> InsertFriendLinkAsync(EditFriendLinkInput input)
{
    var result = new ServiceResult();

    var friendLink = ObjectMapper.Map<EditFriendLinkInput, FriendLink>(input);
    await _friendLinksRepository.InsertAsync(friendLink);

    // 執行清除緩存操作
    await _blogCacheService.RemoveAsync(CachePrefix.Blog_FriendLink);

    result.IsSuccess(ResponseText.INSERT_SUCCESS);
    return result;
}
/// <summary>
/// 更新友鏈
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
public async Task<ServiceResult> UpdateFriendLinkAsync(int id, EditFriendLinkInput input)
{
    var result = new ServiceResult();

    var friendLink = await _friendLinksRepository.GetAsync(id);
    friendLink.Title = input.Title;
    friendLink.LinkUrl = input.LinkUrl;

    await _friendLinksRepository.UpdateAsync(friendLink);

    // 執行清除緩存操作
    await _blogCacheService.RemoveAsync(CachePrefix.Blog_FriendLink);

    result.IsSuccess(ResponseText.UPDATE_SUCCESS);
    return result;
}
/// <summary>
/// 刪除友鏈
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public async Task<ServiceResult> DeleteFriendLinkAsync(int id)
{
    var result = new ServiceResult();

    var friendLink = await _friendLinksRepository.FindAsync(id);
    if (null == friendLink)
    {
        result.IsFailed(ResponseText.WHAT_NOT_EXIST.FormatWith("Id", id));
        return result;
    }

    await _friendLinksRepository.DeleteAsync(id);

    // 執行清除緩存操作
    await _blogCacheService.RemoveAsync(CachePrefix.Blog_FriendLink);

    result.IsSuccess(ResponseText.DELETE_SUCCESS);
    return result;
}

其中查詢友鏈列表和新增友鏈中有兩條AutoMapper配置。

CreateMap<FriendLink, QueryFriendLinkForAdminDto>();

CreateMap<EditFriendLinkInput, FriendLink>().ForMember(x => x.Id, opt => opt.Ignore());

BlogController.Admin.cs中添加接口。

#region FriendLinks

/// <summary>
/// 查詢友鏈列表
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize]
[Route("admin/friendlinks")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult<IEnumerable<QueryFriendLinkForAdminDto>>> QueryFriendLinksForAdminAsync()
{
    return await _blogService.QueryFriendLinksForAdminAsync();
}

/// <summary>
/// 新增友鏈
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[HttpPost]
[Authorize]
[Route("friendlink")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> InsertFriendLinkAsync([FromBody] EditFriendLinkInput input)
{
    return await _blogService.InsertFriendLinkAsync(input);
}

/// <summary>
/// 更新友鏈
/// </summary>
/// <param name="id"></param>
/// <param name="input"></param>
/// <returns></returns>
[HttpPut]
[Authorize]
[Route("friendlink")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> UpdateFriendLinkAsync([Required] int id, [FromBody] EditFriendLinkInput input)
{
    return await _blogService.UpdateFriendLinkAsync(id, input);
}

/// <summary>
/// 刪除友鏈
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
[Authorize]
[Route("friendlink")]
[ApiExplorerSettings(GroupName = Grouping.GroupName_v2)]
public async Task<ServiceResult> DeleteFriendLinkAsync([Required] int id)
{
    return await _blogService.DeleteFriendLinkAsync(id);
}

#endregion

Next

截止本篇,基於 abp vNext 和 .NET Core 開發博客項目 系列的後台API部分便全部開發完成了。

本博客項目系列是我一邊寫代碼一邊記錄后的成果,並不是開發完成后再拿出來寫的,涉及到東西也不是很多,對於新手入門來說應該是夠了的,如果你從中有所收穫請多多轉發分享。

在此,希望大家可以關注一下我的微信公眾號:『阿星Plus』,文章將會首發在公眾號中。

現在有了API,大家可以選擇自己熟悉的方式去開發前端界面,比如目前我博客的線上版本就是用的 ASP.NET Core Web ,感興趣的可以去 release 分支查看。

關於前端部分,看到有人呼籲vue,說實話前端技術不是很厲害,本職主要是後端開發,可能達不到預期效果。

所以我準備入坑 Blazor ,接下來就現學現賣吧,一起學習一起做項目一起進步,加油

開源地址:https://github.com/Meowv/Blog/tree/blog_tutorial

搭配下方課程學習更佳 ↓ ↓ ↓

http://gk.link/a/10iQ7

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計最專業,超強功能平台可客製化

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

※回頭車貨運收費標準

※推薦評價好的iphone維修中心

※教你寫出一流的銷售文案?

台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

台中搬家公司費用怎麼算?