From 0ede28908d371f81696ea35f2a0de86f39435d3e Mon Sep 17 00:00:00 2001 From: luoyunchong Date: Sat, 28 Dec 2019 00:09:25 +0800 Subject: [PATCH] deploy --- 404.html | 27 ++ about/index.html | 35 ++ assets/css/2.styles.2c132230.css | 1 + assets/css/3.styles.a31eb318.css | 1 + assets/css/4.styles.06774d68.css | 1 + assets/css/5.styles.82af18d5.css | 0 assets/css/styles.da0724c2.css | 1 + assets/img/2.8c3cb683.png | Bin 0 -> 15681 bytes assets/img/bottom-logo.385a53c5.png | Bin 0 -> 12205 bytes assets/img/search.9e8df4f9.svg | 1 + assets/js/1.e59787bb.js | 1 + assets/js/10.5fb2e152.js | 1 + assets/js/11.531568d2.js | 1 + assets/js/12.a64efe17.js | 1 + assets/js/13.0374a48d.js | 1 + assets/js/14.679b369a.js | 1 + assets/js/15.4e2834ee.js | 1 + assets/js/16.b94c35d3.js | 1 + assets/js/17.82fc466d.js | 1 + assets/js/18.daa8bb13.js | 1 + assets/js/19.e7d91a01.js | 1 + assets/js/2.2c132230.js | 1 + assets/js/20.497d6fd1.js | 1 + assets/js/21.741dcaf7.js | 1 + assets/js/22.fd0df8ae.js | 1 + assets/js/23.bb985633.js | 1 + assets/js/24.70f91dc9.js | 1 + assets/js/25.68611935.js | 1 + assets/js/26.c4af03ef.js | 1 + assets/js/27.a3860a2a.js | 1 + assets/js/28.72cdef32.js | 1 + assets/js/29.ddaee10b.js | 1 + assets/js/3.a31eb318.js | 1 + assets/js/30.f53cb48a.js | 1 + assets/js/31.7b7b19ea.js | 1 + assets/js/32.c3b644c7.js | 1 + assets/js/33.6564e7fd.js | 1 + assets/js/4.06774d68.js | 1 + assets/js/5.82af18d5.js | 1 + assets/js/6.7b20ecca.js | 1 + assets/js/7.b71ef550.js | 1 + assets/js/8.8cdfa53b.js | 1 + assets/js/9.c05b688d.js | 1 + assets/js/app.da0724c2.js | 8 + colorui/docs/button.html | 65 ++++ colorui/docs/index.html | 35 ++ colorui/docs/text.html | 102 ++++++ dotnetcore/examples/Console-Hello-World.html | 77 +++++ dotnetcore/examples/Console-News-Types.html | 74 ++++ ...Sql-in-asp.net-core-webapi-how-to-use.html | 184 ++++++++++ ...ql-sample-blog-RESTful-use-automapper.html | 323 ++++++++++++++++++ dotnetcore/examples/IdentityServer4.html | 43 +++ dotnetcore/examples/ImCore-Chat.html | 39 +++ dotnetcore/examples/Qiniu-Object-Storage.html | 125 +++++++ dotnetcore/examples/index.html | 44 +++ dotnetcore/lin-cms/IdentityServer4-JWT.html | 92 +++++ .../lin-cms/Newtonsoft.Json-question.html | 160 +++++++++ dotnetcore/lin-cms/Open-source-road.html | 48 +++ ...mbly-Get-Controller-Methods-Attribute.html | 166 +++++++++ dotnetcore/lin-cms/StopWords.html | 194 +++++++++++ dotnetcore/lin-cms/dependency-injection.html | 121 +++++++ dotnetcore/lin-cms/dev-start.html | 43 +++ dotnetcore/lin-cms/dotnetcore-start.html | 49 +++ ...dynamic-authorization-in-asp-net-core.html | 43 +++ dotnetcore/lin-cms/error-code.html | 35 ++ dotnetcore/lin-cms/index.html | 40 +++ dotnetcore/lin-cms/pm-design-comment.html | 44 +++ dotnetcore/lin-cms/pm-design-modules.html | 72 ++++ dotnetcore/lin-cms/spa-github-login.html | 273 +++++++++++++++ dotnetcore/lin-cms/vue-start.html | 96 ++++++ index.html | 52 +++ left-logo.png | Bin 0 -> 9141 bytes logo.png | Bin 0 -> 1396 bytes 73 files changed, 2747 insertions(+) create mode 100644 404.html create mode 100644 about/index.html create mode 100644 assets/css/2.styles.2c132230.css create mode 100644 assets/css/3.styles.a31eb318.css create mode 100644 assets/css/4.styles.06774d68.css create mode 100644 assets/css/5.styles.82af18d5.css create mode 100644 assets/css/styles.da0724c2.css create mode 100644 assets/img/2.8c3cb683.png create mode 100644 assets/img/bottom-logo.385a53c5.png create mode 100644 assets/img/search.9e8df4f9.svg create mode 100644 assets/js/1.e59787bb.js create mode 100644 assets/js/10.5fb2e152.js create mode 100644 assets/js/11.531568d2.js create mode 100644 assets/js/12.a64efe17.js create mode 100644 assets/js/13.0374a48d.js create mode 100644 assets/js/14.679b369a.js create mode 100644 assets/js/15.4e2834ee.js create mode 100644 assets/js/16.b94c35d3.js create mode 100644 assets/js/17.82fc466d.js create mode 100644 assets/js/18.daa8bb13.js create mode 100644 assets/js/19.e7d91a01.js create mode 100644 assets/js/2.2c132230.js create mode 100644 assets/js/20.497d6fd1.js create mode 100644 assets/js/21.741dcaf7.js create mode 100644 assets/js/22.fd0df8ae.js create mode 100644 assets/js/23.bb985633.js create mode 100644 assets/js/24.70f91dc9.js create mode 100644 assets/js/25.68611935.js create mode 100644 assets/js/26.c4af03ef.js create mode 100644 assets/js/27.a3860a2a.js create mode 100644 assets/js/28.72cdef32.js create mode 100644 assets/js/29.ddaee10b.js create mode 100644 assets/js/3.a31eb318.js create mode 100644 assets/js/30.f53cb48a.js create mode 100644 assets/js/31.7b7b19ea.js create mode 100644 assets/js/32.c3b644c7.js create mode 100644 assets/js/33.6564e7fd.js create mode 100644 assets/js/4.06774d68.js create mode 100644 assets/js/5.82af18d5.js create mode 100644 assets/js/6.7b20ecca.js create mode 100644 assets/js/7.b71ef550.js create mode 100644 assets/js/8.8cdfa53b.js create mode 100644 assets/js/9.c05b688d.js create mode 100644 assets/js/app.da0724c2.js create mode 100644 colorui/docs/button.html create mode 100644 colorui/docs/index.html create mode 100644 colorui/docs/text.html create mode 100644 dotnetcore/examples/Console-Hello-World.html create mode 100644 dotnetcore/examples/Console-News-Types.html create mode 100644 dotnetcore/examples/FreeSql-in-asp.net-core-webapi-how-to-use.html create mode 100644 dotnetcore/examples/FreeSql-sample-blog-RESTful-use-automapper.html create mode 100644 dotnetcore/examples/IdentityServer4.html create mode 100644 dotnetcore/examples/ImCore-Chat.html create mode 100644 dotnetcore/examples/Qiniu-Object-Storage.html create mode 100644 dotnetcore/examples/index.html create mode 100644 dotnetcore/lin-cms/IdentityServer4-JWT.html create mode 100644 dotnetcore/lin-cms/Newtonsoft.Json-question.html create mode 100644 dotnetcore/lin-cms/Open-source-road.html create mode 100644 dotnetcore/lin-cms/Reflex-Assembly-Get-Controller-Methods-Attribute.html create mode 100644 dotnetcore/lin-cms/StopWords.html create mode 100644 dotnetcore/lin-cms/dependency-injection.html create mode 100644 dotnetcore/lin-cms/dev-start.html create mode 100644 dotnetcore/lin-cms/dotnetcore-start.html create mode 100644 dotnetcore/lin-cms/dynamic-authorization-in-asp-net-core.html create mode 100644 dotnetcore/lin-cms/error-code.html create mode 100644 dotnetcore/lin-cms/index.html create mode 100644 dotnetcore/lin-cms/pm-design-comment.html create mode 100644 dotnetcore/lin-cms/pm-design-modules.html create mode 100644 dotnetcore/lin-cms/spa-github-login.html create mode 100644 dotnetcore/lin-cms/vue-start.html create mode 100644 index.html create mode 100644 left-logo.png create mode 100644 logo.png diff --git a/404.html b/404.html new file mode 100644 index 0000000..d7a13ae --- /dev/null +++ b/404.html @@ -0,0 +1,27 @@ + + + + + + + IGeekFan的文档 + + + + + + + + +

404

How did we get here?
Take me home.
+ + + diff --git a/about/index.html b/about/index.html new file mode 100644 index 0000000..40cb50d --- /dev/null +++ b/about/index.html @@ -0,0 +1,35 @@ + + + + + + + IGeekFan的文档 + + + + + + + + +
+ + + diff --git a/assets/css/2.styles.2c132230.css b/assets/css/2.styles.2c132230.css new file mode 100644 index 0000000..db168b2 --- /dev/null +++ b/assets/css/2.styles.2c132230.css @@ -0,0 +1 @@ +[data-v-97b6395a]::-webkit-scrollbar-track{border-radius:10px;-webkit-box-shadow:inset 0 0 6px transparent}[data-v-97b6395a]::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.05);background-color:rgba(0,0,0,.2);border-radius:10px;-webkit-box-shadow:inset 1px 1px 0 rgba(0,0,0,.1)}[data-v-97b6395a]::-webkit-scrollbar-thumb,[data-v-97b6395a]::-webkit-scrollbar-track{border-radius:999px;border:5px solid transparent}[data-v-97b6395a]::-webkit-scrollbar-track{box-shadow:inset 1px 1px 5px rgba(0,0,0,.2)}[data-v-97b6395a]::-webkit-scrollbar-thumb{min-height:20px;background-clip:content-box;box-shadow:inset 0 0 0 5px rgba(0,0,0,.2)}[data-v-97b6395a]::-webkit-scrollbar-corner{background:transparent}.badge[data-v-97b6395a]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff;margin-right:5px;background-color:#42b983}.badge.middle[data-v-97b6395a]{vertical-align:middle}.badge.top[data-v-97b6395a]{vertical-align:top}.badge.green[data-v-97b6395a],.badge.tip[data-v-97b6395a]{background-color:#42b983}.badge.error[data-v-97b6395a]{background-color:#da5961}.badge.warn[data-v-97b6395a],.badge.warning[data-v-97b6395a],.badge.yellow[data-v-97b6395a]{background-color:#e7c000} \ No newline at end of file diff --git a/assets/css/3.styles.a31eb318.css b/assets/css/3.styles.a31eb318.css new file mode 100644 index 0000000..1f64892 --- /dev/null +++ b/assets/css/3.styles.a31eb318.css @@ -0,0 +1 @@ +[data-v-b47fce12]::-webkit-scrollbar-track{border-radius:10px;-webkit-box-shadow:inset 0 0 6px transparent}[data-v-b47fce12]::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.05);background-color:rgba(0,0,0,.2);border-radius:10px;-webkit-box-shadow:inset 1px 1px 0 rgba(0,0,0,.1)}[data-v-b47fce12]::-webkit-scrollbar-thumb,[data-v-b47fce12]::-webkit-scrollbar-track{border-radius:999px;border:5px solid transparent}[data-v-b47fce12]::-webkit-scrollbar-track{box-shadow:inset 1px 1px 5px rgba(0,0,0,.2)}[data-v-b47fce12]::-webkit-scrollbar-thumb{min-height:20px;background-clip:content-box;box-shadow:inset 0 0 0 5px rgba(0,0,0,.2)}[data-v-b47fce12]::-webkit-scrollbar-corner{background:transparent}.blue[data-v-b47fce12]{padding:.25rem .5rem;color:#3683d6;font-size:.85em;background:#e4f1ff;border-radius:3px} \ No newline at end of file diff --git a/assets/css/4.styles.06774d68.css b/assets/css/4.styles.06774d68.css new file mode 100644 index 0000000..5e05e8f --- /dev/null +++ b/assets/css/4.styles.06774d68.css @@ -0,0 +1 @@ +.btn[data-v-58baaab4]{height:30px;line-height:30px;background-color:#3683d6;width:70px;border-radius:5px;color:#fff;text-align:center;font-size:14px;display:inline-block;margin:5px}.btn.disabled[data-v-58baaab4]{background-color:#edeef0;color:#c4c9d2}.btn.white[data-v-58baaab4]{background-color:#fff;color:#868f9f;width:68px;border:1px solid #ccc} \ No newline at end of file diff --git a/assets/css/5.styles.82af18d5.css b/assets/css/5.styles.82af18d5.css new file mode 100644 index 0000000..e69de29 diff --git a/assets/css/styles.da0724c2.css b/assets/css/styles.da0724c2.css new file mode 100644 index 0000000..3316c08 --- /dev/null +++ b/assets/css/styles.da0724c2.css @@ -0,0 +1 @@ +.home{padding:4rem 2rem 0;max-width:960px;margin:0 auto}.home .hero{text-align:center}.home .hero img{max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:#6a8bad}.home .hero .action-button{display:inline-block;font-size:1.2rem;color:#fff;background-color:#3683d6;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #2976c8}.home .hero .action-button:hover{background-color:#4a8fda}.home .features{border-top:1px solid #eaecef;padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:#3a5169}.home .feature p{color:#4e6e8e}.home .footer{padding:2.5rem;border-top:1px solid #eaecef;text-align:center;color:#4e6e8e}@media (max-width:920px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.sidebar-button{display:none;width:50px;height:50px;position:absolute;padding:.6rem;top:15px;left:20px}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (max-width:920px){.sidebar-button{display:block}}.search-box{display:inline-block;position:relative;margin-right:1rem}.search-box input::-ms-input-placeholder{color:#c4c9d2}.search-box input::placeholder{color:#c4c9d2}.search-box input{cursor:text;width:250px;height:36px;color:#4e6e8e;display:inline-block;border:1px solid #d1d7de;border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all .2s ease;background:#fff url(https://github.com/vuepress-docs/assets/img/search.9e8df4f9.svg) .6rem .6rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#3683d6}.search-box .suggestions{background:#fff;width:20rem;position:absolute;top:1.5rem;border:1px solid #cfd4db;border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestions.align-right{right:0}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:#5d82a6}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused{background-color:#f3f4f5}.search-box .suggestion.focused a{color:#3683d6}@media (max-width:1060px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width:1060px) and (min-width:920px){.search-box .suggestions{left:0}}@media (max-width:920px){.search-box{margin-right:0}.search-box input{left:1rem}.search-box .suggestions{right:0}}@media (max-width:419px){.search-box .suggestions{width:calc(100vw - 4rem)}.search-box input:focus{width:8rem}}.dropdown-enter,.dropdown-leave-to{height:0!important}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .dropdown-title{display:block}.dropdown-wrapper .dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item h4{margin:.45rem 0 0;border-top:1px solid #eee;padding:.45rem 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover{color:#3683d6}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid #3683d6;border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(1rem - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4{margin-top:0;padding-top:0;border-top:0}@media (max-width:920px){.dropdown-wrapper.open .dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item h4{border-top:0;margin-top:0;padding-top:0}.dropdown-wrapper .nav-dropdown .dropdown-item>a,.dropdown-wrapper .nav-dropdown .dropdown-item h4{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:920px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper .dropdown-title .arrow{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid #ccc;border-bottom:0}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:#fff;padding:.6rem 0;border:1px solid #ddd;border-bottom-color:#ccc;text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.nav-links{display:inline-block}.nav-links a{line-height:1.4rem;color:#45526b}.nav-links a.router-link-active,.nav-links a:hover{color:#3683d6}.nav-links .nav-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:2rem}.nav-links .nav-item:first-child{margin-left:0}.nav-links .repo-link{margin-left:1.5rem}@media (max-width:920px){.nav-links .nav-item,.nav-links .repo-link{margin-left:0}}@media (min-width:920px){.nav-links a.router-link-active,.nav-links a:hover{color:#2c3e50}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;color:#3683d6}}.navbar{position:relative;height:80px;width:100%;display:flex;align-items:center;box-shadow:0 2px 18px #d4d4d4}.navbar a,.navbar img,.navbar span{display:inline-block}.navbar .home-link{line-height:80px;height:80px}.navbar .home-link img{height:24px;min-width:110px;margin-left:3.6vw;margin-top:28px}.navbar .site-name{font-size:1.3rem;font-weight:600;color:#2c3e50;position:relative;margin-left:2.6vw;cursor:pointer}.navbar .search{display:inline-block;margin-left:7.03vw}.navbar .links{padding-left:1.5rem;box-sizing:border-box;background-color:#fff;white-space:nowrap;font-size:1rem;position:absolute;right:$navbar-horizontal-padding;top:$navbar-vertical-padding;display:flex;right:5rem}.navbar .links .search-box{flex:0 0 auto;vertical-align:top}.navbar .links .nav-links{display:flex;justify-content:space-between;font-size:.875rem}@media (max-width:1060px){.navbar{padding-left:4rem}.navbar .can-hide{display:block}.navbar .links{padding-left:0;right:10px}}@media (max-width:920px){.navbar{padding-left:4rem;justify-content:space-around}.navbar .logo{height:32px;width:70px;margin-left:0}.navbar .search{margin-left:40px}.navbar .can-hide{display:none!important}.navbar .links{padding-left:0;right:0}}.page-edit,.page-nav{max-width:900px;margin:0 auto;padding:2rem 4rem}@media (max-width:1060px){.page-edit,.page-nav{padding:2rem}}@media (max-width:419px){.page-edit,.page-nav{padding:1.5rem}}.page{padding-bottom:2rem;margin-right:150px}.page-edit{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block}.page-edit .edit-link a{color:#4e6e8e;margin-right:.25rem}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:#4e6e8e}.page-edit .last-updated .time{font-weight:400;color:#aaa}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid #eaecef;padding-top:1rem;overflow:auto}.page-nav .next{float:right}@media (max-width:920px){.page{margin-right:0}.page-edit .edit-link{margin-bottom:.5rem}.page-edit .last-updated{font-size:.8em;float:none;text-align:left}}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}a.sidebar-link{display:inline-block;color:#45526b;border-left:.25rem solid transparent;width:100%;margin-bottom:4px;box-sizing:border-box;font-size:13px}a.sidebar-link:hover,a.sidebar-link back{color:#3683d6}a.sidebar-link.active{color:#3683d6;padding-top:3p;padding-bottom:3px}.sidebar-group a.sidebar-link{padding-left:4rem}.sidebar-sub-headers a.sidebar-link{padding-top:.25rem;padding-bottom:.25rem;border-left:none}.sidebar-sub-headers a.sidebar-link.active{font-weight:500}ul.sidebar-sub-headers{display:none}.sidebar-group-items li{height:20px;line-height:20px;margin:10px 0}ul.sidebar-group-items .sidebar-link{font-size:13px;font-weight:400}ul.sidebar-group-items li{margin-bottom:15px!important}.sidebar-group:not(.first){margin-top:0}.sidebar-group .sidebar-group{padding-left:.5em}.sidebar-group:not(.collapsable) .sidebar-heading{cursor:auto;color:inherit}.sidebar-heading{color:#45526b;transition:color .15s ease;cursor:pointer;font-size:14px;padding:0 2.5rem;margin-top:0;margin-bottom:10px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;height:40px;line-height:40px;margin-left:5px;position:relative;font-weight:400}.sidebar-heading.open{color:#3683d6;background:#e4f1ff;border-top-left-radius:25px;border-bottom-left-radius:25px}.sidebar-heading:hover{color:#3683d6}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-heading:.open .arrow{top:-.18em}a.text{color:#314659;display:inline-block;width:100%}a.text:hover{color:#3683d6}a.text.router-link-active{color:#3683d6;font-weight:600;border-left-color:#3683d6}.sidebar-group-items{transition:height .1s ease-out;overflow:hidden}.sidebar{box-shadow:1px 0 18px #d4d4d4}.sidebar ul{padding:0;margin:0;list-style-type:none;font-size:14px;color:#45526b}.sidebar a{display:inline-block}.sidebar .nav-links{display:none;border-bottom:1px solid #eaecef;padding:.5rem 0 .75rem 0}.sidebar .nav-links a{font-weight:600}.sidebar .nav-links .nav-item,.sidebar .nav-links .repo-link{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-links{padding:1.5rem 0}@media (max-width:920px){.sidebar .nav-links{display:block}.sidebar .nav-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-links{padding:1rem 0}}.sw-update-popup{position:fixed;right:1em;bottom:1em;padding:1em;border:1px solid #3683d6;border-radius:3px;background:#fff;box-shadow:0 4px 16px rgba(0,0,0,.5);text-align:center}.sw-update-popup button{margin-top:.5em;padding:.25em 2em}.sw-update-popup-enter-active,.sw-update-popup-leave-active{transition:opacity .3s,transform .3s}.sw-update-popup-enter,.sw-update-popup-leave-to{opacity:0;transform:translateY(50%) scale(.5)}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}::-webkit-scrollbar-track{border-radius:10px;-webkit-box-shadow:inset 0 0 6px transparent}::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.05);background-color:rgba(0,0,0,.2);border-radius:10px;-webkit-box-shadow:inset 1px 1px 0 rgba(0,0,0,.1)}::-webkit-scrollbar-thumb,::-webkit-scrollbar-track{border-radius:999px;border:5px solid transparent}::-webkit-scrollbar-track{box-shadow:inset 1px 1px 5px rgba(0,0,0,.2)}::-webkit-scrollbar-thumb{min-height:20px;background-clip:content-box;box-shadow:inset 0 0 0 5px rgba(0,0,0,.2)}::-webkit-scrollbar-corner{background:transparent}#nprogress{pointer-events:none}#nprogress .bar{background:#3683d6;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #3683d6,0 0 5px #3683d6;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#3683d6;border-left-color:#3683d6;border-radius:50%;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.custom-block .custom-block-title{font-weight:600;margin-bottom:-.4rem}.custom-block.danger,.custom-block.tip,.custom-block.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#eff7ff;border-color:#3683d6}.custom-block.warning{background-color:rgba(255,229,100,.3);border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:#2c3e50}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:#2c3e50}.arrow{display:inline-block;width:6px;height:6px}.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:6px solid #ccc}.arrow.down{border-top:1px solid #3683d6;border-right:1px solid #3683d6;transform:rotate(-45deg);margin-bottom:-2px;top:45%}.arrow.down,.arrow.right{transition:all .3s;margin-left:10px;position:absolute;left:85%}.arrow.right{border-top:1px solid #656565;border-right:1px solid #656565;transform:rotate(135deg);top:35%}.arrow.left{border-top:4px solid transparent;border-bottom:4px solid transparent;border-right:6px solid #ccc}.content:not(.custom){max-width:900px;padding:2rem 4rem}@media (max-width:1060px){.content:not(.custom){padding:2rem}}@media (max-width:419px){.content:not(.custom){padding:1.5rem}}.table-of-contents .badge{vertical-align:middle}body,html{padding:0;margin:0}body{font-family:Avenir,Chinese Quote,PingFang SC,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:14px;font-weight:Regular;color:#2c3e50}h1{font-size:28px!important}h2{font-size:24px!important}h3{font-size:20px!important}h4{font-size:16px!important}.page{padding-left:250px}.navbar{z-index:20;right:0;height:4rem;background-color:#fff;box-sizing:border-box;border-bottom:1px solid #eaecef}.navbar,.sidebar-mask{position:fixed;top:0;left:0}.sidebar-mask{z-index:9;width:100vw;height:100vh;display:none}.sidebar{font-size:15px;background-color:#fff;width:250px;position:fixed;z-index:10;margin:0;top:4rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid #eaecef;overflow-y:auto}.content:not(.custom){margin:0 auto;padding:2rem 2.5rem}.content:not(.custom)>:first-child{margin-top:4rem}.content:not(.custom) a:hover{text-decoration:underline}.content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.content:not(.custom) img{max-width:100%}.content.custom{padding:0;margin:0}.content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#3683d6}p a code{font-weight:400}kbd{background:#eee;border:.15rem solid #ddd;border-bottom:.25rem solid #ddd;border-radius:.15rem;padding:0 .15em}blockquote{font-size:1rem;color:#999;border-left:.25rem solid #dfe2e5;margin-left:0;padding-left:1rem}ol,ul{padding-left:1.2em}ul>li>ul{color:#8c98ae}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}.content:not(.custom)>h1,.content:not(.custom)>h2,.content:not(.custom)>h3,.content:not(.custom)>h4,.content:not(.custom)>h5,.content:not(.custom)>h6{margin-top:-3.5rem;padding-top:5rem;margin-bottom:0}.content:not(.custom)>h1:first-child,.content:not(.custom)>h2:first-child,.content:not(.custom)>h3:first-child,.content:not(.custom)>h4:first-child,.content:not(.custom)>h5:first-child,.content:not(.custom)>h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.content:not(.custom)>h1:first-child+.custom-block,.content:not(.custom)>h1:first-child+p,.content:not(.custom)>h1:first-child+pre,.content:not(.custom)>h2:first-child+.custom-block,.content:not(.custom)>h2:first-child+p,.content:not(.custom)>h2:first-child+pre,.content:not(.custom)>h3:first-child+.custom-block,.content:not(.custom)>h3:first-child+p,.content:not(.custom)>h3:first-child+pre,.content:not(.custom)>h4:first-child+.custom-block,.content:not(.custom)>h4:first-child+p,.content:not(.custom)>h4:first-child+pre,.content:not(.custom)>h5:first-child+.custom-block,.content:not(.custom)>h5:first-child+p,.content:not(.custom)>h5:first-child+pre,.content:not(.custom)>h6:first-child+.custom-block,.content:not(.custom)>h6:first-child+p,.content:not(.custom)>h6:first-child+pre{margin-top:2rem}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem}h3{font-size:1.35rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0}a.header-anchor:hover{text-decoration:none}.line-number,code,kbd{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}ol,p,ul{line-height:2}hr{border:0;border-top:1px solid #eaecef}table{border-collapse:collapse;margin:40px 0;display:table;width:100%;max-width:100%;color:#45526b;border:1px solid #dee2e6!important}thead{background:#e8f0f9;text-align:left}thead tr{height:60px}thead tr th{padding-left:.8rem}tr:first-child{border-bottom:1px solid transparent}tr:nth-child(2n){background-color:#f8f9fa}td{padding:.6em 1em;height:30px}td,th:not(:last-child){border:none}td{color:#596c8e}.custom-layout{padding-top:4rem}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-navbar .custom-layout{padding-top:0}@media (min-width:921px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}@media (max-width:1060px){.sidebar{font-size:15px;width:205px}.page{padding-left:205px}}@media (max-width:920px){.sidebar{top:0;padding-top:4rem;transform:translateX(-100%);transition:transform .2s ease}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.icon.outbound{color:#3683d6;display:inline-block}.rightMenuWrapper[data-v-49481e8d]{position:fixed;padding-left:50px;top:100px;right:-18px;width:200px;font-size:14px;padding-right:30px}.rightMenuWrapper li[data-v-49481e8d]{height:30px;line-height:30px;list-style:none;display:flex;position:relative}.rightMenuWrapper li[data-v-49481e8d]:before{content:"";position:absolute;width:5px;height:5px;background:#c4c9d2;border-radius:50%;top:50%;transform:translateY(-50%);left:-21px}.rightMenuWrapper li a[data-v-49481e8d]{text-decoration:none!important;width:200px;height:30px;line-height:30px;color:#596c8e;margin-bottom:10px;position:relative;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rightMenuWrapper li a[data-v-49481e8d]:hover,.rightMenuWrapper li a a.active[data-v-49481e8d]{color:#3683d6}.rightMenuWrapper li img[data-v-49481e8d]{position:absolute;width:20px;height:20px;top:50%;transform:translateY(-50%);left:-30px}@media (max-width:920px){.rightMenuWrapper[data-v-49481e8d]{display:none}}.border[data-v-6397e998]{border:1px solid #dee2e6;padding:5px}.border-wrapper[data-v-4f311c2c]{border:1px solid #dee2e6;position:relative;padding:10px;background:#f7f8f9}.border-wrapper .border-content[data-v-4f311c2c]{border:979797;box-shadow:2px 2px 30px #c7ccd7;background:#fff}.border-wrapper .border-content .slot-content[data-v-4f311c2c]{display:block;padding:10px}.border-wrapper .border-content .slot-code[data-v-4f311c2c]{display:block;padding-left:10px;padding-right:10px}.border-wrapper .border-content .control[data-v-4f311c2c]{height:20px;width:20px;margin:0 auto;border:1px solid #dee2e6;border-radius:50%;display:flex;justify-content:center;align-items:center}.border-wrapper .border-content .control img[data-v-4f311c2c]{color:#3683d6;font-weight:700}.border-wrapper .border-content .control[data-v-4f311c2c]:hover{border:1px solid #3683d6}.tag[data-v-baa11908]{color:#3683d6;padding:.25rem .5rem;margin-left:.5rem;margin-right:.5rem;font-size:.85em;background-color:#e4f1ff;border-radius:.1875rem;display:inline-block}.border-bottom[data-v-174674c0]{display:flex;align-items:center;margin:5px}.border-bottom .text[data-v-174674c0]{font-weight:600;font-size:16px;display:inline-block;white-space:nowrap;padding-right:5px}.border-bottom .border[data-v-174674c0]{width:100%;height:1px;padding-left:5px;padding-right:5px;background:#e2ecf2;display:inline-block}.tip[data-v-4a9392f7]{background:#eff7ff;position:relative;padding-top:15px;padding-left:10px;padding-right:5px;padding-bottom:5px}.tip img[data-v-4a9392f7]{position:absolute;top:-10px;left:40px}.imgWrapper[data-v-d81fa802]{padding:1rem;margin:40px 0;background-color:#fff;border-radius:.1875rem;display:flex;justify-content:center;align-items:center;box-shadow:2px 2px 30px #c7ccd7} \ No newline at end of file diff --git a/assets/img/2.8c3cb683.png b/assets/img/2.8c3cb683.png new file mode 100644 index 0000000000000000000000000000000000000000..95c354e15dc533d1c140986aa29ffdc0773ac7d9 GIT binary patch literal 15681 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 literal 0 HcmV?d00001 diff --git a/assets/img/search.9e8df4f9.svg b/assets/img/search.9e8df4f9.svg new file mode 100644 index 0000000..29616cd --- /dev/null +++ b/assets/img/search.9e8df4f9.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/js/1.e59787bb.js b/assets/js/1.e59787bb.js new file mode 100644 index 0000000..bb2dac9 --- /dev/null +++ b/assets/js/1.e59787bb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[1],{158:function(A,n){A.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABH1JREFUSA3tVl1oHFUUPmdmd2ltklqbpJDiNnXFmgbFktho7YMPNiJSSZM0+CAYSkUELVhM6YuwIPpgoOKDqOBDC0XE2CQoNtQXBUFTTcCi+Wlh1V2TQExsUzcltd3M9Tt3ZjZzZ2fT+OJTL8yeM+eee757fmeJbq//KQL8X3DUSFOcfr7cRsRtxNQMWueeVzOkaITIGqQHNg5y8+jNW9ldM7A6nTpAjuolUikAwq7CE3WcM2RRDz+XGVgN3FptU/aUSlvq9Pa3iZ1+sgAqJyyAFqkipd9dqiwHF3P65YycLWc/6sqGrvoEoIp6DOFaX5h6+dnfjkWprwqsPk0dUGq5vySwDImC10KxFHgGL1SWoc92O3eVht09qdXNH11I2SsTsJYqMWzihqGMi+A+Garf3BAuuLI5oGlULyNfyB/HYNujwktOfRrMr5t77NmevqaUopx0grnKAyvVpmwUDB4x6FPXuGvYLTDwWsejwgtgkYKPqRJg8SV6xaiZ3ZTppGneS4yfH5/66fZSDHv+QZci/+h5c5UHtpy67JUqGppM0sh0Nc1dW6/N1W5Yoqat8/TU/VnadmdeW2PLLSyh0cvxBs3KbqTmwYPpxN4do/mzE8nEpvX/UMu2Wbp74zUAK5q6WkHns7V0eWkdPbPzd3rxkTGybadYySumVzhcaJFbs5UrEkQ/+CK8gF5dnh/6ciIZ73gwQ927L1IitoxKLXYP3SjYdOrHHfTZhRRlFyrorafPk20B3HPD1y2G3qKZME5Jcf3t/HUC13/8tSd++vqFveMUTwAUxSUFI1QekR1+bIze3D9MF2aq6cPvG72CgnldWCFqyRw3lwH8ZMerjTD9ElRO7Gv44wNpC90aASqGfVlz/Rx17srQ57/UU26hkhQqUB7dBR71WmzQhHUnblGmVOEw0jhbV1n9OlXUDCIRGaNV5Jp43N516fN7JmnTHdfp7Hgy0luO4aMhtkLL8Bi3bUWYvzh5Mn1dTxrL6QmGuRhGL/TiTTxRoEdTszSaq9GR0NGA3KdkOz3hqSV3MIDhQ5IVX/Ivx3umBti2es2h4eZby7x8br1rkf7Mo90AqC8aQ3sJeNzqFRu+vSANAQe3PL7l0HGOAdwDCeZYvNKeoZp1Qfs6Aipndh86HmFRi0LAnEO47wsqM6cdfjh3jBPUzhZy7nvlUfFsamED1VQt6aISHVymXZ/B2aCtIG8AI8xfobj2d3en1wWVhOeHELKmLQ1s211s88comkv4UCwWyF787mJdYXtNfhKAXVqnKTq8QZvGAGGOfaTo5pGZ/PwbUCr5+DPr/1J92JNHr9aOl/F3iI5+O1nfybsGxoimvZ3ViWSluDITw3P37mypheDIPY0tw7+O/5ApbkYw+zpfaUVu32Pi98+defdUhEpZkRFq0aqyNh9FuL9hpYbEm6iwi0z2REd09ZmyENEbuhjDWzKvZXTqKYaBIr3tt5kuPtQBZFvEUwHt60vfCNu41XsksH9Ij1BMMz1Y0OOunHNShFIP5868g5zeXmuLwL9T4b6Q2+KejgAAAABJRU5ErkJggg=="},159:function(A,n){A.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABGpJREFUSA3tVVtoXFUU3fvOI53UlmCaKIFmwEhsE7QK0ipFEdHEKpXaZGrp15SINsXUWvBDpBgQRKi0+KKoFeJHfZA+ED9KKoIU2gYD9UejTW4rVIzm0VSTziPzuNu1z507dibTTjL4U/DAzLn3nL3X2o91ziX6f9wMFdh6Jvbm9nNSV0msViVO6tN1Rm7NMu2OpeJ9lWBUTDxrJbYTS0hInuwciu9eLHlFxCLCZEk3MegsJmZ5K/JD6t7FkFdEvGUo1g7qJoG3MHImqRIn8/nzY1K9UPKKiJmtnUqHVE3Gbuay6vJE/N2FEmuxFjW2nUuE0yQXRRxLiTUAzs36zhZvOXJPdX850EVnnLZkB8prodQoM5JGj7Xk2mvC7JB8tG04Ef5PiXtG0UtxupRQSfTnBoCy554x18yJHI6I+G5Eru4LHmPJZEQsrvPUbMiA8G/WgMK7w7I+ez7++o2ANfbrjvaOl1tFMs+htG3IrZH9/hDX1Pr8Tc0UvH8tcX29KzAgIGcEkINyW5BF9x891hw6VYqgJHEk0huccS7vh3C6gTiODL+26huuBtbct8eZnqLML8PkxGYpuPZBqtqwkSjgc4mB5gbgig5i+y0UDK35LMxXisn9xQtK+nd26gTIHsHe/oblK/b29fUmN/8Y+9jAQrnBp56m1LcDlDp9irKTExSKduXJVWSqdBMA08pEJnEIOB3FPPMybu/oeV8zFeYN3xx576Q6RH+VmplE4ncQV5v+5rzSoyOU7PuEAg8g803PwBJ0CExno/jcMbN8tONYeOmHiuUNryvm3fRUy4tMPVLdAGkUhNWuggGrJcXPv+ouCjz0MKUHz1J2/E8IC9nqTabcxgaBYM0hPhD5Y65FsbxRQKxCQrDjDctW7PUM3HuZunFyifSAqEfuzCp48Il24luWUWZoyJCaPR82jE0+kFA643wRFVni4RYSq3ohJO2pZ7B5dO4xkDWbEpossJPLSrPjYID8rS2UHTlvyNxqIGsg674XJJ7vnh5L7PNwC4hh2sjCI96mzszOTpxLF0T7l88Yz7lAuK6OnL8gXLOnTvpzSb22YG8W7us3jSebFHeeqnXRG1vt+MoUM84LQIBmMsCTAcOauTh0T0l0neQK7m2bLMt2mGxU3HYssS0J2cdv5wljlPsrIuZLAG/2DOZIXgCYT8uMGZN+e2kSirfxZOPCsC0f24nTZzspnVn9VePS1Z5vubmAGGXG8ZFno9Hel0yfA5ZPhF7Dh972BQJ2qCpgH67lmWtBYbvk6sz02wjky2vXyz0XErP/kFB619js1BtwfOV4OPRqOQBjy3Qbk18vigUPPSD5ceHnwck7W9bhAqZdd7SuG7w4/P2F/GaJh8c7e9qgow+Q7cGBo+98WsLkuktFqiZabtXuQTu/Y5ETbR0v7tNSFnvrmu6pjdoan2KjMu8q/Hmj1EfCO2ZGfEIbIXKUlw8qaX9/b2oeSJmFksSeT/Fn0V3nSypChh4Gjh74ybO9aeZ/AN2dwciu2/MhAAAAAElFTkSuQmCC"},160:function(A,n){A.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABKFJREFUSA3tVl1oFVcQnrMbrak3QUgkya1akpJYcrUtIqW1JvFBE9LiQ5v6JmJpolbMg32rVrhgoYK0QiMY6i9Y6EMaW5D+xFJaTYItIuK2Kr3+BJNwkxBj05sQY3b3nM6cs2dv9t7NT/vQJw/sndk5M/PNzJkzewGerP+pAmy+ON8lLzUJgA8ZYxYIYZmGYRnctDaWvJJAmTtfP1pvXsBCCPP8QFcCaRkZYACgDZFO4stNIcBCajEOlmmC9XpJ9bAGCaPaPmzPl32dvLSVu3BWCTQs0XQQ6g0DYgwLIoAZbBCdW/i+781o1VVlm/410mw4h06Y7bIPHNyWDyL4FHkX03Q8SrzNhZTZriieckWt7cL6MM85YcLpsi/7O9/iXFT6MswI0DmmpkSaJ0qLxFIm3+i1THHB3zmBH3PYx9CcykcLOeQVVa7QtdxTgQgEleX2AjHYfwA+2ddV77ruGoJUbhGDI09YSNXyMpUt5ylOzxgbUmtOp7NmbNt8v3arjTBfYELmLUV+M+nSawNNAUqpT3ClJWg5I3BLT+cGW/DXNGCa6tx1aakCGEigArTn4TDIPdrXXYKCZNrHLMCOEPvHBlLQ99s9eHB7EB6NTki73CVPQ2F5MSx/uRQixfmq7rK0wYD8w8E905bnPDfwoWs/rfv93NWN/ZfvwsLIU7A09gxECyISeGJkHAau98L97tuw7NXnoPyNF8FcYGLGKsOs0mN3OEyec9esGW/ZEl945dTP34wlR2FZVQWU1q0Cw8Tr7p+hgLLNL0FPxx/Q35mA8aEUrH6nCgwEl0tn7wUiZYJnNRh6DK4UH/k0lfyrsBKdPVv/AriGIQcEDQZ65LBAGe2Rzui9Ybjz7XUppz1/uKBbyVPGkN3ZAeC6hr0x7Nr38N5+EqkoOm17xpoqR9ohQF55ERSvr4Dkr3chNfC3DMzGJlNBElW8w9nsGQvhNGIzDkXzCg8cLK951xHsFBlTJspJNi3ZFIMF2AeDV3q8DNOB+YHi6QTrChDIWDBRi5U5f+ZMfJLu3ccrqxtdxk4SKH336LFxSmkqefwU5T8fhdSdQf9IVKD6aNiwI/hnmcAZ91isYMJIaCUCx9W098+LgruikeTqzqqxKPUwqJyCPJiyemVVZBOijDGjD38Os0jOiSPL1z3SPjXNANbiNPXAdzTfukjjuknNBbyz3nwgTd3AVFqUJ5hpHlq9MveLnWwttUfoygBmvVjuikxND3znrhsELnZk7k+OjIGxeNEkomyLVta0xxn+HZhjBc4YZ/AFjHjz9u3xRZl2BN4aq9nFwWh16IrQ1aHHEd3j1+4/dB9OtH4e29A2H1DyHQRmOSfQZ1Fy7MHBTGB6J/Djq6p3OxyO2cB+4Car7v/o3GXgfAkj23+x9ID1Teoamo/SXcbvSf2PX7Vc8DdCmE1vN9di+32P9/5YR3vLnhCVGUWBjEkr3yh4H8v9CzmsbdhzOKzsJKM90iFdaTMjRPhGVsakRvOaRidljo6H6G7j+ctrJpsP+4COhDIl0La2+FS4+5mlocBaXY5QnGZysIBYoeSsl5qQzrSj/cgNrfuEzlWBfwA+EjrZyWUvpAAAAABJRU5ErkJggg=="},161:function(A,n){A.exports="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAAAXNSR0IArs4c6QAABGZJREFUSA3tVGtsVFUQnnP2brst7G6ry9aCpTZNKrG7aVJrUVvBQkAKNaFajPIDjDE0USo0aqKJmproD2MihjYkNibaGAkIJAZFK62URwoSUx9gDQGhAj5oTd3KdkuXdu/xm7u99G5Z9pH4w0RPcu7Mndc3M2fOIfp//Rs7EOrsfvnKF4eK/oncZDpBFInFupj8aqzzy8p0/OLZpgVMkry6Iq+S4mCou2d1vICpytIDFtJLQhIqz1K62DPa1bM5VaCZdikDK6WAKDxTwGCFVCS3BLsObjV0MyMn+RdJ9NfUwcOH56iwGiJiF4UddRUgStFep9e9VpSVha45JGFSB+7p9amIftKEZMrLTEMKAZEahuQS6CDoIASgalBKcQlHc9G5tOpA1ItIM5mkVMdQ4Xy5PLTZoOxj8jqxUHjw8WAGfJwQL05Q142/JwzB1CelM25padH6/xjyD49dISMGY0i48uaaTQoeCRgypgaPZHVhe9O1tOo9K7CRilVg5Vc0ND2kK9WIGmuQup11mk1S6bw8WlZaQguLC1CnQBNQIycTb+H8XTUL66HXreq41g8+9qwnHA7vBOASOPTWV/hcC/K9fnb8PXCZjp09T6d+GyL/rfnUXLuInI5Ma0wr/72bZleLmtJRq5D564BXrX0hdyIcPAZVPnbzrk3r3Lqu3hJTpmZ1B/rPUMeRrylnloNeX7OKZjkyYmIj8GCmJiuzqisuxCimfmwzhUUlZTvQuPIMTavcuWl9UOm0HX2ctjPOk6goz0N3FRfS3r4f6OLwCFXdXow8AYeNLo0LTdbOrq7onxnf/J8OCMkDDU33oaI3cDWe39HceFTXJ7uVEjfx+SEZM6jBs8yV7SCHptFn3/1IvoK55HE5EYWbKF/NWXTndhMkHo2ZaoCuR7pnOne3btUjEx14mebzdeFrFKXRBAweAJxMbbmfbsl10aFTZ4GJcGxvEwPxwKyy2Hus6H406pNA74nncP/qjOyNauECyovvJS+TsrT8tkLqO3c+CopkNJJ/GkYJPjHAStBc1PaztNmPIO+72W8Cm+8RU14mL1RkJenqFU5gjttJgdBY9DEBMNls6QEDbBx+jpx77vjGQEn8OR7oPfkT2v7uhB7JsGsYFz4S3Gl0JylwzBkj5QEMky8x3rQ2t8r/QURqKwaGhq/muV3RlwpTj8FLDxhN2oeE6+rXPX3zdPjE3IZtrSeOnz43XlpYMBIdLqmclQsCib0w91YDmSXa0erM0Khos8oT8WyLZ9WOHi9BqX0A/wsVxzyP8fxjgD//sPUXjGsLnspHlzdsbIrnYJWxDduyzzMvPvmtJzt3MfQdVpsb8eju9Wv5wxvfwZBswJTsy7TZGz/dteVXq1XdmuZ5Yf1qOwBXorr2/XvaGq36VPi4wOyIap5C+14D60ICR0FPsxyrBLf4XtDLaO1L+3e3bTOkaX5uCMxxVj++OWcsOPkIElgG8PnR2OoCALuyndpHH7//9kiaeP9h878Bmp511/eTzMgAAAAASUVORK5CYII="},164:function(A,n,a){"use strict";a.r(n);var t=a(158),s=a.n(t),r=a(159),e=a.n(r),i=a(160),u=a.n(i),m=a(161),U=a.n(m),c={data:function(){return{src:""}},mounted:function(){switch(this.randomNum(1,4)){case 1:this.src=s.a;break;case 2:this.src=e.a;break;case 3:this.src=u.a;break;case 4:this.src=U.a}},methods:{randomNum:function(A,n){switch(arguments.length){case 1:return parseInt(Math.random()*A+1,10);case 2:return parseInt(Math.random()*(n-A+1)+A,10);default:return 0}}}},o=a(0),d=Object(o.a)(c,function(){var A=this.$createElement,n=this._self._c||A;return n("div",{staticStyle:{display:"inline-block"}},[n("img",{attrs:{src:this.src,alt:""}})])},[],!1,null,"217ea7d4",null);n.default=d.exports}}]); \ No newline at end of file diff --git a/assets/js/10.5fb2e152.js b/assets/js/10.5fb2e152.js new file mode 100644 index 0000000..7b14779 --- /dev/null +++ b/assets/js/10.5fb2e152.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[10],{171:function(t,s,e){"use strict";e.r(s);var a=e(0),n=Object(a.a)({},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),e("p",[t._v("代码托管在GitHub上 "),e("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/tree/master/console-hello-world",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/dotnetcore-examples/tree/master/console-hello-world"),e("OutboundLink")],1)]),t._v(" "),t._m(2),t._v(" "),e("ul",[e("li",[e("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/dotnet/core/tutorials/with-visual-studio-code",target:"_blank",rel:"noopener noreferrer"}},[t._v("C# 和 Visual Studio Code 入门教程"),e("OutboundLink")],1)]),t._v(" "),e("li",[e("a",{attrs:{href:"https://blog.csdn.net/qq_40346899/article/details/80955788",target:"_blank",rel:"noopener noreferrer"}},[t._v("vscode调试运行c#详细操作过程"),e("OutboundLink")],1)]),t._v(" "),e("li",[e("strong",[e("a",{attrs:{href:"https://blog.csdn.net/qin_yu_2010/article/details/83978244",target:"_blank",rel:"noopener noreferrer"}},[t._v("使用Visual Studio Code开发.NET Core看这篇就够了"),e("OutboundLink")],1)]),t._v(" 强烈推荐。")])]),t._v(" "),t._m(3),t._v(" "),e("p",[t._v("创建一个hello-word的console,会输出Hello World!")]),t._v(" "),t._m(4),e("p",[t._v("console-hello-world.csproj")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),e("p",[t._v("在 console-hello-world/bin/Debug/netcoreapp3.0中生成了console-hello-world.dll")]),t._v(" "),t._m(8),e("p",[t._v("修改main函数")]),t._v(" "),t._m(9),t._m(10),e("RightMenu")],1)},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"创建简单hello-world"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#创建简单hello-world","aria-hidden":"true"}},[this._v("#")]),this._v(" 创建简单Hello World")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"源码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#源码","aria-hidden":"true"}},[this._v("#")]),this._v(" 源码")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"相关阅读"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#相关阅读","aria-hidden":"true"}},[this._v("#")]),this._v(" 相关阅读")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"开始"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#开始","aria-hidden":"true"}},[this._v("#")]),this._v(" 开始")])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-bash extra-class"},[s("pre",{pre:!0,attrs:{class:"language-bash"}},[s("code",[s("span",{pre:!0,attrs:{class:"token function"}},[this._v("mkdir")]),this._v(" console-hello-world\n"),s("span",{pre:!0,attrs:{class:"token function"}},[this._v("cd")]),this._v(" console-hello-world\ndotnet new console\ndotnet run\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("OutputType")]),this._v(" 标记指定我们要生成的可执行文件,即控制台应用程序。")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[s("strong",[this._v("TargetFramework")]),this._v(" 标记指定要定位的 .NET 实现代码。 在高级方案中,可以指定多个目标框架,并在单个操作中生成所有目标框架。")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-xml extra-class"},[e("pre",{pre:!0,attrs:{class:"language-xml"}},[e("code",[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("Project")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("Sdk")]),e("span",{pre:!0,attrs:{class:"token attr-value"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("=")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("Microsoft.NET.Sdk"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n\n "),e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("PropertyGroup")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("OutputType")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("Exe"),e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("TargetFramework")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("netcoreapp3.0"),e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("RootNamespace")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("console_hello_world"),e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n "),e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n"),e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token tag"}},[e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("")])]),t._v("\n\n")])])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-PowerShell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-powershell"}},[e("code",[t._v("cd console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("hello"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("world "),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("#要先在console-hello-world目录中")]),t._v("\ndotnet bin"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("Debug"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("netcoreapp3"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("0"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("hello"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("world"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("dll\nHello World\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-c# extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[this._v('using System;\n\nnamespace console_hello_world\n{\n class Program\n {\n static void Main(string[] args)\n {\n if (args.Length > 0)\n {\n Console.WriteLine($"Hello {args[0]}!");\n }\n else\n {\n Console.WriteLine("Hello!");\n }\n }\n }\n}\n')])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("div",{staticClass:"language-PowerShell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-powershell"}},[s("code",[this._v("$ dotnet run "),s("span",{pre:!0,attrs:{class:"token operator"}},[this._v("--")]),this._v(" John\nHello John"),s("span",{pre:!0,attrs:{class:"token operator"}},[this._v("!")]),this._v("\n")])])])}],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/11.531568d2.js b/assets/js/11.531568d2.js new file mode 100644 index 0000000..8af3952 --- /dev/null +++ b/assets/js/11.531568d2.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{170:function(t,s,e){"use strict";e.r(s);var a=e(0),r=Object(a.a)({},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"content"},[t._m(0),t._v(" "),e("p",[t._v("使用PowerShell的dotnet cli命令行创建控制台项目,测试项目,测试项目引用控制台项目。")]),t._v(" "),t._m(1),t._v(" "),e("p",[t._v("代码托管在GitHub上 "),e("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/tree/master/console-news-types",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/dotnetcore-examples/tree/master/console-news-types"),e("OutboundLink")],1)]),t._v(" "),t._m(2),t._v(" "),e("p",[t._v("code :"),e("a",{attrs:{href:"https://github.com/dotnet/samples/blob/master/core/console-apps/NewTypesMsBuild/README.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/dotnet/samples/blob/master/core/console-apps/NewTypesMsBuild/README.md"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("docs: "),e("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/dotnet/core/tutorials/testing-with-cli",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.microsoft.com/zh-cn/dotnet/core/tutorials/testing-with-cli"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("dotnet-add-reference 使用文档 "),e("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet-add-reference",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet-add-reference"),e("OutboundLink")],1)]),t._v(" "),e("p",[t._v("总结如下命令行")]),t._v(" "),t._m(3),e("p",[t._v("其他的代码就看上面的二个链接,把代码复制进去,代码很简单,一个接口,二个实现,main函数调用,测试项目引用控制台项目,")]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._m(6),t._v(" "),t._m(7),e("RightMenu")],1)},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"net-core-简单测试项目"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#net-core-简单测试项目","aria-hidden":"true"}},[this._v("#")]),this._v(" .NET Core 简单测试项目")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"源码"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#源码","aria-hidden":"true"}},[this._v("#")]),this._v(" 源码")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"相关参考"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#相关参考","aria-hidden":"true"}},[this._v("#")]),this._v(" 相关参考")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-PowerShell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-powershell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples> mkdir console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types\n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples> cd "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types\\ \n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types> mkdir src\n\n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types> mkdir test\n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types> "),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("ls")]),t._v("\nMode LastWriteTime Length Name\n"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("\nd-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" 2019"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("6"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("27 20:51 src\nd-"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" 2019"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("6"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("27 20:51 test\n\n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types> cd "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\\src\\\n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types\\src> dotnet new console "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("n NewTypes\n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types\\src> cd "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\\test\\ \n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types\\test> dotnet new xunit "),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("n NewTypesTests \n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types\\test> cd "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\\NewTypesTests\\\n"),e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types\\test\\NewTypesTests> dotnet add reference "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("NewTypes"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("NewTypes"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("csproj\n"),e("span",{pre:!0,attrs:{class:"token comment"}},[t._v("#Reference `..\\..\\src\\NewTypes\\NewTypes.csproj` added to the project.")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("先cd 到src\\NewTypes目录中,执行 "),s("strong",[this._v("dotnet run")])])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-PowerShell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-powershell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types\\src\\NewTypes> dotnet run\nWoof"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\nMeow"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("先cd 到test\\NewTypesTests目录中,执行 "),s("strong",[this._v("dotnet test")]),this._v(",看好代码,测试类 Assert.Equal() 是成功的,如果是Assert.NotEqual() 则Failure")])},function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"language-PowerShell extra-class"},[e("pre",{pre:!0,attrs:{class:"language-powershell"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types\\test\\NewTypesTests> dotnet test\ndotnetcore"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\console"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("news"),e("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("types\\test\\NewTypesTests\\bin\\Debug\\netcoreapp3"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("0\\NewTypesTests"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("dll 的测试运行"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("NETCoreApp"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("Version=v3"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("0"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nMicrosoft "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("R"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" 测试执行命令行工具版本 16"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("0"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("1\n版权所有 "),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("C"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" Microsoft Corporation。保留所有权利。\n\n正在启动测试执行,请稍候"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n\n总测试: 2。已通过: 2。失败: 0。已跳过: 0。\n测试运行成功。\n测试执行时间: 1"),e("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("5134 秒\n")])])])}],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/12.a64efe17.js b/assets/js/12.a64efe17.js new file mode 100644 index 0000000..ab878e0 --- /dev/null +++ b/assets/js/12.a64efe17.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{169:function(t,e,s){"use strict";s.r(e);var r=s(0),a=Object(r.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),s("p",[t._v("主要在介绍FreeSql在ASP.NTE Core WebApi中如何使用的过程,完成一个最简单的博客系统的后端接口。")]),t._v(" "),t._m(2),t._v(" "),s("p",[t._v("国人写的一个功能强大的ORM,FreeSql 支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite,特点:轻量级、可扩展、基于 .NET Standard 跨平台。")]),t._v(" "),t._m(3),t._v(" "),s("p",[t._v("代码托管在GitHub上 "),s("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-freesql",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-freesql"),s("OutboundLink")],1)]),t._v(" "),t._m(4),t._v(" "),s("ul",[s("li",[t._v("FreeSql github "),s("a",{attrs:{href:"https://github.com/2881099/FreeSql",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/2881099/FreeSql"),s("OutboundLink")],1)]),t._v(" "),s("li",[s("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet-new?tabs=netcore22",target:"_blank",rel:"noopener noreferrer"}},[t._v("关于.net core cli中如何使用dotnet new"),s("OutboundLink")],1)]),t._v(" "),s("li",[s("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/aspnet/core/web-api/?view=aspnetcore-2.2",target:"_blank",rel:"noopener noreferrer"}},[t._v("使用 ASP.NET Core 创建 Web API"),s("OutboundLink")],1)]),t._v(" "),s("li",[s("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/web-api-help-pages-using-swagger?view=aspnetcore-2.2",target:"_blank",rel:"noopener noreferrer"}},[t._v("Swagger/OpenAPI 生成接口文档"),s("OutboundLink")],1)]),t._v(" "),s("li",[s("a",{attrs:{href:"https://github.com/domaindrivendev/Swashbuckle.AspNetCore",target:"_blank",rel:"noopener noreferrer"}},[t._v("Swagger GitHub (Swashbuckle.AspNetCore)"),s("OutboundLink")],1)])]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),s("p",[t._v("使用dotnet 命令行创建一个webapi项目,起名为RESTful.FreeSql")]),t._v(" "),t._m(8),s("p",[t._v("然后cd 到RESTful.FreeSql目录,通过dotnet run 命令运行项目")]),t._v(" "),t._m(9),s("p",[t._v("打开浏览器 https://localhost:5001 会出现404")]),t._v(" "),s("p",[t._v("请打开这个地址 https://localhost:5001/api/values ,可看到如下内容。")]),t._v(" "),t._m(10),s("p",[t._v("接下来我们来集成FreeSql,我们以最简单的命令和说明,详细内容去官网看具体内容")]),t._v(" "),s("ul",[s("li",[t._v("官网文档 "),s("a",{attrs:{href:"http://freesql.net/doc",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://freesql.net/doc"),s("OutboundLink")],1)])]),t._v(" "),t._m(11),t._v(" "),s("p",[t._v("要先cd到RESTful.FreeSql目录中。")]),t._v(" "),t._m(12),t._m(13),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://github.com/2881099/FreeSql/blob/master/Docs/codefirst.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("关于CodeFirst,官方文档的介绍"),s("OutboundLink")],1)])]),t._v(" "),s("p",[t._v("代码优先,使用过EntityFramework的应该很清楚这一概念,我的理解就是:在分析数据库表关系时,不通过在数据库中设计表,而是直接在代码中声明对应的类,使用导航属性代替外键关联,通过数据表字段与C#中的类库对应,从而自动生成数据表。")]),t._v(" "),t._m(14),t._v(" "),s("p",[t._v("数据库优先:需求分析后,直接设计数据库,通过数据库中的表,直接生成代码,类。")]),t._v(" "),t._m(15),t._v(" "),t._m(16),t._v(" "),s("p",[t._v("我们以code first 为示例,学习如何使用freesql,实现一个简单的博客。将表内容分为博客表(Blog)和评论表(Post)")]),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),t._m(19),t._v(" "),t._m(20),t._v(" "),s("p",[t._v("建一个Domain文件夹,用于存放数据库表中对应的实体类。")]),t._v(" "),t._m(21),t._v(" "),s("h4",{attrs:{id:"_1-column属性介绍,大家可以看源码,解析"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#_1-column属性介绍,大家可以看源码,解析","aria-hidden":"true"}},[t._v("#")]),t._v(" 1. Column属性介绍,大家可以看"),s("a",{attrs:{href:"https://github.com/2881099/FreeSql/blob/f8c3608fdac2933b528605cc46b21b71c79eaacb/FreeSql/DataAnnotations/ColumnAttribute.cs",target:"_blank",rel:"noopener noreferrer"}},[t._v("源码,解析"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("1). 比如:Blog表中指定了Title为varchar(50),我们如何通过代码指定了主键,唯一值,字形。")]),t._v(" "),t._m(22),s("p",[t._v("2). Column的命名空间在")]),t._v(" "),t._m(23),s("p",[t._v("更多属性介绍")]),t._v(" "),t._m(24),t._v(" "),t._m(25),t._v(" "),t._m(26),s("p",[t._v("更多属性介绍")]),t._v(" "),t._m(27),t._v(" "),t._m(28),t._v(" "),t._m(29),t._v(" "),t._m(30),t._m(31),t._v(" "),t._m(32),t._m(33),t._v(" "),t._m(34),t._v(" "),s("p",[t._v("这里初始化FreeSql,并使用单例模式,注入到默认的依赖中,这样在Controller中即可直接注入。")]),t._v(" "),t._m(35),t._m(36),t._v(" "),s("p",[t._v("在controllers文件夹新建一个控制器BlogController")]),t._v(" "),t._m(37),s("p",[t._v("重新运行,打开地址 http://localhost:5001/api/blog 会发现数据库中生成了表blog,这时候表post并没有生成。所以我们判断,只有在访问到实体类才检查是否存在表结构,然后执行相应的处理。")]),t._v(" "),s("p",[t._v("手动向blog表中加一些数据,然后再次请求")]),t._v(" "),t._m(38),t._v(" "),t._m(39),t._v(" "),s("p",[t._v("此功能默认为开启状态,发布正式环境后,请修改此设置")]),t._v(" "),t._m(40),s("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"freesql-asp-net-core"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#freesql-asp-net-core","aria-hidden":"true"}},[this._v("#")]),this._v(" FreeSql+ASP.NET Core")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"文章概述"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#文章概述","aria-hidden":"true"}},[this._v("#")]),this._v(" 文章概述")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"freesql-简介"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#freesql-简介","aria-hidden":"true"}},[this._v("#")]),this._v(" FreeSql 简介")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"源码"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#源码","aria-hidden":"true"}},[this._v("#")]),this._v(" 源码")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"参考"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#参考","aria-hidden":"true"}},[this._v("#")]),this._v(" 参考")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"项目准备"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#项目准备","aria-hidden":"true"}},[this._v("#")]),this._v(" 项目准备")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("ul",[s("li",[t._v("Mysql 5.6")]),t._v(" "),s("li",[t._v("Visual Studio 2019或2017、Visual Studio code")]),t._v(" "),s("li",[t._v(".NET Core 2.2+")]),t._v(" "),s("li",[t._v("PowerShell")]),t._v(" "),s("li",[t._v("懂点mvc,该教程不会教你如何使用 ASP .NET Core MVC、RESTful")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"创建项目"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#创建项目","aria-hidden":"true"}},[this._v("#")]),this._v(" 创建项目")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('PS dotnetcore-examples\\asp.net-core-freesql> dotnet new webapi -n RESTful.FreeSql\nThe template "ASP.NET Core Web API" was created successfully.\n')])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"language-PowerShell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-powershell"}},[s("code",[s("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\asp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("net"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("core"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("freesql> cd "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\\RESTful"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FreeSql\\\n"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" dotnetcore"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\asp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("net"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("core"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("freesql\\RESTful"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FreeSql> dotnet run\n\ninfo: Microsoft"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Hosting"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Lifetime"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("0"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n Now listening on: https:"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("localhost:5001\ninfo: Microsoft"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Hosting"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Lifetime"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("0"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n Now listening on: http:"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("localhost:5000\ninfo: Microsoft"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Hosting"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Lifetime"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("0"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n Application started"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v(" Press Ctrl"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v("C to shut down"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\ninfo: Microsoft"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Hosting"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Lifetime"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("0"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n Hosting environment: Development\ninfo: Microsoft"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Hosting"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Lifetime"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("0"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n Content root path: D:\\code\\github\\dotnetcore"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("examples\\asp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("net"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("core"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("freesql\\RESTful"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FreeSql\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('["value1","value2"]\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"install"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#install","aria-hidden":"true"}},[this._v("#")]),this._v(" Install")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"language-PowerShell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-powershell"}},[s("code",[s("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" \\asp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("net"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("core"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("freesql\\RESTful"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FreeSql> dotnet add package FreeSql\n"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" \\asp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("net"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("core"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("freesql\\RESTful"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FreeSql> dotnet add package FreeSql"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Provider"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("MySql\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"code-first"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#code-first","aria-hidden":"true"}},[this._v("#")]),this._v(" code first")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"db-first"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#db-first","aria-hidden":"true"}},[this._v("#")]),this._v(" db first")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"开始"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#开始","aria-hidden":"true"}},[this._v("#")]),this._v(" 开始")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"分析需求"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#分析需求","aria-hidden":"true"}},[this._v("#")]),this._v(" 分析需求")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"blog-表"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#blog-表","aria-hidden":"true"}},[this._v("#")]),this._v(" Blog 表")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("table",[s("thead",[s("tr",[s("th",[t._v("字段名")]),t._v(" "),s("th",[t._v("字段类型")]),t._v(" "),s("th",[t._v("说明")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("BlogId")]),t._v(" "),s("td",[t._v("int")]),t._v(" "),s("td",[t._v("博客id")])]),t._v(" "),s("tr",[s("td",[t._v("Title")]),t._v(" "),s("td",[t._v("varchar(50)")]),t._v(" "),s("td",[t._v("博客标题")])]),t._v(" "),s("tr",[s("td",[t._v("Content")]),t._v(" "),s("td",[t._v("varchar(500)")]),t._v(" "),s("td",[t._v("博客内容")])]),t._v(" "),s("tr",[s("td",[t._v("CreateTime")]),t._v(" "),s("td",[t._v("DateTime")]),t._v(" "),s("td",[t._v("发布时间")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"post-表"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#post-表","aria-hidden":"true"}},[this._v("#")]),this._v(" Post 表")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("table",[s("thead",[s("tr",[s("th",[t._v("字段名")]),t._v(" "),s("th",[t._v("字段类型")]),t._v(" "),s("th",[t._v("说明")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("PostId")]),t._v(" "),s("td",[t._v("int")]),t._v(" "),s("td",[t._v("评论id")])]),t._v(" "),s("tr",[s("td",[t._v("ReplyContent")]),t._v(" "),s("td",[t._v("varchar(50)")]),t._v(" "),s("td",[t._v("标题")])]),t._v(" "),s("tr",[s("td",[t._v("BlogId")]),t._v(" "),s("td",[t._v("int")]),t._v(" "),s("td",[t._v("博客id")])]),t._v(" "),s("tr",[s("td",[t._v("ReplyTime")]),t._v(" "),s("td",[t._v("DateTime")]),t._v(" "),s("td",[t._v("回复时间")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"基础介绍"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#基础介绍","aria-hidden":"true"}},[this._v("#")]),this._v(" 基础介绍")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(' public class Blog\n {\n [Column(IsIdentity = true, IsPrimary = true)]\n public int BlogId { get; set; }\n [Column(DbType = "varchar(50)")]\n public string Title { get; set; }\n }\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("using FreeSql.DataAnnotations;\n")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("table",[s("thead",[s("tr",[s("th",[t._v("字段")]),t._v(" "),s("th",[t._v("备注")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("Name")]),t._v(" "),s("td",[t._v("数据库列名")])]),t._v(" "),s("tr",[s("td",[t._v("OldName")]),t._v(" "),s("td",[t._v("指定数据库旧的列名,修改实体属性命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库字段;否则将视为【新增字段】")])]),t._v(" "),s("tr",[s("td",[t._v("DbType")]),t._v(" "),s("td",[t._v("数据库类型,如: varchar(255)")])]),t._v(" "),s("tr",[s("td",[t._v("IsPrimary")]),t._v(" "),s("td",[t._v("主键")])]),t._v(" "),s("tr",[s("td",[t._v("IsIdentity")]),t._v(" "),s("td",[t._v("自增标识")])]),t._v(" "),s("tr",[s("td",[t._v("IsNullable")]),t._v(" "),s("td",[t._v("是否可DBNull")])]),t._v(" "),s("tr",[s("td",[t._v("IsIgnore")]),t._v(" "),s("td",[t._v("忽略此列,不迁移、不插入")])]),t._v(" "),s("tr",[s("td",[t._v("IsVersion")]),t._v(" "),s("td",[t._v("设置行锁(乐观锁)版本号,每次更新累加版本号,若更新整个实体时会附带当前的版本号判断(修改失败时抛出异常)")])]),t._v(" "),s("tr",[s("td",[t._v("DbDefautValue")]),t._v(" "),s("td",[t._v("数据库默认值")])]),t._v(" "),s("tr",[s("td",[t._v("MapType")]),t._v(" "),s("td",[t._v("类型映射,比如:可将 enum 属性映射成 typeof(string)")])]),t._v(" "),s("tr",[s("td",[t._v("Uniques")]),t._v(" "),s("td",[t._v("唯一键,在多个属性指定相同的标识,代表联合键;可使用逗号分割多个 UniqueKey 名。")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"_2-table-的使用:用于在类的上面指定这个表的属性"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2-table-的使用:用于在类的上面指定这个表的属性","aria-hidden":"true"}},[this._v("#")]),this._v(" 2. Table 的使用:用于在类的上面指定这个表的属性")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"language-csharp extra-class"},[s("pre",{pre:!0,attrs:{class:"language-csharp"}},[s("code",[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Table")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Name "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"t_blog"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Blog")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//...")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("table",[s("thead",[s("tr",[s("th",[t._v("字段")]),t._v(" "),s("th",[t._v("备注")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("Name")]),t._v(" "),s("td",[t._v("数据库表名")])]),t._v(" "),s("tr",[s("td",[t._v("OldName")]),t._v(" "),s("td",[t._v("指定数据库旧的表名,修改实体命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库表;否则将视为【创建新表】")])]),t._v(" "),s("tr",[s("td",[t._v("SelectFilter")]),t._v(" "),s("td",[t._v("查询过滤SQL,实现类似 a.IsDeleted = 1 功能")])]),t._v(" "),s("tr",[s("td",[t._v("DisableSyncStructure")]),t._v(" "),s("td",[t._v("禁用 CodeFirst 同步结构迁移")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"_3-其他的还是看-https-github-com-2881099-freesql-blob-master-docs-codefirst-md"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_3-其他的还是看-https-github-com-2881099-freesql-blob-master-docs-codefirst-md","aria-hidden":"true"}},[this._v("#")]),this._v(" 3. 其他的还是看 https://github.com/2881099/FreeSql/blob/master/Docs/codefirst.md")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"blog-cs"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#blog-cs","aria-hidden":"true"}},[this._v("#")]),this._v(" Blog.cs")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('using FreeSql.DataAnnotations;\nusing System;\n\nnamespace RESTful.FreeSql.Domain\n{\n public class Blog\n {\n [Column(IsIdentity = true, IsPrimary = true)]\n public int BlogId { get; set; }\n [Column(DbType = "varchar(50)")]\n public string Title { get; set; }\n [Column(DbType = "varchar(500)")]\n public string Content { get; set; }\n public DateTime CreateTime { get; set; }\n\n\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"post-cs"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#post-cs","aria-hidden":"true"}},[this._v("#")]),this._v(" Post.cs")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-C# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('\nusing FreeSql.DataAnnotations;\nusing System;\n\nnamespace RESTful.FreeSql.Domain\n{\n public class Post\n {\n [Column(IsIdentity = true, IsPrimary = true)]\n public int PostId { get; set; }\n [Column(DbType = "varchar(50)")]\n public string ReplyContent { get; set; }\n public int BlogId { get; set; }\n public DateTime ReplyTime { get; set; }\n public Blog Blog { get; set; }\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"startup-cs"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#startup-cs","aria-hidden":"true"}},[this._v("#")]),this._v(" Startup.cs")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("非全部代码,这里注意点:要先在mysql中创建数据库"),e("strong",[this._v("FreeSql_Blog")]),this._v(",否则一直提示"),e("strong",[this._v("主库xxxxx")]),this._v(",官网未找到相关描述。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('namespace RESTful.FreeSql\n{\n public class Startup\n {\n public Startup(IConfiguration configuration)\n {\n Fsql = new FreeSqlBuilder()\n .UseConnectionString(DataType.MySql, @"Data Source=127.0.0.1;Port=3306;User ID=root;Password=123456;Initial Catalog=FreeSql_Blog;Charset=utf8;SslMode=none;Max pool size=10")\n .UseAutoSyncStructure(true)\n .Build();\n }\n\n public IFreeSql Fsql { get; }\n\n public void ConfigureServices(IServiceCollection services)\n {\n services.AddSingleton(Fsql);\n\n }\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"blogcontroller"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#blogcontroller","aria-hidden":"true"}},[this._v("#")]),this._v(" BlogController")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Threading.Tasks;\nusing FreeSql;\nusing Microsoft.AspNetCore.Mvc;\nusing RESTful.FreeSql.Domain;\n\nnamespace RESTful.FreeSql.Controllers\n{\n [Route("api/[controller]")]\n [ApiController]\n public class BlogController : ControllerBase\n {\n // GET api/Blog\n\n IFreeSql _fsql;\n public BlogController(IFreeSql fsql)\n {\n _fsql = fsql;\n }\n\n [HttpGet]\n public ActionResult> Get()\n {\n List blogs = _fsql.Select().OrderByDescending(r => r.CreateTime).ToList();\n\n return blogs;\n }\n\n // GET api/blog/5\n [HttpGet("{id}")]\n public ActionResult Get(int id)\n {\n return _fsql.Select(id).ToOne();\n }\n\n\n // DELETE api/blog/5\n [HttpDelete("{id}")]\n public void Delete(int id)\n {\n _fsql.Delete(new { BlogId = id }).ExecuteAffrows();\n }\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("http://localhost:5001/api/blog%EF%BC%8C%20%E5%8F%AF%E7%9C%8B%E5%88%B0%E7%9B%B8%E5%BA%94%E7%9A%84%E6%95%B0%E6%8D%AE%E3%80%82")]),this._v(" "),e("li",[this._v("http://localhost:5001/api/blog/1%20%20%E5%8F%AF%E5%BE%97%E5%88%B0%E5%8D%95%E4%B8%AA%E6%95%B0%E6%8D%AE%E3%80%82")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"自动同步实体结构【开发环境必备】"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#自动同步实体结构【开发环境必备】","aria-hidden":"true"}},[this._v("#")]),this._v(" 自动同步实体结构【开发环境必备】")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('Fsql = new FreeSqlBuilder()\n .UseConnectionString(DataType.MySql, @"连接字符串")\n .UseAutoSyncStructure(true)\n .Build();\n \n//UseAutoSyncStructure(true/false)【开发环境必备】自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改\n\n// 也可使用此方法指定是否自动同步结构。 \nFsql.CodeFirst.IsAutoSyncStructure = true;\n')])])])}],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/13.0374a48d.js b/assets/js/13.0374a48d.js new file mode 100644 index 0000000..0120122 --- /dev/null +++ b/assets/js/13.0374a48d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{187:function(t,e,n){"use strict";n.r(e);var s=n(0),a=Object(s.a)({},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),n("p",[t._v("本文使用ASP .NET Core的WEB API,构建一个RESTful风格的接口,使用Freesql访问MySQL数据库,实现二个表的简单博客,并集成AutoMapper。")]),t._v(" "),n("p",[t._v("接上一篇")]),t._v(" "),n("ul",[n("li",[n("p",[n("a",{attrs:{href:"http://blog.igeekfan.cn/2019/06/30/re-start/FreeSql-asp.net-core-how-to-use/",target:"_blank",rel:"noopener noreferrer"}},[t._v("FreeSql在ASP.NTE Core WebApi中如何使用的教程"),n("OutboundLink")],1)])]),t._v(" "),n("li",[n("p",[t._v("项目源码 "),n("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-freesql",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-freesql"),n("OutboundLink")],1)])])]),t._v(" "),t._m(2),t._v(" "),n("p",[t._v("当我们使用RESTful提供接口时,比如创建一个博客,修改一下博客内容时,他们的参数是有区别的。良好的设计应该是")]),t._v(" "),n("p",[t._v("创建一个博客")]),t._v(" "),t._m(3),n("p",[t._v("修改一个博客内容")]),t._v(" "),t._m(4),n("p",[t._v("但一个blog 实体如下")]),t._v(" "),t._m(5),n("p",[t._v("如果我们以Blog作为controllers中的参数时")]),t._v(" "),t._m(6),n("p",[t._v("这时修改swagger显示的默认参数是")]),t._v(" "),t._m(7),n("p",[t._v("如果我们不传递createTime,会出现异常,应该createTime是DateTime,不能为null,只有DateTime?才能为null,有?为可空类型。")]),t._v(" "),n("p",[t._v("所以我们应该为POST方式传递过来时,新建一个实体类,我们称之为DTO(Data Transfer Object),即数据传输对象,因为createTime即使传递,后端为他赋了值,前台传了也无效。有了DTO,这样可让前端清楚他们应该传递的参数,而不会出现没有作用的参数。")]),t._v(" "),n("p",[t._v("在根目录创建Models/Blogs文件夹,在Blogs文件夹中创建")]),t._v(" "),t._m(8),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),n("p",[t._v("有了Dto后,我们会发现了新的问题,往数据库插入时,往往使用了一些ORM,他们只支持原本的实体类,如Blog,Post。但不支持CreateBlogDto、UpdateBlogDto,我们可以手动,将一个类的值,赋值给另一个类。\n如")]),t._v(" "),t._m(12),n("p",[t._v("现在只是非常简单的二个属性,我们还能忍受,但如果是十个属性、而且有着大量的类与类之间的转换呢。这时修改AutoMapper就闪亮登场了。")]),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),n("p",[t._v("我们是在ASP .NET Core下使用AutoMapper "),n("a",{attrs:{href:"https://automapper.readthedocs.io/en/latest/Dependency-injection.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("官网介绍,如何依赖注入中使用"),n("OutboundLink")],1)]),t._v(" "),t._m(15),t._v(" "),n("p",[t._v("先cd到dotnetcore-examples\\asp.net-core-freesql\\RESTful.FreeSql目录")]),t._v(" "),t._m(16),n("p",[t._v("在StartUp.cs中的ConfigureServices配置如下")]),t._v(" "),t._m(17),t._m(18),t._v(" "),n("p",[t._v("AutoMapper/BlogProfile.cs")]),t._v(" "),t._m(19),n("p",[t._v("AutoMapper/BlogProfile.cs")]),t._v(" "),t._m(20),n("p",[t._v("Models/Posts/SearchPostDto.cs根据博客id,得到分页的评论时,集成分页类")]),t._v(" "),t._m(21),n("p",[t._v("Controlers/BlogController.cs文件中,注入IMapper,")]),t._v(" "),t._m(22),n("p",[t._v("Controlers/BlogController.cs文件中,注入IMapper,")]),t._v(" "),t._m(23),t._m(24),t._v(" "),n("ul",[n("li",[t._v("建议大家先看官网 "),n("a",{attrs:{href:"http://automapper.org/",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://automapper.org/"),n("OutboundLink")],1)]),t._v(" "),n("li",[t._v("开源地址 "),n("a",{attrs:{href:"https://github.com/AutoMapper/AutoMapper",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/AutoMapper/AutoMapper"),n("OutboundLink")],1)]),t._v(" "),n("li",[t._v("Getting-started 文档 "),n("a",{attrs:{href:"https://automapper.readthedocs.io/en/latest/Getting-started.html#what-is-automapper",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://automapper.readthedocs.io/en/latest/Getting-started.html#what-is-automapper"),n("OutboundLink")],1)])]),t._v(" "),n("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"restful-freesql-automapper"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#restful-freesql-automapper","aria-hidden":"true"}},[this._v("#")]),this._v(" RESTful+FreeSql+AutoMapper")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"文章概述"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#文章概述","aria-hidden":"true"}},[this._v("#")]),this._v(" 文章概述")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"dto作用"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#dto作用","aria-hidden":"true"}},[this._v("#")]),this._v(" Dto作用")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"language-json extra-class"},[n("pre",{pre:!0,attrs:{class:"language-json"}},[n("code",[t._v("POST /api/blog\ndata"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"title"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"string"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"content"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"string"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"language-json extra-class"},[n("pre",{pre:!0,attrs:{class:"language-json"}},[n("code",[t._v("PUT /api/blog\ndata"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"blogId"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"int"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"title"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"string"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"content"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"string"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(" public class Blog\n {\n [Column(IsIdentity = true, IsPrimary = true)]\n public int BlogId { get; set; }\n public string Title { get; set; }\n public string Content { get; set; }\n public DateTime CreateTime { get; set; }\n public virtual List Posts { get; set; }\n }\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(" // POST api/blog\n [HttpPost]\n public void Post([FromBody] Blog blog)\n {\n blog.CreateTime=DateTime.Now;\n _fsql.Insert(blog).ExecuteAffrows();\n }\n")])])])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"language-json extra-class"},[n("pre",{pre:!0,attrs:{class:"language-json"}},[n("code",[n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"blogId"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"title"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"string"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"content"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"string"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"createTime"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2019-06-30T07:33:05.524Z"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"posts"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"postId"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"replyContent"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"string"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"blogId"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token property"}},[t._v('"replyTime"')]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2019-06-30T07:33:05.524Z"')]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"createblogdto-cs"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#createblogdto-cs","aria-hidden":"true"}},[this._v("#")]),this._v(" CreateBlogDto.cs")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("namespace RESTful.FreeSql.Models.Blogs\n{\n public class CreateBlogDto\n {\n public string Title { get; set; }\n public string Content { get; set; }\n\n }\n}\n\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"updateblogdto-cs"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#updateblogdto-cs","aria-hidden":"true"}},[this._v("#")]),this._v(" UpdateBlogDto.cs")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("namespace RESTful.FreeSql.Models.Blogs\n{\n public class UpdateBlogDto\n {\n public int BlogId { get; set; }\n public string Title { get; set; }\n public string Content { get; set; }\n }\n}\n\n\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(' CreateBlogDto createBlogDto = new CreateBlogDto()\n {\n Title = "我是title",\n Content = "我是content"\n };\n\n Blog newBlog=new Blog()\n {\n Title = createBlogDto.Title,\n Content = createBlogDto.Content\n };\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"automapper"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#automapper","aria-hidden":"true"}},[this._v("#")]),this._v(" AutoMapper")])},function(){var t=this.$createElement,e=this._self._c||t;return e("blockquote",[e("p",[this._v("作用:A convention-based object-object mapper.")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"setup"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#setup","aria-hidden":"true"}},[this._v("#")]),this._v(" Setup")])},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"language-PowerShell extra-class"},[n("pre",{pre:!0,attrs:{class:"language-powershell"}},[n("code",[n("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" > dotnet add package AutoMapper\n"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" > dotnet add package AutoMapper"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Extensions"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Microsoft"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("DependencyInjection "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("version 6"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("1\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(" public void ConfigureServices(IServiceCollection services)\n {\n // .... Ignore code before this\n \n //AddAutoMapper会去找继承Profile的类,\n services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());\n\n // .... Ignore code after this\n }\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"adding-profiles"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#adding-profiles","aria-hidden":"true"}},[this._v("#")]),this._v(" Adding Profiles")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("using AutoMapper;\nusing RESTful.FreeSql.Domain;\nusing RESTful.FreeSql.Models.Blogs;\n\nnamespace RESTful.FreeSql.AutoMapper\n{\n public class BlogProfile : Profile\n {\n public BlogProfile() \n {\n CreateMap();\n CreateMap();\n }\n }\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("using AutoMapper;\nusing RESTful.FreeSql.Domain;\nusing RESTful.FreeSql.Models.Blogs;\n\nnamespace RESTful.FreeSql.AutoMapper\n{\n public class PostProfile : Profile\n {\n public PostProfile()\n {\n CreateMap();\n }\n }\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("using RESTful.FreeSql.Web;\n\nnamespace RESTful.FreeSql.Models.Posts\n{\n public class SearchPostDto:PageDto\n {\n public int BlogId { get; set; }\n }\n}\n\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('using AutoMapper;\nusing Microsoft.AspNetCore.Mvc;\nusing System;\nusing System.Collections.Generic;\nusing RESTful.FreeSql.Domain;\nusing RESTful.FreeSql.Models.Blogs;\nusing RESTful.FreeSql.Web;\n\nnamespace RESTful.FreeSql.Controllers\n{\n [Route("api/[controller]")]\n [ApiController]\n public class BlogController : ControllerBase\n {\n // GET api/Blog\n\n private readonly IFreeSql _fsql;\n private readonly IMapper _mapper;\n public BlogController(IFreeSql fsql, IMapper mapper)\n {\n _fsql = fsql;\n _mapper = mapper;\n }\n\n /// \n /// 博客列表页 \n /// \n /// 分页参数\n /// \n [HttpGet]\n public ActionResult> Get([FromQuery]PageDto pageDto)\n {\n List blogs = _fsql.Select().OrderByDescending(r => r.CreateTime).Page(pageDto.PageNumber, pageDto.PageSize).ToList();\n long count = _fsql.Select().Count();\n return new PagedResultDto(count, blogs);\n }\n\n // GET api/blog/5\n [HttpGet("{id}")]\n public ActionResult Get(int id)\n {\n // eg.1 return _fsql.Select().Where(a => a.Id == id).ToOne();\n // eg.2\n return _fsql.Select(id).ToOne();\n }\n\n // POST api/blog\n [HttpPost]\n public void Post([FromBody] CreateBlogDto createBlogDto)\n {\n Blog blog = _mapper.Map(createBlogDto);\n blog.CreateTime = DateTime.Now;\n _fsql.Insert(blog).ExecuteAffrows();\n }\n\n // PUT api/blog\n [HttpPut]\n public void Put([FromBody] UpdateBlogDto updateBlogDto)\n {\n\n //eg.1 更新指定列\n //_fsql.Update(updateBlogDto.BlogId).Set(a => new Blog()\n //{\n // Title = updateBlogDto.Title,\n // Content = updateBlogDto.Content\n //}).ExecuteAffrows();\n\n //eg.2将这个实体更新到数据库中。当更新时,会把其他列的值,如CreateTime也更新掉。\n //使用IgnoreColumns可忽略某一些列。\n\n Blog blog = _mapper.Map(updateBlogDto);\n _fsql.Update().SetSource(blog).IgnoreColumns(r => r.CreateTime).ExecuteAffrows();\n }\n\n // DELETE api/blog/5\n [HttpDelete("{id}")]\n public void Delete(int id)\n {\n _fsql.Delete(new { BlogId = id }).ExecuteAffrows();\n }\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-c# extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('using FreeSql;\nusing Microsoft.AspNetCore.Mvc;\nusing System;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\nusing AutoMapper;\nusing RESTful.FreeSql.Domain;\nusing RESTful.FreeSql.Models.Posts;\nusing RESTful.FreeSql.Web;\n\nnamespace RESTful.FreeSql.Controllers\n{\n [Route("api/[controller]")]\n [ApiController]\n public class PostController : ControllerBase\n {\n // GET: api/Post\n private readonly IFreeSql _fsql;\n private readonly IMapper _mapper;\n public PostController(IFreeSql fsql, IMapper mapper)\n {\n _fsql = fsql;\n _mapper = mapper;\n }\n\n /// \n /// 根据博客id、分页条件查询评论信息\n /// \n /// \n /// \n [HttpGet]\n public PagedResultDto Get(SearchPostDto searchPostDto)\n {\n ISelect selectPost = _fsql\n .Select()\n .Where(r => r.BlogId == searchPostDto.BlogId);\n\n List posts = selectPost.OrderByDescending(r => r.ReplyTime)\n .Page(searchPostDto.PageNumber, searchPostDto.PageSize)\n .ToList();\n\n long total = selectPost.Count();\n\n return new PagedResultDto(total, posts);\n }\n\n // GET: api/Post/5\n [HttpGet("{id}", Name = "Get")]\n public Post Get(int id)\n {\n return _fsql.Select().Where(a => a.PostId == id).ToOne();\n }\n\n // POST: api/Post\n [HttpPost]\n public void Post([FromBody] CreatePostDto createPostDto)\n {\n Post post = _mapper.Map(createPostDto);\n post.ReplyTime = DateTime.Now;\n _fsql.Insert(post).ExecuteAffrows();\n }\n\n\n // DELETE: api/Post/\n [HttpDelete("{id}")]\n public async Task DeleteAsync(int id)\n {\n await _fsql.Delete(new Post { PostId = id }).ExecuteAffrowsAsync();\n }\n }\n}\n\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"参考"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#参考","aria-hidden":"true"}},[this._v("#")]),this._v(" 参考")])}],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/14.679b369a.js b/assets/js/14.679b369a.js new file mode 100644 index 0000000..615e2d2 --- /dev/null +++ b/assets/js/14.679b369a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{168:function(t,e,r){"use strict";r.r(e);var n=r(0),i=Object(n.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("p",[t._v("IdentityServer4 实现单点登录授权token验证")]),t._v(" "),t._m(2),t._v(" "),r("p",[t._v("代码托管在GitHub上 "),r("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-identityserver4",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-identityserver4"),r("OutboundLink")],1)]),t._v(" "),t._m(3),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://www.cnblogs.com/edisonchou/p/identityserver4_foundation_and_quickstart_01.html",target:"_blank",rel:"noopener noreferrer"}},[t._v(".NET Core微服务之基于IdentityServer建立授权与验证服务"),r("OutboundLink")],1),t._v(" "),r("RightMenu")],1)])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"identityserver4"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#identityserver4","aria-hidden":"true"}},[this._v("#")]),this._v(" IdentityServer4")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"写的一些identityserver4的示例"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#写的一些identityserver4的示例","aria-hidden":"true"}},[this._v("#")]),this._v(" 写的一些IdentityServer4的示例")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"源码"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#源码","aria-hidden":"true"}},[this._v("#")]),this._v(" 源码")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"参考"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#参考","aria-hidden":"true"}},[this._v("#")]),this._v(" 参考")])}],!1,null,null,null);e.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/15.4e2834ee.js b/assets/js/15.4e2834ee.js new file mode 100644 index 0000000..7961448 --- /dev/null +++ b/assets/js/15.4e2834ee.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{176:function(t,e,r){"use strict";r.r(e);var a=r(0),s=Object(a.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("p",[t._v("利用 webSocket 协议实现简易、高性能、集群即时通讯组件,支持点对点通讯、群聊通讯、上线下线事件消息等众多实用性功能。")]),t._v(" "),t._m(2),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/2881099/im",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/2881099/im"),r("OutboundLink")],1)]),t._v(" "),t._m(3),t._v(" "),r("p",[r("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/tree/master/dotnet-core-im",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/dotnetcore-examples/tree/master/dotnet-core-im"),r("OutboundLink")],1)]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),r("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"imcore-即时通讯"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#imcore-即时通讯","aria-hidden":"true"}},[this._v("#")]),this._v(" ImCore 即时通讯")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"简介"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#简介","aria-hidden":"true"}},[this._v("#")]),this._v(" 简介")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"开源地址"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#开源地址","aria-hidden":"true"}},[this._v("#")]),this._v(" 开源地址")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"示例"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#示例","aria-hidden":"true"}},[this._v("#")]),this._v(" 示例")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"演示效果"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#演示效果","aria-hidden":"true"}},[this._v("#")]),this._v(" 演示效果")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"https://ae01.alicdn.com/kf/Hf8b31f05dbb94f8da565fce29f78aa78Y.png",alt:""}})])}],!1,null,null,null);e.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/16.b94c35d3.js b/assets/js/16.b94c35d3.js new file mode 100644 index 0000000..d8577a4 --- /dev/null +++ b/assets/js/16.b94c35d3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[16],{177:function(t,e,n){"use strict";n.r(e);var r=n(0),a=Object(r.a)({},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("p",[t._v(".NET Core下集成 七牛云下的对象存储")]),t._v(" "),t._m(1),t._v(" "),n("p",[n("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-qiniu",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-qiniu"),n("OutboundLink")],1)]),t._v(" "),t._m(2),t._v(" "),n("table",[t._m(3),t._v(" "),n("tbody",[n("tr",[n("td",[n("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/tree/master/dotnet-core-efcore",target:"_blank",rel:"noopener noreferrer"}},[t._v("Qiniu云对象存储"),n("OutboundLink")],1)]),t._v(" "),n("td",[n("a",{attrs:{href:"https://github.com/qiniu/csharp-sdk",target:"_blank",rel:"noopener noreferrer"}},[t._v(".net"),n("OutboundLink")],1),t._v("/"),n("a",{attrs:{href:"https://github.com/Hello-Mango/MQiniu.Core",target:"_blank",rel:"noopener noreferrer"}},[t._v(".net core"),n("OutboundLink")],1)]),t._v(" "),n("td",[n("a",{attrs:{href:"https://developer.qiniu.com/kodo/sdk/1237/csharp",target:"_blank",rel:"noopener noreferrer"}},[t._v("c# sdk"),n("OutboundLink")],1)]),t._v(" "),n("td",[t._v("由于官网未支持. net core,所以 大家看"),n("a",{attrs:{href:"https://github.com/Hello-Mango/MQiniu.Core",target:"_blank",rel:"noopener noreferrer"}},[t._v("社区版解决方案"),n("OutboundLink")],1)])])])]),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),n("p",[t._v("在七牛云中自行注册后,在个人中心,密钥管理,生成自己的密钥(https://portal.qiniu.com/user/key)\nv* AK,SK分别代表:AccessKey/SecretKey")]),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),n("p",[t._v("配置swagger的过程就不说了,创建QiniuController")]),t._v(" "),t._m(13),n("p",[t._v("Upload方法中,Zone.ZONE_CN_South,代表华南,所以创建对象存储时要注意,请选择与此相同的位置(华南),或根据实际情况修改Zone所在地区的枚举值即可。")]),t._v(" "),t._m(14),t._m(15),t._v(" "),n("p",[t._v("双击 publish.bat,生成的文件夹为如下:")]),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),n("p",[t._v("使用xshell远程登录后,进入root权限。")]),t._v(" "),t._m(18),n("p",[t._v("前置条件,在ubuntu上安装好了docker。并且正常运行。")]),t._v(" "),n("p",[t._v("-d 代表后台运行,此时将对外显露5000端口运行,5000是运行后,docker对外的端口,80是这个服务对外的端口,其中Dockerfile 存在语句EXPOSE 80")]),t._v(" "),t._m(19),n("p",[t._v("此时打开 浏览器, ip+端口5000即可访问服务,请加/swagger。")]),t._v(" "),n("p",[t._v("本项目已部署至服务器 "),n("a",{attrs:{href:"http://122.152.192.161:5000/swagger/index.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://122.152.192.161:5000/swagger/index.html"),n("OutboundLink")],1)]),t._v(" "),t._m(20),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),n("ul",[n("li",[t._v(".NET Core版本七牛云SDK使用"),n("a",{attrs:{href:"https://www.cnblogs.com/OMango/p/8447480.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://www.cnblogs.com/OMango/p/8447480.html"),n("OutboundLink")],1)]),t._v(" "),n("li",[t._v(".NET Core部署至Linux 下的Docker "),n("a",{attrs:{href:"http://blog.igeekfan.cn/2019/06/09/dotnetcore/ASP.NET-Core-Deploy-To-Docker-Ubuntu/",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://blog.igeekfan.cn/2019/06/09/dotnetcore/ASP.NET-Core-Deploy-To-Docker-Ubuntu/"),n("OutboundLink")],1)])]),t._v(" "),n("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"七牛云对象存储"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#七牛云对象存储","aria-hidden":"true"}},[this._v("#")]),this._v(" 七牛云对象存储")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"开源地址"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#开源地址","aria-hidden":"true"}},[this._v("#")]),this._v(" 开源地址")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"集成类库"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#集成类库","aria-hidden":"true"}},[this._v("#")]),this._v(" 集成类库")])},function(){var t=this.$createElement,e=this._self._c||t;return e("thead",[e("tr",[e("th",[this._v("基础类库集成方案")]),this._v(" "),e("th",[this._v("开源地址")]),this._v(" "),e("th",[this._v("文档")]),this._v(" "),e("th",[this._v("说明")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"前提"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#前提","aria-hidden":"true"}},[this._v("#")]),this._v(" 前提")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("本地windows 10,安装 .net core 2.2+")]),this._v(" "),e("li",[this._v("Linux 服务器 Ubuntu Server 16+")]),this._v(" "),e("li",[this._v("服务器安装了Docker")]),this._v(" "),e("li",[this._v("本地xftp、xshell(这二个分别是windows传文件至linux,执行命令行。)")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"准备"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#准备","aria-hidden":"true"}},[this._v("#")]),this._v(" 准备")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("Bucket中的vant-ui,是创建对象存储时起的名字")]),this._v(" "),e("li",[this._v("PrefixPath中的值,随意字符串,前缀地址。")]),this._v(" "),e("li",[this._v("Host为:融合 CDN 测试域名,可自行绑定自己的域名,否则只有三十天免费使用时长。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"appsettings-json配置项"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#appsettings-json配置项","aria-hidden":"true"}},[this._v("#")]),this._v(" appsettings.json配置项")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(' "Qiniu": {\n "AK": "eUH1O-ft66S4XM2GIK7FGmj7czuYkcAyNGDAc-wq",\n "SK": "4dOi1daSr2-YgofhAfWb8JaLrbgozCmgD6AUmmM9",\n "Bucket": "vant-ui",\n "PrefixPath": "ui",\n "Host": "http://pu5vnz60k.bkt.clouddn.com/"\n }\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"安装包"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#安装包","aria-hidden":"true"}},[this._v("#")]),this._v(" 安装包")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("Install-Package MQiniu.Core \n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"代码解读"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#代码解读","aria-hidden":"true"}},[this._v("#")]),this._v(" 代码解读")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(' /// \n /// 七牛云上传服务\n /// \n [Route("api/[controller]")]\n [ApiController]\n public class QiniuController : ControllerBase\n {\n private readonly IConfiguration _configuration;\n\n public QiniuController(IConfiguration configuration)\n {\n _configuration = configuration;\n }\n\n /// \n /// 根据后台配置项,得到请求七牛云的token值,前台也可根据此token值上传至七牛云服务\n /// \n /// \n [HttpGet("access_token")]\n public string GetAccessToken()\n {\n Mac mac = new Mac(_configuration["Qiniu:AK"], _configuration["Qiniu:SK"]);\n PutPolicy putPolicy = new PutPolicy { Scope = _configuration["Qiniu:Bucket"] };\n return Auth.CreateUploadToken(mac, putPolicy.ToJsonString());\n }\n\n /// \n /// 上传文件至七牛云,code为200,代表上传成功,其他代表不成功\n /// \n /// 单个文件\n /// new { code = 200, data ="七牛云文件地址,包括http://....mm.png", msg = "上传成功" };\n [HttpPost("upload")]\n public dynamic Upload(IFormFile file)\n {\n if (file.Length == 0)\n {\n return new { code = 1, msg = "文件为空" };\n }\n\n FormUploader upload = new FormUploader(new Config()\n {\n Zone = Zone.ZONE_CN_South,//华南 \n UseHttps = true\n });\n\n var fileName = ContentDispositionHeaderValue\n .Parse(file.ContentDisposition)\n .FileName.Trim();\n\n string qiniuName = _configuration["Qiniu:PrefixPath"] + "/" + DateTime.Now.ToString("yyyyMMddHHmmssffffff") + fileName;\n Stream stream = file.OpenReadStream();\n HttpResult result = upload.UploadStream(stream, qiniuName, GetAccessToken(), null);\n\n if (result.Code == 200)\n {\n return new { code = 200, data = _configuration["Qiniu:Host"] + qiniuName, msg = "上传成功" };\n }\n\n return new { code = 1, msg = "上传失败" };\n }\n }\n\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("FormUploader upload = new FormUploader(new Config()\n{\n Zone = Zone.ZONE_CN_South,//华南 \n UseHttps = true\n});\n\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"发布至linux下的docker"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#发布至linux下的docker","aria-hidden":"true"}},[this._v("#")]),this._v(" 发布至Linux下的Docker")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"https://upload-images.jianshu.io/upload_images/2001974-bcd72707e4fcc7f8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240",alt:"image"}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("修改项目名为qiniu-web,复制 至linux服务器中,(xftp工具)\n"),e("img",{attrs:{src:"https://upload-images.jianshu.io/upload_images/2001974-48771e9fce281262.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240",alt:"image"}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("root@VM-37-104-ubuntu:/home/ubuntu/"),e("span",{pre:!0,attrs:{class:"token comment"}},[this._v("# sudo su")]),this._v("\nroot@VM-37-104-ubuntu:/home/ubuntu/"),e("span",{pre:!0,attrs:{class:"token comment"}},[this._v("# cd qiniu-web")]),this._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[this._v("docker build -t igeekfan/qiniu "),e("span",{pre:!0,attrs:{class:"token keyword"}},[this._v(".")]),this._v(" "),e("span",{pre:!0,attrs:{class:"token comment"}},[this._v("#生成images")]),this._v("\ndocker run -d -p 5000:80 igeekfan/qiniu "),e("span",{pre:!0,attrs:{class:"token comment"}},[this._v("# 生成 container 并运行在5000端口")]),this._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"运行结果"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#运行结果","aria-hidden":"true"}},[this._v("#")]),this._v(" 运行结果")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"https://upload-images.jianshu.io/upload_images/2001974-2b72cc6338db1434.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240",alt:"image.png"}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"参考"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#参考","aria-hidden":"true"}},[this._v("#")]),this._v(" 参考")])}],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/17.82fc466d.js b/assets/js/17.82fc466d.js new file mode 100644 index 0000000..236c9c5 --- /dev/null +++ b/assets/js/17.82fc466d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{178:function(t,e,s){"use strict";s.r(e);var r=s(0),n=Object(r.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("p",[t._v("浏览左侧导航菜单以深入了解文档.")]),t._v(" "),s("p",[t._v(".NET Core 学习示例文档主要是结合 ASP .NET Core,集成第三方类库的示例,运用基础组件,写好Demo。")]),t._v(" "),t._m(1),t._v(" "),s("p",[t._v("代码托管在GitHub上 "),s("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/dotnetcore-examples"),s("OutboundLink")],1)]),t._v(" "),t._m(2),t._v(" "),s("ul",[s("li",[s("p",[t._v("Microsoft Docs "),s("a",{attrs:{href:"https://docs.microsoft.com/zh-cn",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.microsoft.com/zh-cn"),s("OutboundLink")],1)]),t._v(" "),s("p",[t._v("其中包含如下")]),t._v(" "),t._m(3)])]),t._v(" "),t._m(4),t._v(" "),s("ul",[s("li",[t._v("Windows 10 (18922.rs_prerelease.190614-1427)")]),t._v(" "),s("li",[t._v(".NET Core 3.0.100-preview6-012264")]),t._v(" "),s("li",[t._v("Visual Studio Code 1.35.1、Microsoft Visual Studio 2019 16.1.3")]),t._v(" "),s("li",[t._v("PowerSheel")]),t._v(" "),s("li",[t._v("MySQL 5.7.25")]),t._v(" "),s("li",[t._v("Navicat Premium 12 "),s("a",{attrs:{href:"http://blog.igeekfan.cn/2018/06/02/%E5%A4%A7%E5%90%8E%E7%AB%AF/Navicat%20Premium%2012%20%20%E7%A0%B4%E8%A7%A3%E7%89%88%E5%85%8D%E8%B4%B9%E4%B8%8B%E8%BD%BD/",target:"_blank",rel:"noopener noreferrer"}},[t._v("欢迎下载"),s("OutboundLink")],1)])]),t._v(" "),t._m(5),t._v(" "),s("p",[t._v("本地开发选择SDK安装即可,还是安装 2.2的吧,3.0(19.6.29)目前还没有发布稳定版本。")]),t._v(" "),t._m(6),t._v(" "),s("p",[t._v("安装后,在 PowerShell 中任一目录查看安装后的版本")]),t._v(" "),t._m(7),t._m(8),t._v(" "),s("p",[t._v("全称:command-line interface,命令行界面,主要是cmd、bash(sh等等)、powershell等。")]),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),s("p",[t._v(".NET Core 项目默认使用最新版本的 .NET Core,在根目录使用PowerShell中执行如下命令,")]),t._v(" "),s("p",[t._v("语法 :dotnet new global.json --sdk-version ")]),t._v(" "),t._m(11),s("p",[t._v("参考")]),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet-new?tabs=netcore22",target:"_blank",rel:"noopener noreferrer"}},[t._v("dotnet new 命令行"),s("OutboundLink")],1)]),t._v(" "),s("li",[s("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/dotnet/core/versions/selection?view=dotnet-plat-ext-2.1",target:"_blank",rel:"noopener noreferrer"}},[t._v("选择要使用的 .NET Core 版本"),s("OutboundLink")],1)])]),t._v(" "),t._m(12),t._v(" "),s("p",[t._v(".NET Core是什么? 官网说的太高大上了 https://docs.microsoft.com/zh-cn/dotnet/core/about")]),t._v(" "),s("p",[t._v("总结,.NET Core包含如下")]),t._v(" "),t._m(13),t._v(" "),s("p",[t._v("三个发布包:")]),t._v(" "),t._m(14),t._v(" "),s("p",[t._v("所以本地调试时,可直接安装最全的SDK即可。")]),t._v(" "),t._m(15),t._v(" "),s("p",[t._v("目前 关于此项目的文档放到docs文件夹中,zh-Hans为中文,这样可支持多语言,欢迎翻译PR,之后会发布至")]),t._v(" "),s("ul",[s("li",[t._v("文档官网 "),s("a",{attrs:{href:"https://luoyunchong.github.io/vuepress-docs/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://luoyunchong.github.io/vuepress-docs/"),s("OutboundLink")],1)])]),t._v(" "),s("p",[t._v("将使用abp vnext 下的modules的"),s("a",{attrs:{href:"https://github.com/abpframework/abp/blob/dev/modules/docs/README.md",target:"_blank",rel:"noopener noreferrer"}},[t._v("docs模块"),s("OutboundLink")],1),t._v("。不过abp vnext 现在也不稳定,0.18.1,还是有各种问题,我还是写基础模块的使用文档吧,后期完善后,发布文档网站。")]),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),s("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"net-core-学习示例文档"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#net-core-学习示例文档","aria-hidden":"true"}},[this._v("#")]),this._v(" .NET Core 学习示例文档")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"源码"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#源码","aria-hidden":"true"}},[this._v("#")]),this._v(" 源码")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"推荐阅读"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#推荐阅读","aria-hidden":"true"}},[this._v("#")]),this._v(" 推荐阅读")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v(".NET Core 指南 https://docs.microsoft.com/zh-cn/dotnet/core/")]),this._v(" "),e("li",[this._v("ASP .NET 文档 https://docs.microsoft.com/zh-cn/aspnet/")]),this._v(" "),e("li",[this._v(".NET Core CLI 文档 https://docs.microsoft.com/zh-cn/dotnet/core/tools/?tabs=netcore2x")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"本地环境说明"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#本地环境说明","aria-hidden":"true"}},[this._v("#")]),this._v(" 本地环境说明")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"install"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#install","aria-hidden":"true"}},[this._v("#")]),this._v(" Install")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("安装这个 "),e("strong",[this._v("https://dotnet.microsoft.com/download/dotnet-core/2.2")])]),this._v(" "),e("li",[this._v("https://dotnet.microsoft.com/download/dotnet-core/3.0")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"language-PowerShell extra-class"},[s("pre",{pre:!0,attrs:{class:"language-powershell"}},[s("code",[s("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" C:\\WINDOWS\\system32> dotnet "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("version\n3"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("0"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("100"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("preview6"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("012264\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 本地安装了好几个.net core sdk版本")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("PS")]),t._v(" C:\\WINDOWS\\system32> dotnet "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("list"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("sdks\n2"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("1"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("700 "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("[C:\\Program Files\\dotnet\\sdk]")]),t._v("\n2"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("2"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("300 "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("[C:\\Program Files\\dotnet\\sdk]")]),t._v("\n3"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("0"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("100"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("preview6"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("012264 "),s("span",{pre:!0,attrs:{class:"token namespace"}},[t._v("[C:\\Program Files\\dotnet\\sdk]")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"cli"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#cli","aria-hidden":"true"}},[this._v("#")]),this._v(" CLI")])},function(){var t=this.$createElement,e=this._self._c||t;return e("blockquote",[e("p",[this._v("说明 "),e("strong",[this._v("所有命令行都在windows10自带的powershell中执行。")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"指定sdk版本"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#指定sdk版本","aria-hidden":"true"}},[this._v("#")]),this._v(" 指定SDK版本")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("dotnet new globaljson --sdk-version 2.2.300\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"net-core"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#net-core","aria-hidden":"true"}},[this._v("#")]),this._v(" .NET Core")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("ul",[s("li",[t._v("二个RunTime\n"),s("ul",[s("li",[t._v(".NET Core RunTime:基础类型系统 、垃圾回收、基元类型等,")]),t._v(" "),s("li",[t._v("ASP .NET RunTime :提供WEB、LOT等应用程序的框架支持。")])])]),t._v(" "),s("li",[t._v(".NET Core CLI工具:各种命令行工具,创建项目、编译项目,发布项目等;")]),t._v(" "),s("li",[t._v("语言编译器:(支持C#、F#、VB等语言)")]),t._v(" "),s("li",[t._v("dotnet 工具:.NET Core运行时和库的安装程序包")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v(".NET Core 运行时")]),this._v(" "),e("li",[this._v("ASP .NET Core 运行时")]),this._v(" "),e("li",[this._v(".NET Core SDK:包括上面二个内容,再加上 .NET CLI工具等")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"项目文档"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#项目文档","aria-hidden":"true"}},[this._v("#")]),this._v(" 项目文档")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"说明"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#说明","aria-hidden":"true"}},[this._v("#")]),this._v(" 说明")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("本项目也是我的学习记录,,用于测试不同类库集成的解决方案,所以用最基础的方案,"),e("strong",[this._v("命令行")]),this._v("来创建项目,引用包,运行,测试等。让自己对 .net core 的原理结构了解地更加深入一些。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("采用的都是 Visual Studio Code/VS2019 +PowerShell运行,关于如何采用Visual Studio 2019创建项目,引用包是非常简单的,不再说明。")])])}],!1,null,null,null);e.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/18.daa8bb13.js b/assets/js/18.daa8bb13.js new file mode 100644 index 0000000..7dd1fa1 --- /dev/null +++ b/assets/js/18.daa8bb13.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[18],{195:function(t,e,s){"use strict";s.r(e);var n=s(0),a=Object(n.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("p",[t._v("认证分为以下情况:当前角色为管理员,该分组配置了权限,该分组未分配某一方法的权限.")]),t._v(" "),s("p",[t._v("状态码(StatusCode):401 UnAuthorized")]),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),s("p",[t._v("返回结果应为:状态码:401 UNAUTHORIZED")]),t._v(" "),t._m(3),t._m(4),t._v(" "),t._m(5),s("p",[t._v("返回结果应为::状态码:401 UNAUTHORIZED")]),t._v(" "),t._m(6),t._m(7),t._v(" "),t._m(8),t._v(" "),s("p",[t._v("返回结果应为:状态码:200")]),t._v(" "),t._m(9),t._m(10),t._v(" "),s("p",[t._v("返回结果应为:状态码:401 UNAUTHORIZED")]),t._v(" "),t._m(11),t._m(12),t._v(" "),t._m(13),t._m(14),t._v(" "),t._m(15),s("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"认证鉴权状态"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#认证鉴权状态","aria-hidden":"true"}},[this._v("#")]),this._v(" 认证鉴权状态")])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("table",[s("thead",[s("tr",[s("th",[t._v("StatusCode")]),t._v(" "),s("th",[t._v("含义")])])]),t._v(" "),s("tbody",[s("tr",[s("td",[t._v("401 UnAuthorized")]),t._v(" "),s("td",[t._v("未授权、无权限、未登录")])]),t._v(" "),s("tr",[s("td",[t._v("422 UNPROCESSABLE ENTITY")]),t._v(" "),s("td",[t._v("令牌失效")])]),t._v(" "),s("tr",[s("td",[t._v("200")]),t._v(" "),s("td",[t._v("访问正常")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ol",[e("li",[this._v("未登录,不带access_token,直接请求需要登录的接口、管理员接口结果一样。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('{\n "error_code": 10000,\n "msg": "认证失败,请检查请求头或者重新登陆",\n "request": "GET /cms/admin/authority"\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ol",{attrs:{start:"2"}},[e("li",[this._v("携带access_token,但非超级管理员(admin字段为2),访问的方法为角色为超管才有权限的方法。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("[LinCmsAuthorize(Roles = LinGroup.Administrator)]\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('{\n "error_code": 10000,\n "msg": "只有超级管理员可操作",\n "request": "GET /cms/admin/authority"\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ol",{attrs:{start:"3"}},[e("li",[this._v("携带access_token ,访问只需要登录的接口(/cms/user/auths)")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("控制器或方法上指定 "),e("strong",[this._v("[Authorize]")]),this._v(" 或 "),e("strong",[this._v("[LinCmsAuthorize]")]),this._v(" 特性标签时,必须登录才能访问,否则返回第一种结果。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('{\n "active": 1,\n "admin": 1,\n "auths": [\n {\n "信息": [\n {\n "auth": "查看lin的信息",\n "module": "信息"\n }\n ]\n }\n ],\n "avatar": null,\n "create_time": 1564372600000,\n "email": "acs@acs.com",\n "group_id": 54,\n "id": 112,\n "nickname": "alan",\n "update_time": 1564487059000\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ol",{attrs:{start:"4"}},[e("li",[this._v("携带access_token,但此用户无访问此方法的权限(即该用户的组别未配置此权限)。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('{\n "error_code": 10000,\n "msg": "权限不够,请联系超级管理员获得权限",\n "request": "GET /cms/log/search"\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ol",{attrs:{start:"5"}},[e("li",[this._v("携带过期的access_token值\n返回结果应为:状态码:401 UNAUTHORIZED")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('{\n "error_code": 10050,\n "msg": "令牌过期",\n "request": "GET /cms/admin/users"\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ol",{attrs:{start:"6"}},[e("li",[this._v("携带不正常的access_token值,后台无法下正常解析出用户信息\n返回结果应为:状态码:422 UNPROCESSABLE ENTITY")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('{\n "error_code": 10040,\n "msg": "令牌失效",\n "request": "GET /cms/admin/users"\n}\n')])])])}],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/19.e7d91a01.js b/assets/js/19.e7d91a01.js new file mode 100644 index 0000000..c756597 --- /dev/null +++ b/assets/js/19.e7d91a01.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[19],{180:function(t,s,a){"use strict";a.r(s);var n=a(0),e=Object(n.a)({},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("p",[t._v("它是.NET下的一个序列化、反序化的基础类库,更基础的用法还是看别人的吧,这里只说一些遇到的问题。")]),t._v(" "),t._m(1),t._v(" "),a("p",[t._v("ContractResolver 默认是小驼峰,我想改成下划线方式,遇到了一些问题,dictionary的键未格式化")]),t._v(" "),a("p",[t._v("in controller 创建一个控制器")]),t._v(" "),t._m(2),a("p",[t._v("In Startup.cs")]),t._v(" "),t._m(3),a("p",[t._v("此时运行后,得到的是Key,而不是key,我想他的键都变成下划线方式的小写")]),t._v(" "),t._m(4),a("p",[t._v("其他测试,增加多级,测试正常")]),t._v(" "),t._m(5),a("p",[t._v("此时运行后,满足要求,多层结构也不会影响")]),t._v(" "),t._m(6),a("p",[t._v("看了Newtonsoft.Json的github,并在in this repository 搜索Dictionary,看issues中的配置项如下即可满足dictionary的键也转小写,\nProcessDictionaryKeys 功能:A flag indicating whether dictionary keys should be processed. Defaults to false.")]),t._v(" "),t._m(7),t._m(8),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),a("p",[t._v("这里的时间戳是毫秒级别")]),t._v(" "),t._m(12),t._m(13),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/JamesNK/Newtonsoft.Json/issues/2088",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/JamesNK/Newtonsoft.Json/issues/2088"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Serialization_NamingStrategy_ProcessDictionaryKeys.htm",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_Serialization_NamingStrategy_ProcessDictionaryKeys.htm"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://blog.csdn.net/yw1688/article/details/38492583",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://blog.csdn.net/yw1688/article/details/38492583"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://www.jianshu.com/p/c53b1a2a121d",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://www.jianshu.com/p/c53b1a2a121d"),a("OutboundLink")],1)])]),t._v(" "),a("RightMenu")],1)},[function(){var t=this.$createElement,s=this._self._c||t;return s("h1",{attrs:{id:"newtonsoft-json基础问题"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#newtonsoft-json基础问题","aria-hidden":"true"}},[this._v("#")]),this._v(" Newtonsoft.Json基础问题")])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"设置下划线"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#设置下划线","aria-hidden":"true"}},[this._v("#")]),this._v(" 设置下划线")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-csharp extra-class"},[a("pre",{pre:!0,attrs:{class:"language-csharp"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("HttpGet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"getDictionary"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" IDictionary"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("GetDictionary")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n IDictionary"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" dics "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token generic-method"}},[a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Dictionary")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n dics"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Key"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Value"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n dics"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"KeyTest"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Value_Test"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" dics"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-csharp extra-class"},[a("pre",{pre:!0,attrs:{class:"language-csharp"}},[a("code",[t._v("services\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddMvc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("SetCompatibilityVersion")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("CompatibilityVersion"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Version_2_2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddJsonOptions")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("opt "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 设置下划线方式,首字母是小写")]),t._v("\n opt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("SerializerSettings"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ContractResolver "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DefaultContractResolver")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n NamingStrategy "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SnakeCaseNamingStrategy")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Key"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Value"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"KeyTest"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Value_Test"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-csharp extra-class"},[a("pre",{pre:!0,attrs:{class:"language-csharp"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("HttpGet")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"get"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("dynamic")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Content "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Url"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("Request"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n NewUrlTest"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test in new url test"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"content"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"url"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/test/get"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"new_url_test"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test in new url test"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-csharp extra-class"},[a("pre",{pre:!0,attrs:{class:"language-csharp"}},[a("code",[t._v("services\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddMvc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("SetCompatibilityVersion")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("CompatibilityVersion"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Version_2_2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddJsonOptions")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("opt "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 设置下划线方式,首字母是小写")]),t._v("\n opt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("SerializerSettings"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ContractResolver "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DefaultContractResolver")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n NamingStrategy "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("SnakeCaseNamingStrategy")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ProcessDictionaryKeys "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("true")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"key"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Value"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"key_test"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Value_Test"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"实现时间戳"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#实现时间戳","aria-hidden":"true"}},[this._v("#")]),this._v(" 实现时间戳")])},function(){var t=this.$createElement,s=this._self._c||t;return s("p",[this._v("前台要的格式为 "),s("strong",[this._v("1562904163734")]),this._v(",只有一个数字,我搜索了一下,也没找到相关的文档,本身这个类库有一些时间戳,不过他们都包含特殊字符,如/Date(1562904163734)/,好像类似这样,他好像在逗我,为啥他要加Date,怕是有毒吧。看到\n他有Converters属性可配置,即配置自己的序列化返回格式。")])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-csharp extra-class"},[a("pre",{pre:!0,attrs:{class:"language-csharp"}},[a("code",[t._v("services"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddMvc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("SetCompatibilityVersion")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("CompatibilityVersion"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Version_2_2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddJsonOptions")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("opt "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=>")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('//opt.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:MM:ss";')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//设置时间戳格式")]),t._v("\n opt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("SerializerSettings"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Converters "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token generic-method"}},[a("span",{pre:!0,attrs:{class:"token function"}},[t._v("List")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("JsonConverter")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LinCmsTimeConverter")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n")])])])},function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("div",{staticClass:"language-csharp extra-class"},[a("pre",{pre:!0,attrs:{class:"language-csharp"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// ")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 配合LinCMS中的时间戳 后台只返回 1562904163734")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// ")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("class")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("LinCmsTimeConverter")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DateTimeConverterBase")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("void")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WriteJson")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("JsonWriter")]),t._v(" writer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("value")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("JsonSerializer")]),t._v(" serializer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("double")]),t._v(" javaScriptTicks "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("value")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("is")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DateTime")]),t._v(" dateTime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n javaScriptTicks "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ConvertDateTimeInt")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dateTime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("value")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("is")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DateTimeOffset")]),t._v(" dateTimeOffset"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("throw")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("JsonSerializationException")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Expected date object value."')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n javaScriptTicks "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ConvertDateTimeInt")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dateTimeOffset"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ToUniversalTime")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("UtcDateTime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n writer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WriteValue")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("javaScriptTicks"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("override")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ReadJson")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("JsonReader")]),t._v(" reader"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("Type")]),t._v(" objectType"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("object")]),t._v(" existingValue"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("JsonSerializer")]),t._v(" serializer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ConvertIntDateTime")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("double")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Parse")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("reader"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ToString")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n \n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// ")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// 日期转换为时间戳(时间戳单位毫秒)")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// ")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('/// ')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("/// ")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("static")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DateTime")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ConvertIntDateTime")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("double")]),t._v(" milliseconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DateTime")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1970")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddMilliseconds")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("milliseconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("public")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("static")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("double")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ConvertDateTimeInt")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DateTime")]),t._v(" aDt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("aDt "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("new")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token class-name"}},[t._v("DateTime")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1970")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("TotalMilliseconds"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,s=this._self._c||t;return s("h2",{attrs:{id:"参考"}},[s("a",{staticClass:"header-anchor",attrs:{href:"#参考","aria-hidden":"true"}},[this._v("#")]),this._v(" 参考")])}],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/2.2c132230.js b/assets/js/2.2c132230.js new file mode 100644 index 0000000..f2ca191 --- /dev/null +++ b/assets/js/2.2c132230.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{153:function(t,n,e){},156:function(t,n,e){"use strict";var a=e(153);e.n(a).a},179:function(t,n,e){"use strict";e.r(n);var a={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,n){var e=n.props,a=n.slots;return t("span",{class:["badge",e.type,e.vertical]},e.text||a().default)}},r=(e(156),e(0)),i=Object(r.a)(a,void 0,void 0,!1,null,"97b6395a",null);n.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/20.497d6fd1.js b/assets/js/20.497d6fd1.js new file mode 100644 index 0000000..e9d8d62 --- /dev/null +++ b/assets/js/20.497d6fd1.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[20],{181:function(t,e,_){"use strict";_.r(e);var r=_(0),s=Object(r.a)({},function(){var t=this,e=t.$createElement,_=t._self._c||e;return _("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),_("p",[t._v("1.什么是cms?")]),t._v(" "),_("p",[t._v("Content Management System,内容管理系统。")]),t._v(" "),_("p",[t._v("2.dotnetcore是什么")]),t._v(" "),_("p",[t._v(".NET Core,是由Microsoft开发,目前在.NET Foundation(一个非营利的开源组织)下进行管理,采用宽松的MIT协议,可构建各种软件,包括Web应用程序、移动应用程序、桌面应用程序、云服务、微服务、API、游戏和物联网应用程序")]),t._v(" "),_("p",[t._v("3.lin-cms 是什么")]),t._v(" "),_("p",[t._v("Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套内容管理系统框架。Lin-CMS 可以有效的帮助开发者提高 CMS 的开发效率, 需要前端?请访问前端仓库。官方团队产品了解请访问TaleLin")]),t._v(" "),_("p",[t._v("4.lin-cms-dotnetcore有哪些特点?")]),t._v(" "),_("p",[t._v("前后端分离,提供后端接口,更少的依赖,后续将实现模块化安装与卸载。")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),_("p",[t._v("BaseType 1 对BaseItem多。")]),t._v(" "),_("p",[t._v("如:标签管理,一个文章下可以设置多个标签,原本需要设计表Tag,字段也大抵为Id,Name,Sort及关联表。\n我们使用BaseType、BaseItem实现。\nBaseType中TypeCode为tag,FullName为标签,id为1时。\nBaseItem中BaseTypeId为1,ItemCode为编码,ItemName为标签。ItemCode为不重复的字符串即可。")]),t._v(" "),_("p",[t._v("另Tag与Article的关联表,需要另设计一个表。")]),t._v(" "),_("p",[t._v("当我们要实现文章类别的下拉,原本需要设计一个表ArticleType,有字段,id,name,sort等。\n我们可以通过BaseType、BaseItem来实现,从而简化这些基础数据。\nBaseType有一条数据,TypeCode为 字符串category、FullName文章类别,BaseItem存多个文章类别(Java、大数据、Python、C#等),编码不同即可。")]),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),_("p",[t._v("使用FreeSql实现时,如果二个表之间没有导航属性,是更复杂的。")]),t._v(" "),t._m(15),t._v(" "),_("p",[t._v("扩展实现一个博客,项目地址:"),_("a",{attrs:{href:"https://github.com/luoyunchong/lin-cms-vue",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/lin-cms-vue"),_("OutboundLink")],1)]),t._v(" "),_("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"开源lin-cms-dotnetcore"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#开源lin-cms-dotnetcore","aria-hidden":"true"}},[this._v("#")]),this._v(" 开源lin-cms-dotnetcore")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("地址:https://github.com/luoyunchong/lin-cms-dotnetcore")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("用户管理、分组管理、分组权限管理、日志系统、文件上传等")]),this._v(" "),e("li",[this._v("更多功能(自定义扩展-模块系统)")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"设计如下"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#设计如下","aria-hidden":"true"}},[this._v("#")]),this._v(" 设计如下")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"字典管理"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#字典管理","aria-hidden":"true"}},[this._v("#")]),this._v(" 字典管理")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"我原本想实现这样的功能:"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#我原本想实现这样的功能:","aria-hidden":"true"}},[this._v("#")]),this._v(" 我原本想实现这样的功能:")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ol",[e("li",[this._v("表结构")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"base-type-(字典类别管理)"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#base-type-(字典类别管理)","aria-hidden":"true"}},[this._v("#")]),this._v(" base_type (字典类别管理)")])},function(){var t=this,e=t.$createElement,_=t._self._c||e;return _("table",[_("thead",[_("tr",[_("th",[t._v("字段")]),t._v(" "),_("th",[t._v("类型")]),t._v(" "),_("th",[t._v("备注")])])]),t._v(" "),_("tbody",[_("tr",[_("td",[t._v("id")]),t._v(" "),_("td",[t._v("int")]),t._v(" "),_("td")]),t._v(" "),_("tr",[_("td",[t._v("type_code")]),t._v(" "),_("td",[t._v("varchar(50)")]),t._v(" "),_("td",[t._v("类别编码")])]),t._v(" "),_("tr",[_("td",[t._v("full_name")]),t._v(" "),_("td",[t._v("varchar(50)")]),t._v(" "),_("td",[t._v("全称")])]),t._v(" "),_("tr",[_("td",[t._v("sort_code")]),t._v(" "),_("td",[t._v("int")]),t._v(" "),_("td",[t._v("排序码")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"base-type-(字典管理)"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#base-type-(字典管理)","aria-hidden":"true"}},[this._v("#")]),this._v(" base_type (字典管理)")])},function(){var t=this,e=t.$createElement,_=t._self._c||e;return _("table",[_("thead",[_("tr",[_("th",[t._v("字段")]),t._v(" "),_("th",[t._v("类型")]),t._v(" "),_("th",[t._v("备注")])])]),t._v(" "),_("tbody",[_("tr",[_("td",[t._v("id")]),t._v(" "),_("td",[t._v("int")]),t._v(" "),_("td")]),t._v(" "),_("tr",[_("td",[t._v("base_type_id")]),t._v(" "),_("td",[t._v("int")]),t._v(" "),_("td",[t._v("类别id(关联base_type的id)")])]),t._v(" "),_("tr",[_("td",[t._v("item_code")]),t._v(" "),_("td",[t._v("varchar(50)")]),t._v(" "),_("td",[t._v("字典编码")])]),t._v(" "),_("tr",[_("td",[t._v("item_name")]),t._v(" "),_("td",[t._v("varchar(50)")]),t._v(" "),_("td",[t._v("字典全称")])]),t._v(" "),_("tr",[_("td",[t._v("sort_code")]),t._v(" "),_("td",[t._v("int")]),t._v(" "),_("td",[t._v("排序码")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"但现实总是事与愿违"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#但现实总是事与愿违","aria-hidden":"true"}},[this._v("#")]),this._v(" 但现实总是事与愿违")])},function(){var t=this.$createElement,e=this._self._c||t;return e("blockquote",[e("p",[this._v("后台取文章列表时,想要取出文章对应的分类,手动join时,总觉得join的表会有些奇怪。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("当然还有其他原因,"),e("strong",[this._v("局限性")]),this._v(":")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ol",[e("li",[this._v("比如使用了这个字典,分类需要增加一个图片字段,就不能满足要求,那怎么办呢,做不了。")]),this._v(" "),e("li",[this._v("比如标签需要实现这个标签下有多少个文章,通过统计也能实现,")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"前端cms"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#前端cms","aria-hidden":"true"}},[this._v("#")]),this._v(" 前端cms")])}],!1,null,null,null);e.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/21.741dcaf7.js b/assets/js/21.741dcaf7.js new file mode 100644 index 0000000..265a7c0 --- /dev/null +++ b/assets/js/21.741dcaf7.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[21],{182:function(t,e,r){"use strict";r.r(e);var i=r(0),s=Object(i.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v("\n😃 A simple and practical CMS implememted by .NET Core 2.2\n"),t._m(1),t._v(" "),t._m(2),t._v(" "),r("p",[t._v("本项目是完全出于个人喜爱,看到Lin-cms有了python,node.js,社区也有人出了"),r("a",{attrs:{href:"https://github.com/ChenJinchuang/lin-cms-tp5",target:"_blank",rel:"noopener noreferrer"}},[t._v("lin-cms-tp5"),r("OutboundLink")],1),t._v("的版本")]),t._v(" "),r("p",[t._v("本项目是 Lin CMS 后端的 "),r("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/dotnet/core/",target:"_blank",rel:"noopener noreferrer"}},[t._v(".NET Core 2.2"),r("OutboundLink")],1),t._v("的 实现,")]),t._v(" "),t._m(3),t._v(" "),r("p",[t._v("Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套"),r("strong",[t._v("内容管理系统框架")]),t._v("。Lin-CMS 可以有效的帮助开发者提高 CMS 的开发效率, 需要前端?请访问"),r("a",{attrs:{href:"https://github.com/TaleLin/lin-cms-vue",target:"_blank",rel:"noopener noreferrer"}},[r("strong",[t._v("前端仓库")]),r("OutboundLink")],1),t._v("。官方团队产品了解请访问"),r("a",{attrs:{href:"https://github.com/TaleLin",target:"_blank",rel:"noopener noreferrer"}},[r("strong",[t._v("TaleLin")]),r("OutboundLink")],1)]),t._v(" "),t._m(4),t._v(" "),r("p",[r("a",{attrs:{href:"https://luoyunchong.github.io/vuepress-docs/dotnetcore/lin-cms/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://luoyunchong.github.io/vuepress-docs/dotnetcore/lin-cms/"),r("OutboundLink")],1)]),t._v(" "),t._m(5),t._v(" "),r("ul",[r("li",[t._v("官方地址 "),r("a",{attrs:{href:"http://face.cms.7yue.pro/#/login",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://face.cms.7yue.pro/#/login"),r("OutboundLink")],1),t._v(" "),t._m(6)]),t._v(" "),r("li",[t._v("本项目swagger地址 "),r("a",{attrs:{href:"http://47.106.80.39:5001/swagger/index.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://47.106.80.39:5001/swagger/index.html"),r("OutboundLink")],1)])]),t._v(" "),t._m(7),t._v(" "),r("ul",[r("li",[t._v("在原"),r("a",{attrs:{href:"https://github.com/TaleLin/lin-cms-vue",target:"_blank",rel:"noopener noreferrer"}},[t._v("开源项目"),r("OutboundLink")],1),t._v("中增加了博客文章、回复、留言板、标签、文章分组管理、插件式功能(还没有)")]),t._v(" "),r("li",[r("a",{attrs:{href:"https://github.com/luoyunchong/lin-cms-vue",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/lin-cms-vue"),r("OutboundLink")],1)])]),t._v(" "),t._m(8),t._v(" "),r("p",[t._v("Lin CMS 的构筑思想是有其自身特点的。下面我们阐述一些 Lin 的主要特点。")]),t._v(" "),t._m(9),t._v(" "),r("p",[t._v("这意味着,Lin 既提供后台的支撑,也有一套对应的前端系统,")]),t._v(" "),r("p",[t._v("首先,传统的网站开发更多的是采用服务端渲染的方式,需用使用一种模板语言在服务端完成页面渲染:比如 Razor等模板技术。")]),t._v(" "),r("p",[t._v("服务端渲染的好处在于可以比较好的支持 SEO,但作为内部使用的 CMS 管理系统,SEO 并不重要。")]),t._v(" "),r("p",[t._v("但一个不可忽视的事实是,服务器渲染的页面到底是由前端开发者来完成,还是由服务器开发者来完成?其实都不太合适。现在已经没有多少前端开发者是了解这些服务端模板语言的,而服务器开发者本身是不太擅长开发页面的。那还是分开吧,前端用最熟悉的 Vue 写 JS 和 CSS,而服务器只关注自己的 API 即可。")]),t._v(" "),r("p",[t._v("其次,单页面应用程序的体验本身就要好于传统网站。")]),t._v(" "),t._m(10),t._v(" "),r("p",[t._v("Lin 已经内置了 CMS 中最为常见的需求:用户管理、权限管理、日志系统等。开发者只需要集中精力开发自己的 CMS 业务即可")]),t._v(" "),r("p",[t._v("更多关于Lin CMS的介绍请访问"),r("a",{attrs:{href:"http://doc.cms.7yue.pro/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Lin CMS线上文档"),r("OutboundLink")],1)]),t._v(" "),t._m(11),t._v(" "),r("p",[t._v("由于 Lin 采用的是前后端分离的架构,所以你至少需要熟悉 C# 和 Vue。")]),t._v(" "),t._m(12),t._v(" "),r("p",[t._v("该项目的Lin 的服务端框架是基于"),r("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/dotnet/core/",target:"_blank",rel:"noopener noreferrer"}},[t._v(".NET Core 2.2"),r("OutboundLink")],1),t._v("构建的,所以如果你比较熟悉Mvc、WebAPI、过滤器等概念,或者是 有.NET Framework中Mvc开发经验,相信你一定很容易写出代码。")]),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),r("p",[t._v("前端需要开发者比较熟悉 Vue 的,另外需要了解 ES6,axios,ElementUi、webpack、Vuex、Vue-Router等等等")]),t._v(" "),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),r("img",{staticClass:"QR-img",attrs:{width:"258",height:"300",src:"https://ae01.alicdn.com/kf/Hed659970c86c4004b42480fe7d7f97acW.jpg"}}),t._v(" "),r("img",{staticClass:"QR-img",staticStyle:{"margin-left":"10px"},attrs:{width:"258",height:"300",src:"https://ae01.alicdn.com/kf/H6c1668c7987a436caae6b19ee6b86af5J.jpg"}}),t._v(" "),t._m(19),t._v(" "),r("p",[t._v("微信搜索:林间有风")]),t._v(" "),r("img",{staticClass:"QR-img",attrs:{src:"https://ae01.alicdn.com/kf/H4e69faac4a834b8a82f54ea05d2dd53av.jpg"}}),t._v(" "),t._m(20),t._v(" "),r("ol",[r("li",[t._v("迁移Mock.Luo项目至lin-cms-dotnetcore,")]),t._v(" "),r("li",[t._v("实现模块化开发,支持基础组件安装与卸载。")]),t._v(" "),r("li",[t._v("实现abp vnext的文档的功能 "),r("a",{attrs:{href:"https://docs.abp.io/en/abp/latest",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://docs.abp.io/en/abp/latest"),r("OutboundLink")],1)]),t._v(" "),r("li",[t._v("写文档。")])]),t._v(" "),t._m(21),t._v(" "),t._m(22),t._v(" "),r("ul",[r("li",[t._v("GitHub 链接后端接口 "),r("a",{attrs:{href:"https://github.com/luoyunchong/lin-cms-dotnetcore",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/lin-cms-dotnetcore"),r("OutboundLink")],1)]),t._v(" "),r("li",[t._v("管理后台UI "),r("a",{attrs:{href:"https://github.com/luoyunchong/lin-cms-vue",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/lin-cms-vue"),r("OutboundLink")],1)]),t._v(" "),r("li",[t._v("前端UI"),r("a",{attrs:{href:"https://github.com/luoyunchong/lin-cms-vvlog",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/lin-cms-vvlog"),r("OutboundLink")],1)])])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{align:"center"}},[e("a",{attrs:{href:"http://doc.cms.7yue.pro/"}},[e("img",{attrs:{width:"200",src:"https://consumerminiaclprd01.blob.core.chinacloudapi.cn/miniappbackground/sfgmember/lin/left-logo.png"}})]),this._v(" "),e("h1",[this._v("\n Lin CMS .NET Core\n ")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"项目简介"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#项目简介","aria-hidden":"true"}},[this._v("#")]),this._v(" 项目简介")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"https://travis-ci.org/luoyunchong/lin-cms-dotnetcore.svg?branch=master",alt:"Build Status"}}),this._v(" "),e("img",{attrs:{src:"https://img.shields.io/badge/.NETCore-2.2.0-3963bc.svg",alt:""}}),this._v(" "),e("img",{attrs:{src:"https://img.shields.io/badge/license-MIT-3963bc.svg",alt:""}}),this._v(" "),e("img",{attrs:{src:"https://img.shields.io/badge/developer-IGeekFan-3963bc.svg",alt:""}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"什么是-lin-cms?"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#什么是-lin-cms?","aria-hidden":"true"}},[this._v("#")]),this._v(" 什么是 Lin CMS?")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"线上文档地址-完善中"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#线上文档地址-完善中","aria-hidden":"true"}},[this._v("#")]),this._v(" 线上文档地址(完善中)")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"线上-demo"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#线上-demo","aria-hidden":"true"}},[this._v("#")]),this._v(" 线上 Demo")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("用户名: super")]),this._v(" "),e("li",[this._v("密码: 123456")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"前端"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#前端","aria-hidden":"true"}},[this._v("#")]),this._v(" 前端")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"lin-cms-的特点"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#lin-cms-的特点","aria-hidden":"true"}},[this._v("#")]),this._v(" Lin CMS 的特点")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"lin-cms-是一个前后端分离的-cms-解决方案"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#lin-cms-是一个前后端分离的-cms-解决方案","aria-hidden":"true"}},[this._v("#")]),this._v(" Lin CMS 是一个前后端分离的 CMS 解决方案")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"框架本身已内置了-cms-常用的功能"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#框架本身已内置了-cms-常用的功能","aria-hidden":"true"}},[this._v("#")]),this._v(" 框架本身已内置了 CMS 常用的功能")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"所需基础"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#所需基础","aria-hidden":"true"}},[this._v("#")]),this._v(" 所需基础")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"后端-c"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#后端-c","aria-hidden":"true"}},[this._v("#")]),this._v(" 后端 C#")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"后端主要技术"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#后端主要技术","aria-hidden":"true"}},[this._v("#")]),this._v(" 后端主要技术")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ul",[r("li",[t._v("数据库:FreeSql+MySQL5.6")]),t._v(" "),r("li",[t._v("ASP.NET Core2.2+MVC")]),t._v(" "),r("li",[t._v("简化对象映射:AutoMapper")]),t._v(" "),r("li",[t._v("身份认证框架:IdentityServer4")]),t._v(" "),r("li",[t._v("Json Web令牌:JWT")]),t._v(" "),r("li",[t._v("文档:Swagger")]),t._v(" "),r("li",[t._v("序列化:Newtonsoft.Json")]),t._v(" "),r("li",[t._v("测试框架:Xunit")]),t._v(" "),r("li",[t._v("日志 NLog")]),t._v(" "),r("li",[t._v("简化注入服务:Scrutor")]),t._v(" "),r("li",[t._v("通用扩展方法 Z.ExtensionMethods")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"前端-2"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#前端-2","aria-hidden":"true"}},[this._v("#")]),this._v(" 前端")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"讨论交流"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#讨论交流","aria-hidden":"true"}},[this._v("#")]),this._v(" 讨论交流")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"qq-交流群"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#qq-交流群","aria-hidden":"true"}},[this._v("#")]),this._v(" QQ 交流群")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("林间有风(lin-cms-vue) QQ 群号:643205479")]),this._v(" "),e("li",[this._v(".NET Core搬砖队(lin-cms-dotnetcore) QQ群号:762828442")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"微信公众号"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#微信公众号","aria-hidden":"true"}},[this._v("#")]),this._v(" 微信公众号")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"下个版本开发计划"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#下个版本开发计划","aria-hidden":"true"}},[this._v("#")]),this._v(" 下个版本开发计划")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ul",[r("li",[t._v("[x] 创建时间、创建人、修改时间、修改人、删除人、删除时间、是否删除(软删除)")]),t._v(" "),r("li",[t._v("[x] 部署")]),t._v(" "),r("li",[t._v("[ ] 系统访问日志、错误日志可视化")]),t._v(" "),r("li",[t._v("[ ] 完善文档")]),t._v(" "),r("li",[t._v("[ ] 重构核心库结构")]),t._v(" "),r("li",[t._v("[ ] 基于lin-cms-vue的基础,将之前Mock.luo项目中的博客迁移过来\n"),r("ul",[r("li",[t._v("[x] 博客随笔发布/编辑/删除")]),t._v(" "),r("li",[t._v("[ ] 留言板")]),t._v(" "),r("li",[t._v("[x] 评论回复、审核、点赞等")]),t._v(" "),r("li",[t._v("[x] 前端展示博客效果")]),t._v(" "),r("li",[t._v("[x] 基础资料 BaseItem")]),t._v(" "),r("li",[t._v("[x] 类别管理 BaseType")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"开源地址"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#开源地址","aria-hidden":"true"}},[this._v("#")]),this._v(" 开源地址")])}],!1,null,null,null);e.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/22.fd0df8ae.js b/assets/js/22.fd0df8ae.js new file mode 100644 index 0000000..0680a63 --- /dev/null +++ b/assets/js/22.fd0df8ae.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[22],{183:function(t,e,r){"use strict";r.r(e);var i=r(0),n=Object(i.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("p",[t._v("有个需求,就是在. NET Core中,我们想在项目 启动时,获取LinCmsAuthorizeAttribute这个特性标签所有出现的地方,把他的参数,放入一个集合并缓存起来,以便后面使用此数据用于权限验证。")]),t._v(" "),r("p",[t._v("我们通过反射获取所有控制器下及方法的Attribute。")]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://www.cnblogs.com/RainingNight/p/dynamic-authorization-in-asp-net-core.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://www.cnblogs.com/RainingNight/p/dynamic-authorization-in-asp-net-core.html"),r("OutboundLink")],1)])]),t._v(" "),r("p",[t._v("当然,这个只是部分代码,完整代码请查看最下方开源地址,其中LinCmsAuthorizeAttribute继承AuthorizeAttribute,拥有指定角色权限控制,当Permission未指定时,当过滤器与Authorize功能相同。Module是指模块,即多个权限,属于同一个模块,方便前台展示为树型结构。Permission属性的值不可重复。")]),t._v(" "),t._m(4),t._m(5),t._v(" "),r("p",[t._v("在 LinCms.Web中的Controller,至于为什么Permission为中文,目前的主要原因,此项目用于适配 "),r("a",{attrs:{href:"https://github.com/TaleLin/lin-cms-vue",target:"_blank",rel:"noopener noreferrer"}},[t._v("Lin-CMS-VUE"),r("OutboundLink")],1),t._v("项目,所以于平常我们以某个字符串作为权限名不同,但不须大惊小怪,道理相同。")]),t._v(" "),t._m(6),t._m(7),t._v(" "),r("p",[t._v("in xunit test 项目工程中,开始我们的测试")]),t._v(" "),t._m(8),t._m(9),t._v(" "),r("p",[t._v("可在输出文本中查看,正是我们想要的东西,最后一行,是其他Controller中的内容,而且我们重写了ToString(),所以我们能看到其属性。")]),t._v(" "),t._m(10),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),r("p",[t._v("只有AdminController加了此标签,所以只有一行。")]),t._v(" "),t._m(14),r("p",[t._v('此时Roles为Administrator,Permission及Module都是null,\n这是因为只有AdminController中加了LinGroup.Administrator="Administrator"字符串,在登录过程中,已经给当前登录用户设置了 new Claim(ClaimTypes.Role,user.IsAdmin()?LinGroup.Administrator:user.GroupId.ToString()),即"Administrator,当用户访问AdminController中的方法时,LinCmsAuthorize并没有做相关验证,都是AuthorizeAttribute,实现了固定角色权限的判断及登录的判断。LinCmsAuthorize完成了固定权限设置为不同的动态角色后,判断用户是否拥有此权限。')]),t._v(" "),t._m(15),t._m(16),t._v(" "),r("ul",[r("li",[t._v("c# – 如何在asp. net core rc2中获取控制器的自定义属性 "),r("a",{attrs:{href:"https://codeday.me/bug/20181207/453278.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://codeday.me/bug/20181207/453278.html"),r("OutboundLink")],1)]),t._v(" "),r("li")]),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),r("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"获取控制器及方法特性标签"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#获取控制器及方法特性标签","aria-hidden":"true"}},[this._v("#")]),this._v(" 获取控制器及方法特性标签")])},function(){var t=this.$createElement,e=this._self._c||t;return e("blockquote",[e("p",[this._v(".NET Core 反射获取所有控制器及方法上特定标签.")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"lincmsauthorizeattribute是什么"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#lincmsauthorizeattribute是什么","aria-hidden":"true"}},[this._v("#")]),this._v(" LinCmsAuthorizeAttribute是什么")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("其代码非常简单,用于自定义权限验证,通过重写OnAuthorizationAsync方法,实现固定权限可分配给动态角色(也能分配给动态用户)。主要就"),e("strong",[this._v("基于权限的授权")]),this._v("的实现进行研究,实现方法级别的权限验证。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]\npublic class LinCmsAuthorizeAttribute : AuthorizeAttribute, IAsyncAuthorizationFilter\n{\n public string Permission { get; set; }\n public string Module { get; set; }\n\n public LinCmsAuthorizeAttribute()\n {\n\n }\n\n public LinCmsAuthorizeAttribute(string permission,string module)\n {\n Permission = permission;\n Module = module;\n }\n\n public LinCmsAuthorizeAttribute(string permission,string module, string policy) : base(policy)\n {\n Permission = permission;\n Module = module;\n }\n\n public async Task OnAuthorizationAsync(AuthorizationFilterContext context)\n {\n if (Permission == null) return;\n var authorizationService = (IAuthorizationService)context.HttpContext.RequestServices.GetService(typeof(IAuthorizationService));\n var authorizationResult = await authorizationService.AuthorizeAsync(context.HttpContext.User, null, new OperationAuthorizationRequirement() { Name = Permission });\n if (!authorizationResult.Succeeded)\n {\n context.Result = new ForbidResult();\n }\n }\n\n public override string ToString()\n {\n return $"\\"{base.ToString()}\\",\\"Permission:{Permission}\\",\\"Module:{Module}\\",\\"Roles:{Roles}\\",\\"Policy:{Policy}\\",\\"AuthenticationSchemes:{AuthenticationSchemes}\\"";\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"controller"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#controller","aria-hidden":"true"}},[this._v("#")]),this._v(" Controller")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[Route("cms/log")]\n[ApiController]\npublic class LogController : ControllerBase\n{\n private readonly ILogService _logService;\n\n public LogController(ILogService logService)\n {\n _logService = logService;\n }\n\n [HttpGet("users")]\n [LinCmsAuthorize("查询日志记录的用户", "日志")]\n public List GetLoggedUsers([FromQuery]PageDto pageDto)\n {\n return _logService.GetLoggedUsers(pageDto);\n }\n\n \n [HttpGet]\n [LinCmsAuthorize("查询所有日志", "日志")]\n public PagedResultDto GetLogs([FromQuery]LogSearchDto searchDto)\n {\n return _logService.GetLogUsers(searchDto);\n }\n\n [HttpGet("search")]\n [LinCmsAuthorize("搜索日志", "日志")]\n public PagedResultDto SearchLogs([FromQuery]LogSearchDto searchDto)\n {\n return _logService.GetLogUsers(searchDto);\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"测试类获取方法上的特定标签"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#测试类获取方法上的特定标签","aria-hidden":"true"}},[this._v("#")]),this._v(" 测试类获取方法上的特定标签")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("[Fact]\npublic void GetAssemblyMethodsAttributes()\n{\n var assembly = typeof(Startup).Assembly.GetTypes().AsEnumerable()\n .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToList();\n\n assembly.ForEach(r =>\n {\n foreach (var methodInfo in r.GetMethods())\n {\n foreach (Attribute attribute in methodInfo.GetCustomAttributes())\n {\n if (attribute is LinCmsAuthorizeAttribute linCmsAuthorize)\n {\n _testOutputHelper.WriteLine(linCmsAuthorize.ToString());\n }\n }\n }\n });\n} \n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"方法结果"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#方法结果","aria-hidden":"true"}},[this._v("#")]),this._v(" 方法结果")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('"LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:查询日志记录的用户","Module:日志","Roles:","Policy:","AuthenticationSchemes:"\n"LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:查询所有日志","Module:日志","Roles:","Policy:","AuthenticationSchemes:"\n"LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:搜索日志","Module:日志","Roles:","Policy:","AuthenticationSchemes:"\n"LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:查看lin的信息","Module:信息","Roles:","Policy:","AuthenticationSchemes:"\n\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"获取控制器上特性标签"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#获取控制器上特性标签","aria-hidden":"true"}},[this._v("#")]),this._v(" 获取控制器上特性标签")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('/// \n/// 获取控制器上的LinCmsAuthorizeAttribute\n/// \n/// "LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:","Module:","Roles:Administrator","Policy:","AuthenticationSchemes:"\n[Fact]\npublic void GetControllerAttributes()\n{\n var assembly = typeof(Startup).Assembly.GetTypes().AsEnumerable()\n .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToList();\n\n assembly.ForEach(d =>\n {\n var linCmsAuthorize = d.GetCustomAttribute();\n if (linCmsAuthorize != null)\n {\n _testOutputHelper.WriteLine(linCmsAuthorize.ToString());\n }\n });\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"controller结果"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#controller结果","aria-hidden":"true"}},[this._v("#")]),this._v(" Controller结果")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('"LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:","Module:","Roles:Administrator","Policy:","AuthenticationSchemes:"\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("[LinCmsAuthorize(Roles = LinGroup.Administrator)]\npublic class AdminController : ControllerBase\n{\n ...\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"参考"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#参考","aria-hidden":"true"}},[this._v("#")]),this._v(" 参考")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"开源地址"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#开源地址","aria-hidden":"true"}},[this._v("#")]),this._v(" 开源地址")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[e("a",{attrs:{href:"github.com/luoyunchong/lin-cms-dotnetcore"}},[this._v("github.com/luoyunchong/lin-cms-dotnetcore")])])])}],!1,null,null,null);e.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/23.bb985633.js b/assets/js/23.bb985633.js new file mode 100644 index 0000000..2928e6b --- /dev/null +++ b/assets/js/23.bb985633.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[23],{184:function(t,e,a){"use strict";a.r(e);var n=a(0),r=Object(n.a)({},function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"content"},[t._m(0),t._v(" "),a("p",[t._v("基于ToolGood.Words类库,配合敏感字的文本文件,写的API接口。")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-%E6%95%8F%E6%84%9F%E8%AF%8D%E5%A4%84%E7%90%86",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-%E6%95%8F%E6%84%9F%E8%AF%8D%E5%A4%84%E7%90%86"),a("OutboundLink")],1)])]),t._v(" "),a("p",[t._v("一共二种方式")]),t._v(" "),t._m(1),t._v(" "),a("p",[t._v("类库配合敏感库")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/toolgood/ToolGood.Words",target:"_blank",rel:"noopener noreferrer"}},[t._v("ToolGood.Words"),a("OutboundLink")],1)])]),t._v(" "),a("p",[t._v("简单用法")]),t._v(" "),t._m(2),a("p",[t._v("配合敏感库文本文件,写的工具类")]),t._v(" "),a("p",[t._v("二个文件放到wwwroot/_Illegal目录下,通过ReplaceStopWords方法调用即可对")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://github.com/toolgood/ToolGood.Words/blob/master/csharp/ToolGood.Words.Test/_Illegal/IllegalKeywords.txt",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/toolgood/ToolGood.Words/blob/master/csharp/ToolGood.Words.Test/_Illegal/IllegalKeywords.txt"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/toolgood/ToolGood.Words/blob/master/csharp/ToolGood.Words.Test/_Illegal/IllegalUrls.txt",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/toolgood/ToolGood.Words/blob/master/csharp/ToolGood.Words.Test/_Illegal/IllegalUrls.txt"),a("OutboundLink")],1)])]),t._v(" "),t._m(3),t._m(4),t._v(" "),a("p",[t._v("方案:通过维护敏感库,循环replace\n大佬分享给我的,稍微改成了从文件中获取敏感字。")]),t._v(" "),t._m(5),t._m(6),t._v(" "),a("p",[t._v("使用FreeSql这个ORM时,全局处理string类型的值,进行敏感词处理。代码在StartUp.cs的构造函数中。")]),t._v(" "),t._m(7),a("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"全局敏感词处理"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#全局敏感词处理","aria-hidden":"true"}},[this._v("#")]),this._v(" 全局敏感词处理")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"_1-toolgood-words"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1-toolgood-words","aria-hidden":"true"}},[this._v("#")]),this._v(" 1.ToolGood.Words")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[Fact]\npublic void IssuesTest_17()\n{\n var illegalWordsSearch = new IllegalWordsSearch();\n string s = "中国|zg人|abc";\n illegalWordsSearch.SetKeywords(s.Split(\'|\'));\n var str = illegalWordsSearch.Replace("我是中美国人厉害中国完美abcddb好的", \'*\');\n\n Assert.Equal("我是中美国人厉害**完美***ddb好的", str);\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('public class ToolGoodUtils\n{\n //敏感库只要这二个文件存在即可\n //本地敏感库缓存-https://github.com/toolgood/ToolGood.Words/tree/master/csharp/ToolGood.Words.Test/_Illegal\n //因为需要上传至github并同步gitee,安全起见,所以未上传至git,需要自行下载并复制\n private const string KeywordsPath = "wwwroot/_Illegal/IllegalKeywords.txt";\n private const string UrlsPath = "wwwroot/_Illegal/IllegalUrls.txt";\n\n private const string InfoPath = "wwwroot/_Illegal/IllegalInfo.txt";\n private const string BitPath = "wwwroot/_Illegal/IllegalBit.iws";\n\n private static IllegalWordsSearch _search;\n /// \n /// 本地敏感库,文件修改后,重新创建缓存Bit\n /// \n /// \n public static IllegalWordsSearch GetIllegalWordsSearch()\n {\n if (_search == null)\n {\n string ipath = Path.GetFullPath(InfoPath);\n if (File.Exists(ipath) == false)\n {\n _search = CreateIllegalWordsSearch();\n }\n else\n {\n var texts = File.ReadAllText(ipath).Split(\'|\');\n if (new FileInfo(Path.GetFullPath(KeywordsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") !=\n texts[0] ||\n new FileInfo(Path.GetFullPath(UrlsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") !=\n texts[1]\n )\n {\n _search = CreateIllegalWordsSearch();\n }\n else\n {\n var s = new IllegalWordsSearch();\n s.Load(Path.GetFullPath(BitPath));\n _search = s;\n }\n }\n }\n return _search;\n }\n\n private static IllegalWordsSearch CreateIllegalWordsSearch()\n {\n string[] words1 = File.ReadAllLines(Path.GetFullPath(KeywordsPath), Encoding.UTF8);\n string[] words2 = File.ReadAllLines(Path.GetFullPath(UrlsPath), Encoding.UTF8);\n var words = new List();\n foreach (var item in words1)\n {\n words.Add(item.Trim());\n }\n foreach (var item in words2)\n {\n words.Add(item.Trim());\n }\n\n var search = new IllegalWordsSearch();\n search.SetKeywords(words);\n\n search.Save(Path.GetFullPath(BitPath));\n\n var text = new FileInfo(Path.GetFullPath(KeywordsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") + "|"+ new FileInfo(Path.GetFullPath(UrlsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss");\n File.WriteAllText(Path.GetFullPath(InfoPath), text);\n\n return search;\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"_2-循环使用replace"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2-循环使用replace","aria-hidden":"true"}},[this._v("#")]),this._v(" 2. 循环使用Replace")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('public static class StopWords\n{\n\n static readonly ConcurrentDictionary FunNlpDataSensitive = new ConcurrentDictionary();\n static readonly ConcurrentDictionary ReplaceNewValue = new ConcurrentDictionary();\n\n private const string KeywordsPath = "wwwroot/_Illegal/IllegalKeywords.txt";\n private const string UrlsPath = "wwwroot/_Illegal/IllegalUrls.txt";\n\n\n static StopWords()\n {\n LoadDataFromFile();\n }\n\n public static void LoadDataFromFile()\n {\n string words1 = File.ReadAllText(Path.GetFullPath(KeywordsPath), Encoding.UTF8);\n string words2 = File.ReadAllText(Path.GetFullPath(UrlsPath), Encoding.UTF8);\n LoadDataFromText(words1);\n LoadDataFromText(words2);\n }\n\n\n public static void LoadDataFromText(string text)\n {\n int oldcount = FunNlpDataSensitive.Count;\n foreach (string wd in text.Split(\'\\n\'))\n {\n string keykey = wd.Trim().Trim(\'\\r\', \'\\n\').Trim();\n if (string.IsNullOrEmpty(keykey)) continue;\n FunNlpDataSensitive.TryAdd(keykey, true);\n if (ReplaceNewValue.ContainsKey(keykey.Length) == false)\n ReplaceNewValue.TryAdd(keykey.Length, "".PadRight(keykey.Length, \'*\'));\n }\n Console.WriteLine($"敏感词加载完毕,增加数量:{FunNlpDataSensitive.Count - oldcount}");\n }\n\n\n /// \n /// 替换所有敏感词为 *\n /// \n /// \n /// \n public static string ReplaceStopWords(this string that)\n {\n foreach (var wd in FunNlpDataSensitive.Keys)\n that = that.Replace(wd, ReplaceNewValue.TryGetValue(wd.Length, out var tryval) ? tryval : "".PadRight(wd.Length, \'*\'));\n return that;\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"freesql全局处理敏感词"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#freesql全局处理敏感词","aria-hidden":"true"}},[this._v("#")]),this._v(" FreeSql全局处理敏感词")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("//敏感词处理\nIllegalWordsSearch illegalWords = ToolGoodUtils.GetIllegalWordsSearch();\n\nFsql.Aop.AuditValue += (s, e) =>\n{\n if (e.Column.CsType == typeof(string) && e.Value != null)\n {\n string oldVal = (string)e.Value;\n string newVal = illegalWords.Replace(oldVal);\n //第二种处理敏感词的方式\n //string newVal = oldVal.ReplaceStopWords();\n if (newVal != oldVal)\n {\n e.Value = newVal;\n }\n }\n};\n")])])])}],!1,null,null,null);e.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/24.70f91dc9.js b/assets/js/24.70f91dc9.js new file mode 100644 index 0000000..5b9df97 --- /dev/null +++ b/assets/js/24.70f91dc9.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[24],{185:function(e,t,s){"use strict";s.r(t);var n=s(0),r=Object(n.a)({},function(){var e=this,t=e.$createElement,s=e._self._c||t;return s("div",{staticClass:"content"},[e._m(0),e._v(" "),s("p",[e._v("官网介绍")]),e._v(" "),s("p",[s("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2",target:"_blank",rel:"noopener noreferrer"}},[e._v("https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2"),s("OutboundLink")],1)]),e._v(" "),e._m(1),e._v(" "),s("ul",[s("li",[e._v("开源地址"),s("a",{attrs:{href:"https://github.com/khellang/Scrutor",target:"_blank",rel:"noopener noreferrer"}},[e._v("https://github.com/khellang/Scrutor"),s("OutboundLink")],1)]),e._v(" "),s("li",[e._v("参考文档 "),s("a",{attrs:{href:"https://www.cnblogs.com/catcher1994/p/10316928.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("https://www.cnblogs.com/catcher1994/p/10316928.html"),s("OutboundLink")],1),e._v("\n手动管理依赖注入过于麻烦,当有多个仓储,服务,无法统一注入,Scrutor能帮助我们简化ASP.NET Core的DI注册。")])]),e._v(" "),s("p",[e._v("在ConfigServices中,我们原本需要这样子依次注入仓储,服务和其他接口及实现,当有多个仓储时,这样就过于繁琐。")]),e._v(" "),e._m(2),e._m(3),e._v(" "),s("p",[e._v("当我们有多个Service后缀的服务时,使用以下方法,可将服务扫描只留下以Serivce结尾的类,将其类型注册为提供所有公共接口生成服务,其生命周期为Transient,")]),e._v(" "),e._m(4),e._m(5),e._v(" "),s("p",[e._v("新建一个空接口,当其他类继承此接口后,统一注入到DI中,以Transient的生命周期。")]),e._v(" "),e._m(6),e._m(7),e._v(" "),e._m(8),e._m(9),e._v(" "),e._m(10),s("p",[e._v("扫描所有继承ITransientDependency的实现。")]),e._v(" "),e._m(11),e._m(12),e._v(" "),s("p",[e._v("在其他类中使用此接口")]),e._v(" "),e._m(13),e._m(14),e._v(" "),s("p",[e._v("当然,我们可以统一注入,而非写二次servics.Scan")]),e._v(" "),e._m(15),s("RightMenu")],1)},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"依赖注入"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#依赖注入","aria-hidden":"true"}},[this._v("#")]),this._v(" 依赖注入")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"scrutor"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#scrutor","aria-hidden":"true"}},[this._v("#")]),this._v(" Scrutor")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("services.AddTransient();\nservices.AddTransient();\nservices.AddTransient();\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"serivce后缀服务注入di"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#serivce后缀服务注入di","aria-hidden":"true"}},[this._v("#")]),this._v(" Serivce后缀服务注入DI")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('services.Scan(scan => scan\n //加载Startup这个类所在的程序集\n .FromAssemblyOf()\n // 表示要注册那些类,上面的代码还做了过滤,只留下了以 Service 结尾的类\n .AddClasses(classes => classes.Where(t => t.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase)))\n //表示将类型注册为提供其所有公共接口作为服务\n .AsImplementedInterfaces()\n //表示注册的生命周期为 Transient\n .WithTransientLifetime()\n );\n\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"itransientdependency"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#itransientdependency","aria-hidden":"true"}},[this._v("#")]),this._v(" ITransientDependency")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("namespace LinCms.Zero.Dependency\n{\n public interface ITransientDependency\n {\n }\n}\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"接口"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#接口","aria-hidden":"true"}},[this._v("#")]),this._v(" 接口")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v("public interface ICurrentUser\n{\n int? Id { get; }\n\n int? GroupId { get; }\n\n bool? IsAdmin { get; }\n}\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"模拟实现"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#模拟实现","aria-hidden":"true"}},[this._v("#")]),this._v(" 模拟实现")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v(" public class CurrentUser : ICurrentUser, ITransientDependency\n {\n \n public int? Id => 1;\n public int? GroupId => 2;\n public bool? IsAdmin => true;\n }\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v(" services.Scan(scan => scan\n // We start out with all types in the assembly of ITransientService\n .FromAssemblyOf()\n // AddClasses starts out with all public, non-abstract types in this assembly.\n // These types are then filtered by the delegate passed to the method.\n // In this case, we filter out only the classes that are assignable to ITransientService.\n .AddClasses(classes => classes.AssignableTo())\n // We then specify what type we want to register these classes as.\n // In this case, we want to register the types as all of its implemented interfaces.\n // So if a type implements 3 interfaces; A, B, C, we'd end up with three separate registrations.\n .AsImplementedInterfaces()\n // And lastly, we specify the lifetime of these registrations.\n .WithTransientLifetime()\n );\n\n")])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"如何使用"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#如何使用","aria-hidden":"true"}},[this._v("#")]),this._v(" 如何使用")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('[ApiController]\n[Route("cms/user")]\npublic class UserController : ControllerBase\n{\n private readonly ICurrentUser _currentUser;\n\n public UserController(ICurrentUser currentUser)\n {\n _currentUser = currentUser;\n }\n\n [HttpGet]\n public int GetUser()\n {\n return _currentUser.Id;\n }\n}\n')])])])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"统一注入"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#统一注入","aria-hidden":"true"}},[this._v("#")]),this._v(" 统一注入")])},function(){var e=this.$createElement,t=this._self._c||e;return t("div",{staticClass:"language- extra-class"},[t("pre",{pre:!0,attrs:{class:"language-text"}},[t("code",[this._v('services.Scan(scan => scan\n .FromAssemblyOf()\n .AddClasses(classes => classes.Where(t => t.Name.EndsWith("Service",StringComparison.OrdinalIgnoreCase)))\n .AsImplementedInterfaces()\n .WithTransientLifetime()\n .FromAssemblyOf()\n .AddClasses(classes => classes.AssignableTo())\n .AsImplementedInterfaces()\n .WithTransientLifetime()\n );\n')])])])}],!1,null,null,null);t.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/25.68611935.js b/assets/js/25.68611935.js new file mode 100644 index 0000000..2da3806 --- /dev/null +++ b/assets/js/25.68611935.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{186:function(e,t,r){"use strict";r.r(t);var s=r(0),a=Object(s.a)({},function(){var e=this,t=e.$createElement,r=e._self._c||t;return r("div",{staticClass:"content"},[e._m(0),e._v(" "),e._m(1),e._v(" "),r("p",[e._v("无")]),e._v(" "),e._m(2),e._v(" "),e._m(3),e._v(" "),r("ul",[r("li",[r("p",[r("a",{attrs:{href:"https://luoyunchong.github.io/vuepress-docs/dotnetcore/examples/FreeSql-in-asp.net-core-webapi-how-to-use.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("主要在介绍FreeSql在ASP.NTE Core WebApi中如何使用的过程,完成一个最简单的博客系统的后端接口"),r("OutboundLink")],1)])]),e._v(" "),r("li",[r("p",[r("a",{attrs:{href:"https://luoyunchong.github.io/vuepress-docs/dotnetcore/examples/FreeSql-sample-blog-RESTful-use-automapper.html",target:"_blank",rel:"noopener noreferrer"}},[e._v("本文使用ASP .NET Core的WEB API,构建一个RESTful风格的接口,使用Freesql访问MySQL数据库,实现二个表的简单博客,并集成AutoMapper。"),r("OutboundLink")],1)])])]),e._v(" "),e._m(4),e._v(" "),r("ul",[r("li",[r("router-link",{attrs:{to:"/dotnetcore/examples/IdentityServer4.html"}},[e._v("IdentityServer4 在本项目中的应用")])],1)]),e._v(" "),r("RightMenu")],1)},[function(){var e=this.$createElement,t=this._self._c||e;return t("h1",{attrs:{id:"开发起步"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#开发起步","aria-hidden":"true"}},[this._v("#")]),this._v(" 开发起步")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"项目结构"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#项目结构","aria-hidden":"true"}},[this._v("#")]),this._v(" 项目结构")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h2",{attrs:{id:"类库使用介绍"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#类库使用介绍","aria-hidden":"true"}},[this._v("#")]),this._v(" 类库使用介绍")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"freesql"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#freesql","aria-hidden":"true"}},[this._v("#")]),this._v(" FreeSql")])},function(){var e=this.$createElement,t=this._self._c||e;return t("h3",{attrs:{id:"identityserver4"}},[t("a",{staticClass:"header-anchor",attrs:{href:"#identityserver4","aria-hidden":"true"}},[this._v("#")]),this._v(" IdentityServer4")])}],!1,null,null,null);t.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/26.c4af03ef.js b/assets/js/26.c4af03ef.js new file mode 100644 index 0000000..c8e1499 --- /dev/null +++ b/assets/js/26.c4af03ef.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{188:function(t,e,s){"use strict";s.r(e);var r=s(0),a=Object(r.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),s("ul",[s("li",[t._v("安装软件开发包 "),s("a",{attrs:{href:"https://dotnet.microsoft.com/download/dotnet-core/2.2",target:"_blank",rel:"noopener noreferrer"}},[t._v(".NET Core SDK 2.2"),s("OutboundLink")],1)]),t._v(" "),s("li",[t._v("安装开发工具 "),s("a",{attrs:{href:"https://visualstudio.microsoft.com/zh-hans/vs/?rr=https%3A%2F%2Fcn.bing.com%2F",target:"_blank",rel:"noopener noreferrer"}},[t._v("Visual Studio 2019"),s("OutboundLink")],1)]),t._v(" "),s("li",[t._v("安装MySQL(version: 5.6+,别装8.0+,未测试)")])]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._m(4),t._v(" "),s("p",[t._v("文件位置src/LinCms.Web/appsettings.json,当数据库中存储表情包是,Charset为utf8mb4")]),t._v(" "),t._m(5),t._v(" "),t._m(6),t._m(7),t._v(" "),s("p",[t._v("该项目使用"),s("a",{attrs:{href:"https://github.com/2881099/FreeSql",target:"_blank",rel:"noopener noreferrer"}},[t._v("FreeSql"),s("OutboundLink")],1),t._v(",默认自动迁移数据表结构,"),s("strong",[t._v("需要自己创建数据库,名字为LinCms")]),t._v(",无须用户操作,但无数据,而且只有访问到表时才会创建某个表,所以用户可将"),s("a",{attrs:{href:"https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/docs/sql/lincms.sql",target:"_blank",rel:"noopener noreferrer"}},[t._v("备份SQL"),s("OutboundLink")],1),t._v("放到Mysql中生成,还原表结构及数据。")]),t._v(" "),t._m(8),t._v(" "),s("p",[t._v("使用vscode打开项目,")]),t._v(" "),t._m(9),t._v(" "),t._m(10),t._m(11),t._v(" "),t._m(12),t._m(13),t._v(" "),t._m(14),s("p",[t._v("这时候打开 https://localhost:5001/swagger/index.html,即可看到swagger页面。")]),t._v(" "),t._m(15),t._v(" "),s("p",[t._v("双击lin-cms-dotnetcore.sln即可使用vs2019打开项目。右键解决方案,点击生成解决方案,然后,再单击LinCms .Web,即可自动启动后台服务。")]),t._v(" "),t._m(16),t._v(" "),s("p",[t._v("会打开浏览器,访问"),s("a",{attrs:{href:"https://localhosst:5001/swagger/index.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://localhosst:5001/swagger/index.html"),s("OutboundLink")],1),t._v(",会看到swagger的文档。")]),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://blog.igeekfan.cn/2019/06/09/dotnetcore/ASP.NET-Core-Deploy-To-Ubuntu/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://blog.igeekfan.cn/2019/06/09/dotnetcore/ASP.NET-Core-Deploy-To-Ubuntu/"),s("OutboundLink")],1)])]),t._v(" "),t._m(19),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://blog.igeekfan.cn/2019/06/09/dotnetcore/ASP.NET-Core-Deploy-To-Docker-Ubuntu/",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://blog.igeekfan.cn/2019/06/09/dotnetcore/ASP.NET-Core-Deploy-To-Docker-Ubuntu/"),s("OutboundLink")],1)])]),t._v(" "),s("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"后端准备"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#后端准备","aria-hidden":"true"}},[this._v("#")]),this._v(" 后端准备")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"server-端必备环境"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#server-端必备环境","aria-hidden":"true"}},[this._v("#")]),this._v(" Server 端必备环境")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"获取工程项目"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#获取工程项目","aria-hidden":"true"}},[this._v("#")]),this._v(" 获取工程项目")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-bash extra-class"},[e("pre",{pre:!0,attrs:{class:"language-bash"}},[e("code",[e("span",{pre:!0,attrs:{class:"token function"}},[this._v("git")]),this._v(" clone https://github.com/luoyunchong/lin-cms-dotnetcore.git\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"数据库配置"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#数据库配置","aria-hidden":"true"}},[this._v("#")]),this._v(" 数据库配置")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("strong",[this._v("请务必根据自己的实际情况修改此配置项")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"language-json extra-class"},[s("pre",{pre:!0,attrs:{class:"language-json"}},[s("code",[s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"ConnectionStrings"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Default"')]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Data Source=127.0.0.1;Port=3306;User ID=root;Password=123456;Initial Catalog=LinCms;Charset=utf8;SslMode=none;Max pool size=10"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"数据迁移"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#数据迁移","aria-hidden":"true"}},[this._v("#")]),this._v(" 数据迁移")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"_1-vscode命令行运行"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1-vscode命令行运行","aria-hidden":"true"}},[this._v("#")]),this._v(" 1.vscode命令行运行")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"切换到lincms-web目录"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#切换到lincms-web目录","aria-hidden":"true"}},[this._v("#")]),this._v(" 切换到LinCms.Web目录")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(" D:\\code\\github\\lin-cms-dotnetcore> cd src\\LinCms.Web\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"编译、安装依赖包"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#编译、安装依赖包","aria-hidden":"true"}},[this._v("#")]),this._v(" 编译、安装依赖包")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("D:\\code\\github\\lin-cms-dotnetcore\\src\\LinCms.Web> dotnet build\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"运行"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#运行","aria-hidden":"true"}},[this._v("#")]),this._v(" 运行")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("D:\\code\\github\\lin-cms-dotnetcore\\src\\LinCms.Web> dotnet run watch\nNow listening on: https://localhost:5001\nNow listening on: http://localhost:5000\nApplication started. Press Ctrl+C to shut down.\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"_2-visual-studio-2019运行"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2-visual-studio-2019运行","aria-hidden":"true"}},[this._v("#")]),this._v(" 2.visual studio 2019运行")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"https://ae01.alicdn.com/kf/H70086026eaca4dc8ab4806ee1d07443bP.jpg",alt:""}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"https://ae01.alicdn.com/kf/He52bc4d3708242d2995419bb584e1f53Q.jpg",alt:""}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"部署至linux-ubuntu16-06"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#部署至linux-ubuntu16-06","aria-hidden":"true"}},[this._v("#")]),this._v(" 部署至linux(Ubuntu16.06)")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"部署至docker"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#部署至docker","aria-hidden":"true"}},[this._v("#")]),this._v(" 部署至Docker")])}],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/27.a3860a2a.js b/assets/js/27.a3860a2a.js new file mode 100644 index 0000000..1209b06 --- /dev/null +++ b/assets/js/27.a3860a2a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[27],{189:function(t,e,r){"use strict";r.r(e);var n=r(0),a=Object(n.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://www.cnblogs.com/RainingNight/p/dynamic-authorization-in-asp-net-core.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://www.cnblogs.com/RainingNight/p/dynamic-authorization-in-asp-net-core.html"),r("OutboundLink")],1)])]),t._v(" "),t._m(2),t._v(" "),r("p",[t._v("IdentityServer4")]),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://blog.csdn.net/jasonsong2008/article/details/89226705",target:"_blank",rel:"noopener noreferrer"}},[t._v("ASP.NET Core WebAPI JWT Bearer 认证失败返回自定义数据 Json"),r("OutboundLink")],1)]),t._v(" "),r("li",[r("a",{attrs:{href:"https://www.cnblogs.com/stulzq/p/8119928.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("IdentityServer4 中文文档与实战"),r("OutboundLink")],1)])]),t._v(" "),t._m(3),t._v(" "),r("p",[t._v("json web token")]),t._v(" "),r("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"认证与授权"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#认证与授权","aria-hidden":"true"}},[this._v("#")]),this._v(" 认证与授权")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("主要就"),e("strong",[this._v("基于权限的授权")]),this._v("的实现进行研究,实现方法级别的权限验证。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"认证鉴权相关"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#认证鉴权相关","aria-hidden":"true"}},[this._v("#")]),this._v(" 认证鉴权相关")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"jwt"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#jwt","aria-hidden":"true"}},[this._v("#")]),this._v(" JWT")])}],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/28.72cdef32.js b/assets/js/28.72cdef32.js new file mode 100644 index 0000000..008aed1 --- /dev/null +++ b/assets/js/28.72cdef32.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[28],{190:function(t,_,v){"use strict";v.r(_);var e=v(0),r=Object(e.a)({},function(){var t=this,_=t.$createElement,v=t._self._c||_;return v("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),v("p",[t._v("0 成功")]),t._v(" "),v("p",[t._v("999 服务器未知错误")]),t._v(" "),v("p",[t._v("9999 失败")]),t._v(" "),v("p",[t._v("10000 认证失败")]),t._v(" "),v("p",[t._v("10020 资源不存在")]),t._v(" "),v("p",[t._v("10030 参数错误")]),t._v(" "),v("p",[t._v("10040 令牌失效")]),t._v(" "),v("p",[t._v("10050 令牌过期")]),t._v(" "),v("p",[t._v("10060 字段重复")]),t._v(" "),v("p",[t._v("10070 禁止操作")]),t._v(" "),v("p",[t._v("10080 请求方法不允许")]),t._v(" "),v("p",[t._v("10100 refresh token 获取失败")]),t._v(" "),v("p",[t._v("10110 文件体积过大")]),t._v(" "),v("p",[t._v("10120 文件数量过多")]),t._v(" "),v("p",[t._v("10130 文件扩展名不符合规范")]),t._v(" "),v("p",[t._v("10140 请求过于频繁,请稍后重试")]),t._v(" "),v("RightMenu")],1)},[function(){var t=this.$createElement,_=this._self._c||t;return _("h1",{attrs:{id:"code"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#code","aria-hidden":"true"}},[this._v("#")]),this._v(" code")])},function(){var t=this.$createElement,_=this._self._c||t;return _("h2",{attrs:{id:"核心库内置已使用状态码"}},[_("a",{staticClass:"header-anchor",attrs:{href:"#核心库内置已使用状态码","aria-hidden":"true"}},[this._v("#")]),this._v(" 核心库内置已使用状态码")])}],!1,null,null,null);_.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/29.ddaee10b.js b/assets/js/29.ddaee10b.js new file mode 100644 index 0000000..ebaa547 --- /dev/null +++ b/assets/js/29.ddaee10b.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{191:function(t,e,i){"use strict";i.r(e);var _=i(0),s=Object(_.a)({},function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),i("p",[t._v("将评论分为二级,第一级采用时间倒序,二级按照时间正序,有助于理解上下文关系。")]),t._v(" "),i("p",[t._v("用户操作:")]),t._v(" "),t._m(2),t._v(" "),i("p",[t._v("运营操作:")]),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),i("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"产品设计-评论模块的设计"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#产品设计-评论模块的设计","aria-hidden":"true"}},[this._v("#")]),this._v(" 产品设计-评论模块的设计")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"主题式"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#主题式","aria-hidden":"true"}},[this._v("#")]),this._v(" 主题式")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("评论随笔(内容支持超链接、emoji)")]),this._v(" "),e("li",[this._v("点赞评论/取消点赞")]),this._v(" "),e("li",[this._v("回复评论")]),this._v(" "),e("li",[this._v("删除自己的评论")])])},function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("ul",[i("li",[t._v("审核通过/拉黑评论")]),t._v(" "),i("li",[t._v("删除任何评论")]),t._v(" "),i("li",[t._v("拉黑后的显示逻辑。(保留当前区块、显示内容为:该评论因违规被拉黑)")]),t._v(" "),i("li",[t._v("删除:(如果是二级评论,直接软删除,如果是一级评论,软删除子评论和当前评论-需要提前提醒用户)\n交互设计")]),t._v(" "),i("li",[t._v("评论的字数长度(500)、emoji。")]),t._v(" "),i("li",[t._v("点赞交互-动画、消息通知/推送")]),t._v(" "),i("li",[t._v("评论区域元素,需要有明确可点击的区域,会跳转到哪个地方。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"优化"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#优化","aria-hidden":"true"}},[this._v("#")]),this._v(" 优化")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("精选评论")])])}],!1,null,null,null);e.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/3.a31eb318.js b/assets/js/3.a31eb318.js new file mode 100644 index 0000000..f381e8a --- /dev/null +++ b/assets/js/3.a31eb318.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[3],{155:function(n,t,e){},162:function(n,t,e){"use strict";var o=e(155);e.n(o).a},167:function(n,t,e){"use strict";e.r(t);var o={functional:!0,props:{text:String},render:function(n,t){var e=t.props,o=t.slots;return n("span",{class:["blue"]},e.text||o().default)}},s=(e(162),e(0)),r=Object(s.a)(o,void 0,void 0,!1,null,"b47fce12",null);t.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/30.f53cb48a.js b/assets/js/30.f53cb48a.js new file mode 100644 index 0000000..934d12c --- /dev/null +++ b/assets/js/30.f53cb48a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[30],{192:function(t,e,i){"use strict";i.r(e);var _=i(0),r=Object(_.a)({},function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7),t._v(" "),t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),i("p",[t._v("分为三种")]),t._v(" "),t._m(14),t._m(15),t._v(" "),t._m(16),t._v(" "),t._m(17),t._m(18),t._v(" "),i("ul",[i("li",[t._v("后端接口 "),i("a",{attrs:{href:"https://github.com/luoyunchong/lin-cms-dotnetcore",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/lin-cms-dotnetcore"),i("OutboundLink")],1)]),t._v(" "),i("li",[t._v("管理后台UI "),i("a",{attrs:{href:"https://github.com/luoyunchong/lin-cms-vue",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/lin-cms-vue"),i("OutboundLink")],1)]),t._v(" "),i("li",[t._v("前端UI"),i("a",{attrs:{href:"https://github.com/luoyunchong/lin-cms-vvlog",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/lin-cms-vvlog"),i("OutboundLink")],1)])]),t._v(" "),i("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"功能模块的设计"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#功能模块的设计","aria-hidden":"true"}},[this._v("#")]),this._v(" 功能模块的设计")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"基础权限模块"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#基础权限模块","aria-hidden":"true"}},[this._v("#")]),this._v(" 基础权限模块")])},function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("ul",[i("li",[t._v("用户信息:邮件、用户名(唯一)、昵称、头像、分组、是否激活、手机号、是否是Admin、个性签名\n"),i("ul",[i("li",[t._v("[x] 注册/登录")]),t._v(" "),i("li",[t._v("[x] 上传头像")]),t._v(" "),i("li",[t._v("[x] 修改密码")]),t._v(" "),i("li",[t._v("用户基本信息修改")]),t._v(" "),i("li",[t._v("[x] 配置分组")]),t._v(" "),i("li",[t._v("第三方账号绑定/登录")])])]),t._v(" "),i("li",[t._v("绑定信息:功能(QQ快速登录,GitHub快速登录)。")]),t._v(" "),i("li",[t._v("分组信息:是否静态分组(无法删除,无法修改分组编码)、名称可以修改\n"),i("ul",[i("li",[t._v("[x] 分组增删改")]),t._v(" "),i("li",[t._v("[x] 组别配置权限")])])]),t._v(" "),i("li",[t._v("文件管理\n"),i("ul",[i("li",[t._v("[x] 本地文件上传")]),t._v(" "),i("li",[t._v("七牛云存储")]),t._v(" "),i("li",[t._v("文件去重,秒传")])])]),t._v(" "),i("li",[t._v("[x] 系统日志:请求方法、路径、http返回码、时间、用户昵称、用户id、访问哪个权限、 日志信息\n"),i("ul",[i("li",[t._v("记录系统请求的日志")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"cms-管理员维护模块"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#cms-管理员维护模块","aria-hidden":"true"}},[this._v("#")]),this._v(" cms 管理员维护模块")])},function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("ul",[i("li",[t._v("[x] 标签管理:名称、图片,是否启用/禁用,排序、文章数量、用户关注数量。\n"),i("ul",[i("li",[t._v("[x] 标签增删改")]),t._v(" "),i("li",[t._v("[x] 标签列表,禁用")])])]),t._v(" "),i("li",[t._v("随笔管理:\n"),i("ul",[i("li",[t._v("审核随笔/拉黑")])])]),t._v(" "),i("li",[t._v("评论管理\n"),i("ul",[i("li",[t._v("后台审核通过/拉黑")]),t._v(" "),i("li",[t._v("管理员删除评论")])])]),t._v(" "),i("li",[t._v("字典管理\n"),i("ul",[i("li",[t._v("[x] 字典类别管理")]),t._v(" "),i("li",[t._v("[x] 字典管理:如随笔类型(原创、转载、翻译)")])])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"cms-用户端模块"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#cms-用户端模块","aria-hidden":"true"}},[this._v("#")]),this._v(" cms 用户端模块")])},function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("ul",[i("li",[t._v("分类专栏管理:发布随笔时可选择单个分类。\n"),i("ul",[i("li",[t._v("[x] 分类增删改(随笔数量、图片、名称、排序)")]),t._v(" "),i("li",[t._v("[x] 分类列表,仅查看、编辑自己创建的分类专栏")])])]),t._v(" "),i("li",[t._v("标签:统计每个标签下多少个文章、多少人关注\n"),i("ul",[i("li",[t._v("[x] 标签列表")]),t._v(" "),i("li",[t._v("[x] 无限加载")]),t._v(" "),i("li",[t._v("[x] 最新/最热 根据名称模糊查询")]),t._v(" "),i("li",[t._v("我关注的标签")])])]),t._v(" "),i("li",[t._v("随笔\n"),i("ul",[i("li",[t._v("[x] 支持markdown,增删改(仅自己的随笔),修正分类专栏中的随笔数量")]),t._v(" "),i("li",[t._v("[x] 列表无限加载,按标签查询随笔")]),t._v(" "),i("li",[t._v("[x] 点赞随笔")]),t._v(" "),i("li",[t._v("随笔详情页\n"),i("ul",[i("li",[t._v("[x] 支持目录导航(滚动时,固定至顶部位置),展示字数统计、预计阅读时长;")]),t._v(" "),i("li",[t._v("[x] 作者介绍:头像,昵称,签名,随笔数;")]),t._v(" "),i("li",[t._v("[x] 展示文章类型:原创、转载、翻译")]),t._v(" "),i("li",[t._v("相关文章")]),t._v(" "),i("li",[t._v("推荐文章")])])])])]),t._v(" "),i("li",[t._v("评论\n"),i("ul",[i("li",[t._v("评论随笔(内容支持超链接、emoji)")]),t._v(" "),i("li",[t._v("[x] 删除自己的评论")]),t._v(" "),i("li",[t._v("[x] 点赞评论")]),t._v(" "),i("li",[t._v("[x] 回复评论")])])]),t._v(" "),i("li",[t._v("关注\n"),i("ul",[i("li",[t._v("关注/取消关注用户")]),t._v(" "),i("li",[t._v("关注/取消关注标签")]),t._v(" "),i("li")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"个人主页"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#个人主页","aria-hidden":"true"}},[this._v("#")]),this._v(" 个人主页")])},function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("ul",[i("li",[t._v("用户专栏分类展示")]),t._v(" "),i("li",[t._v("最新发布、最热的随笔")]),t._v(" "),i("li",[t._v("关注的人/标签")]),t._v(" "),i("li",[t._v("粉丝")]),t._v(" "),i("li",[t._v("个人主页详细页:设置个人资料,绑定邮件,")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"更多"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#更多","aria-hidden":"true"}},[this._v("#")]),this._v(" 更多")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("消息通知:如点赞随笔,评论随笔、点赞评论、回复评论后的消息通知。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"脑图分享"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#脑图分享","aria-hidden":"true"}},[this._v("#")]),this._v(" 脑图分享")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[e("img",{attrs:{src:"https://camo.githubusercontent.com/49d4fca8ccfef50fb7a2f3106e6be05c4a82b887/68747470733a2f2f706963322e73757065726265642e636e2f6974656d2f3564643366363838386530653265336565393330663435382e706e67",alt:"image"}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"分组"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#分组","aria-hidden":"true"}},[this._v("#")]),this._v(" 分组")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("id name info\n1\tAdmin\t 系统管理员\n2\tCmsAdmin\t内容管理员\n3\tUser\t 普通用户\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"审计日志"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#审计日志","aria-hidden":"true"}},[this._v("#")]),this._v(" 审计日志")])},function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("p",[t._v("大多数表存在如下8个字段,用于记录行的变化状态,is_deleted为软删除,执行删除操作时,将其状态置为true,默认实体类继承 "),i("strong",[t._v("FullAduitEntity")]),t._v(" 即可拥有以下8个字段。该设计参考ABP中的实现。FullAduitEntity为泛型,默认id为long类型,FullAduitEntity,即可改变主键类型,默认LinUser表主键long,保持"),i("strong",[t._v("create_user_id")]),t._v(","),i("strong",[t._v("delete_user_id")]),t._v(","),i("strong",[t._v("update_user_id")]),t._v("都与LinUser的主键相同")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("\nid\t bigint\ncreate_user_id \tbigint\ncreate_time\t datetime\nis_deleted\t bit\ndelete_user_id \tbigint\ndelete_time\t datetime\nupdate_user_id\t bigint\nupdate_time\t datetime\n\n\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"lin-cms-开源地址分享"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#lin-cms-开源地址分享","aria-hidden":"true"}},[this._v("#")]),this._v(" lin-cms 开源地址分享")])}],!1,null,null,null);e.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/31.7b7b19ea.js b/assets/js/31.7b7b19ea.js new file mode 100644 index 0000000..5e15040 --- /dev/null +++ b/assets/js/31.7b7b19ea.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[31],{193:function(t,e,n){"use strict";n.r(e);var i=n(0),s=Object(i.a)({},function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"content"},[t._m(0),t._v(" "),n("p",[t._v("使用SPA+.NET Core3.1实现 GitHub第三方授权登录 类似使用AspNet.Security.OAuth.GitHub,前端使用如下:VUE+Vue-Router+axios")]),t._v(" "),t._m(1),t._v(" "),n("ul",[n("li",[t._v("GitHub "),n("a",{attrs:{href:"https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers"),n("OutboundLink")],1)])]),t._v(" "),t._m(2),t._v(" "),n("p",[t._v("什么配置的过程不说了。。有一推。")]),t._v(" "),n("ul",[n("li",[n("a",{attrs:{href:"https://www.jianshu.com/p/78d186aeb526",target:"_blank",rel:"noopener noreferrer"}},[t._v("GitHub 第三方登录"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://juejin.im/post/5dfb04cee51d45583a66c2f3",target:"_blank",rel:"noopener noreferrer"}},[t._v("给你的网站添加第三方登录以及短信验证功能"),n("OutboundLink")],1)])]),t._v(" "),n("p",[t._v("下面为示例")]),t._v(" "),t._m(3),n("p",[t._v("Get")]),t._v(" "),t._m(4),n("p",[t._v("会重定向到")]),t._v(" "),n("p",[n("a",{attrs:{href:"https://localhost:5001/signin-github?code=07537a84d12bbae08361",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://localhost:5001/signin-github?code=07537a84d12bbae08361"),n("OutboundLink")],1)]),t._v(" "),n("p",[t._v("这个code放到下面的请求中,获取access_token\nPOST方式(PostMan去请求)")]),t._v(" "),t._m(5),n("p",[t._v("Get方式")]),t._v(" "),t._m(6),n("p",[t._v("然后就能获取用户信息")]),t._v(" "),t._m(7),t._m(8),t._v(" "),n("p",[t._v("以下代码为主要代码,完整代码看下面的DEMO链接。")]),t._v(" "),n("p",[t._v("使用WebApi时,看了一些项目,全是基于MVC结构的,都不是我想要的。看了一些博客上面介绍 ,步骤都是千篇一律,都是配合前后端分离的。")]),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),n("p",[t._v("本地测试时,gitHub回调地址设置 http(s)😕/ip:端口/signin-github")]),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15),t._v(" "),n("p",[n("a",{attrs:{href:"https://docs.microsoft.com/zh-cn/aspnet/core/security/authentication/social/google-logins?view=aspnetcore-3.1",target:"_blank",rel:"noopener noreferrer"}},[t._v("google-登录,微软文档"),n("OutboundLink")],1),t._v(",其中有一个"),n("strong",[t._v("更改默认回调 URI")]),t._v(",通过 AddGitHub中的CallbackPath属性配置。")]),t._v(" "),n("p",[t._v("介绍了回调地址应配置signin-google,所以这里应该是signin-github,他是可以配置的,不需要自己写程序处理signin-google这个路由,内部有中间件已经处理了。")]),t._v(" "),t._m(16),t._v(" "),n("p",[t._v("具体上面的根据code获取access_token,根据access_token获取用户的信息的过程,这些处理的过程,都不需要我们自己处理。我们可以用直接获取用户信息。")]),t._v(" "),t._m(17),t._v(" "),t._m(18),t._v(" "),n("p",[t._v("这个url为另一个获取用户信息的路由,只要拼接好地址即可。")]),t._v(" "),t._m(19),n("p",[t._v("需要注入")]),t._v(" "),t._m(20),t._m(21),t._v(" "),n("p",[t._v("打开NuGet包管理,安装包")]),t._v(" "),t._m(22),n("p",[t._v("appSettings.json")]),t._v(" "),t._m(23),n("p",[t._v("add扩展方法")]),t._v(" "),t._m(24),n("p",[t._v("默认情况下,如头像,email,是没有获取的。")]),t._v(" "),t._m(25),n("p",[t._v("startup.cs")]),t._v(" "),n("p",[t._v("ConfigureServices中配置此服务")]),t._v(" "),t._m(26),n("p",[t._v("创建AuthenticationController.cs\n增加SignIn,用于处理用户授权成功后,重定回signin-callback,并将参数带回。")]),t._v(" "),t._m(27),n("p",[t._v("在signin方法中,用户点击授权后(第一次),会根据其传递的URL,重定向到这个地址,signin-callback,参数也会一同携带。provider为GitHub,redirectUrl为:http://localhost:8081/login-result.")]),t._v(" "),t._m(28),n("p",[t._v("这时候我们能获取用户信息了。那么前端怎么办呢。我们写个方法,获取用户信息,看看效果。")]),t._v(" "),t._m(29),t._v(" "),t._m(30),n("p",[t._v("我记得之前传Token时,后台是可以这样获取的。")]),t._v(" "),t._m(31),n("p",[t._v("LoginResult.vue在created生命周期中。都是得到null")]),t._v(" "),t._m(32),t._m(33),t._v(" "),n("p",[t._v("因为前后端分离,不是基于Cookies的。http是无状态的。每次请求无法区分用户的。我们可以根据当前的ClaimsPrincipal,根据JWT生成相应的Token,axios请求时,放到headers中。")]),t._v(" "),n("p",[t._v("安装包")]),t._v(" "),t._m(34),n("p",[t._v("AppSettings.json配置改成")]),t._v(" "),t._m(35),n("p",[t._v("AddJwtConfiguration改成如下内容")]),t._v(" "),t._m(36),n("p",[n("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/blob/master/dotnetcore3.1/VoVo.AspNetCore.OAuth2/spa-vue-oauth2/src/components/LoginResult.vue",target:"_blank",rel:"noopener noreferrer"}},[t._v("前端LoginResult.vue代码"),n("OutboundLink")],1)]),t._v(" "),n("p",[t._v("前端运行")]),t._v(" "),t._m(37),n("p",[t._v("点击GitHub登录")]),t._v(" "),n("p",[t._v("GetOpenIdByToken根据生成的token值,解析出了用户id,这样前端在login-result这个组件中,把token保存好,并重定向自己的主页,获取用户所有信息即可。")]),t._v(" "),t._m(38),n("p",[t._v("OpenId?provider=GitHub则得不到数据,只能浏览器直接请求https://localhost:5001/OpenId?provider=GitHub,才能到github 的id。这个适应于前后端不分离,或者属于之前我们经常使用MVC结构,同一域名下,同一端口,基于Cookies登录的判断。")]),t._v(" "),t._m(39),t._v(" "),n("ul",[n("li",[n("a",{attrs:{href:"https://www.cnblogs.com/rsls/p/10522649.html",target:"_blank",rel:"noopener noreferrer"}},[t._v(".net Core2.2 WebApi通过OAuth2.0实现微信登录"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"https://blog.csdn.net/weixin_30414305/article/details/101389325",target:"_blank",rel:"noopener noreferrer"}},[t._v("AspNetCore3.0 和 JWT"),n("OutboundLink")],1)]),t._v(" "),n("li",[n("a",{attrs:{href:"http://www.woshipm.com/pd/509712.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("用户系统设计:第三方授权、账号绑定及解绑(下)"),n("OutboundLink")],1)])]),t._v(" "),t._m(40),t._v(" "),n("ul",[n("li",[t._v("GitHub "),n("a",{attrs:{href:"https://github.com/luoyunchong/dotnetcore-examples/tree/master/dotnetcore3.1/VoVo.AspNetCore.OAuth2",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/dotnetcore-examples/tree/master/dotnetcore3.1/VoVo.AspNetCore.OAuth2"),n("OutboundLink")],1),t._v(" "),n("RightMenu")],1)])])},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"github第三方授权登录"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#github第三方授权登录","aria-hidden":"true"}},[this._v("#")]),this._v(" GitHub第三方授权登录")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"aspnet-security-oauth-github"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#aspnet-security-oauth-github","aria-hidden":"true"}},[this._v("#")]),this._v(" AspNet.Security.OAuth.GitHub")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"github授权登录"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#github授权登录","aria-hidden":"true"}},[this._v("#")]),this._v(" GitHub授权登录")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("client_id:0be6b05fc717bfc4fb67\nclient_secret:dcaced9f176afba64e89d88b9b06ffc4a887a609\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("https://github.com/login/oauth/authorize?client_id=0be6b05fc717bfc4fb67&redirect_uri=https://localhost:5001/signin-github\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("https://github.com/login/oauth/access_token?client_id=0be6b05fc717bfc4fb67&client_secret=dcaced9f176afba64e89d88b9b06ffc4a887a609&code=07537a84d12bbae08361\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("https://api.github.com/user?access_token=787506afa3271d077b98f18af56d7cfdc8db43b4\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('{\n "login": "luoyunchong",\n "id": 18613266,\n "node_id": "MDQ6VXNlcjE4NjEzMjY2",\n "avatar_url": "https://avatars1.githubusercontent.com/u/18613266?v=4",\n "gravatar_id": "",\n "url": "https://api.github.com/users/luoyunchong",\n "html_url": "https://github.com/luoyunchong",\n "followers_url": "https://api.github.com/users/luoyunchong/followers",\n "following_url": "https://api.github.com/users/luoyunchong/following%7B/other_user%7D",\n "gists_url": "https://api.github.com/users/luoyunchong/gists%7B/gist_id%7D",\n "starred_url": "https://api.github.com/users/luoyunchong/starred%7B/owner%7D%7B/repo%7D",\n "subscriptions_url": "https://api.github.com/users/luoyunchong/subscriptions",\n "organizations_url": "https://api.github.com/users/luoyunchong/orgs",\n "repos_url": "https://api.github.com/users/luoyunchong/repos",\n "events_url": "https://api.github.com/users/luoyunchong/events%7B/privacy%7D",\n "received_events_url": "https://api.github.com/users/luoyunchong/received_events",\n "type": "User",\n "site_admin": false,\n "name": "IGeekFan",\n "company": null,\n "blog": "https://blog.igeekfan.cn/",\n "location": null,\n "email": "luoyunchong@foxmail.com",\n "hireable": null,\n "bio": "学习之路漫漫无期。",\n "public_repos": 14,\n "public_gists": 0,\n "followers": 16,\n "following": 11,\n "created_at": "2016-04-22T10:33:44Z",\n "updated_at": "2019-12-21T14:49:33Z"\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"net-core3-1"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#net-core3-1","aria-hidden":"true"}},[this._v("#")]),this._v(" .NET Core3.1")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("前端运行在:http://localhost:8081")]),this._v(" "),e("li",[this._v("后端运行在:https://localhost:5001")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"前后端分离的spa-配合第三方授权登录流程如下"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#前后端分离的spa-配合第三方授权登录流程如下","aria-hidden":"true"}},[this._v("#")]),this._v(" 前后端分离的SPA 配合第三方授权登录流程如下")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("如: https://localhost:5001/signin-github。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"_1-上面这个明明填写的后端的地址,那后端怎么把结果通知前端呢?"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_1-上面这个明明填写的后端的地址,那后端怎么把结果通知前端呢?","aria-hidden":"true"}},[this._v("#")]),this._v(" 1. 上面这个明明填写的后端的地址,那后端怎么把结果通知前端呢?")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("前端请求"),e("strong",[this._v("https://localhost:5001/signin?provider=GitHub&redirectUrl=http://localhost:8080/login-result")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("提供参数provider为GitHub,")]),this._v(" "),e("li",[this._v("redirectUrl为GitHub授权登录后,回调signin-github后,后端再去重定向的地址,这里填前端的一个路由。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"_2-后端只提供了signin,signin-callback路由,没有signin-github,那github上配置的路由是怎么回调回来呢?"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_2-后端只提供了signin,signin-callback路由,没有signin-github,那github上配置的路由是怎么回调回来呢?","aria-hidden":"true"}},[this._v("#")]),this._v(" 2. 后端只提供了signin,signin-callback路由,没有signin-github,那github上配置的路由是怎么回调回来呢?")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h4",{attrs:{id:"_3-回调到signin-github后,后端怎么处理,才能让前端刷新。获取登录后的信息呢。"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#_3-回调到signin-github后,后端怎么处理,才能让前端刷新。获取登录后的信息呢。","aria-hidden":"true"}},[this._v("#")]),this._v(" 3. 回调到signin-github后,后端怎么处理,才能让前端刷新。获取登录后的信息呢。")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("一个方法SignIn,只要"),e("strong",[this._v("return Challenge(properties, provider);")]),this._v(",")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("provider 为 GitHub,")]),this._v(" "),e("li",[this._v("properties var properties = new AuthenticationProperties { RedirectUri = url };")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("var authenticateResult = await _contextAccessor.HttpContext.AuthenticateAsync(provider);\nstring email = authenticateResult.Principal.FindFirst(ClaimTypes.Email)?.Value;\nstring name = authenticateResult.Principal.FindFirst(ClaimTypes.Name)?.Value;\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("private readonly IHttpContextAccessor _contextAccessor;\npublic AuthenticationController( IHttpContextAccessor contextAccessor)\n{\n _contextAccessor = contextAccessor;\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"代码部署(简化)"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#代码部署(简化)","aria-hidden":"true"}},[this._v("#")]),this._v(" 代码部署(简化)")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("Install-Package AspNet.Security.OAuth.GitHub\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('"Authentication": {\n "GitHub": {\n "ClientId": "0be6b05fc717bfc4fb67",\n "ClientSecret": "dcaced9f176afba64e89d88b9b06ffc4a887a609"\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('public static class JwtConfiguration\n{\n public static void AddJwtConfiguration(this IServiceCollection services, IConfiguration configuration)\n {\n\n services.AddAuthentication(opts =>\n {\n opts.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;\n opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;\n }).AddCookie(options =>\n {\n options.LoginPath = "/signin";\n options.LogoutPath = "/signout";\n }).AddGitHub(options =>\n {\n options.ClientId = configuration["Authentication:GitHub:ClientId"];\n options.ClientSecret = configuration["Authentication:GitHub:ClientSecret"];\n });\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('.AddGitHub(options =>\n{\n options.ClientId = configuration["Authentication:GitHub:ClientId"];\n options.ClientSecret = configuration["Authentication:GitHub:ClientSecret"];\n //options.CallbackPath = new PathString("~/signin-github");//与GitHub上的回调地址相同,默认即是/signin-github\n options.Scope.Add("user:email");\n //authenticateResult.Principal.FindFirst(LinConsts.Claims.AvatarUrl)?.Value; 得到GitHub头像\n options.ClaimActions.MapJsonKey(LinConsts.Claims.AvatarUrl, "avatar_url");\n options.ClaimActions.MapJsonKey(LinConsts.Claims.BIO, "bio");\n options.ClaimActions.MapJsonKey(LinConsts.Claims.BlogAddress, "blog");\n});\n\n#其中LinConsts类为静态常量\npublic static class LinConsts\n{\n public static class Claims\n {\n public const string BIO = "urn:github:bio";\n public const string AvatarUrl = "urn:github:avatar_url";\n public const string BlogAddress = "urn:github:blog";\n }\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(" services.AddSingleton();\n services.AddJwtConfiguration(Configuration);\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(' private readonly IHttpContextAccessor _contextAccessor;\n private readonly IConfiguration _configuration;\n\n public AuthenticationController(IHttpContextAccessor contextAccessor, IConfiguration configuration)\n {\n _contextAccessor = contextAccessor;\n _configuration = configuration;\n }\n \n [HttpGet("~/signin")]\n public async Task SignIn(string provider, string redirectUrl)\n {\n var request = _contextAccessor.HttpContext.Request;\n var url =\n $"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}-callback?provider={provider}&redirectUrl={redirectUrl}";\n var properties = new AuthenticationProperties { RedirectUri = url };\n properties.Items["LoginProviderKey"] = provider;\n return Challenge(properties, provider);\n\n }\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[HttpGet("~/signin-callback")]\npublic async Task Home(string provider = null, string redirectUrl = "")\n{\n var authenticateResult = await _contextAccessor.HttpContext.AuthenticateAsync(provider);\n if (!authenticateResult.Succeeded) return Redirect(redirectUrl);\n var openIdClaim = authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier);\n if (openIdClaim == null || string.IsNullOrWhiteSpace(openIdClaim.Value))\n return Redirect(redirectUrl);\n\n //TODO 记录授权成功后的信息 \n\n string email = authenticateResult.Principal.FindFirst(ClaimTypes.Email)?.Value;\n string name = authenticateResult.Principal.FindFirst(ClaimTypes.Name)?.Value;\n string gitHubName = authenticateResult.Principal.FindFirst(GitHubAuthenticationConstants.Claims.Name)?.Value;\n string gitHubUrl = authenticateResult.Principal.FindFirst(GitHubAuthenticationConstants.Claims.Url)?.Value;\n //startup 中 AddGitHub配置项 options.ClaimActions.MapJsonKey(LinConsts.Claims.AvatarUrl, "avatar_url");\n string avatarUrl = authenticateResult.Principal.FindFirst(LinConsts.Claims.AvatarUrl)?.Value;\n\n return Redirect($"{redirectUrl}?openId={openIdClaim.Value}");\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("浏览器直接打开能得到github的id。")]),this._v(" "),e("li",[this._v("axios GET请求 https://localhost:5001/OpenId 得到null")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[HttpGet("~/OpenId")]\npublic async Task OpenId(string provider = null)\n{\n var authenticateResult = await _contextAccessor.HttpContext.AuthenticateAsync(provider);\n if (!authenticateResult.Succeeded) return null;\n var openIdClaim = authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier);\n return openIdClaim?.Value;\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('[HttpGet("~/GetOpenIdByToken")]\npublic string GetOpenIdByToken()\n{\n return User.FindFirst(ClaimTypes.NameIdentifier)?.Value;\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('axios({\n methods: "get",\n url: "https://localhost:5001/OpenId?provider=GitHub"\n})\n .then(function(response) {\n // handle success\n console.log(response);\n })\n\naxios({\n methods: "get",\n url: "https://localhost:5001/GetOpenIdByToken"\n})\n .then(function(response) {\n // handle success\n console.log(response);\n })\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"为什么呢???"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#为什么呢???","aria-hidden":"true"}},[this._v("#")]),this._v(" 为什么呢???")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("Install-Package Microsoft.AspNetCore.Authentication.JwtBearer\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('"Authentication": {\n"JwtBearer": {\n "SecurityKey": "JWTStudyWebsite_DI20DXU3",\n "Issuer": "JWTStudy",\n "Audience": "JWTStudyWebsite"\n},\n"GitHub": {\n "ClientId": "0be6b05fc717bfc4fb67",\n "ClientSecret": "dcaced9f176afba64e89d88b9b06ffc4a887a609"\n}\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('public static void AddJwtConfiguration(this IServiceCollection services, IConfiguration configuration)\n{\n\n services.AddAuthentication(opts =>\n {\n opts.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;\n opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;\n }).AddCookie(options =>\n {\n options.LoginPath = "/signin";\n options.LogoutPath = "/signout";\n }).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>\n {\n options.Audience = configuration["Authentication:JwtBearer:Audience"];\n\n options.TokenValidationParameters = new TokenValidationParameters\n {\n // The signing key must match!\n ValidateIssuerSigningKey = true,\n IssuerSigningKey = new SymmetricSecurityKey(\n Encoding.ASCII.GetBytes(configuration["Authentication:JwtBearer:SecurityKey"])),\n\n // Validate the JWT Issuer (iss) claim\n ValidateIssuer = true,\n ValidIssuer = configuration["Authentication:JwtBearer:Issuer"],\n\n // Validate the JWT Audience (aud) claim\n ValidateAudience = true,\n ValidAudience = configuration["Authentication:JwtBearer:Audience"],\n\n // Validate the token expiry\n ValidateLifetime = true,\n\n // If you want to allow a certain amount of clock drift, set that here\n //ClockSkew = TimeSpan.Zero\n };\n }).AddGitHub(options =>\n {\n options.ClientId = configuration["Authentication:GitHub:ClientId"];\n options.ClientSecret = configuration["Authentication:GitHub:ClientSecret"];\n //options.CallbackPath = new PathString("~/signin-github");//与GitHub上的回调地址相同,默认即是/signin-github\n options.Scope.Add("user:email");\n //authenticateResult.Principal.FindFirst(LinConsts.Claims.AvatarUrl)?.Value; 得到GitHub头像\n options.ClaimActions.MapJsonKey(LinConsts.Claims.AvatarUrl, "avatar_url");\n options.ClaimActions.MapJsonKey(LinConsts.Claims.BIO, "bio");\n options.ClaimActions.MapJsonKey(LinConsts.Claims.BlogAddress, "blog");\n });\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("yarn install\nyarn serve\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('data: 18613266\nstatus: 200\nconfig: {url: "https://localhost:5001/GetOpenIdByToken"}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"参考"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#参考","aria-hidden":"true"}},[this._v("#")]),this._v(" 参考")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"demo-示例"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#demo-示例","aria-hidden":"true"}},[this._v("#")]),this._v(" Demo 示例")])}],!1,null,null,null);e.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/32.c3b644c7.js b/assets/js/32.c3b644c7.js new file mode 100644 index 0000000..18bea38 --- /dev/null +++ b/assets/js/32.c3b644c7.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[32],{194:function(t,e,r){"use strict";r.r(e);var n=r(0),s=Object(n.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://github.com/luoyunchong/lin-cms-vue",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/lin-cms-vue"),r("OutboundLink")],1)])]),t._v(" "),t._m(2),t._v(" "),r("ul",[r("li",[t._v("介绍 "),r("a",{attrs:{href:"http://doc.cms.7yue.pro/",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://doc.cms.7yue.pro/"),r("OutboundLink")],1)]),t._v(" "),r("li",[t._v("前端 "),r("a",{attrs:{href:"http://doc.cms.7yue.pro/lin/client/s",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://doc.cms.7yue.pro/lin/client/"),r("OutboundLink")],1)])]),t._v(" "),t._m(3),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"http://face.cms.7yue.pro/",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://face.cms.7yue.pro/"),r("OutboundLink")],1)])]),t._v(" "),t._m(4),t._v(" "),r("ul",[r("li",[r("p",[t._v("后端swagger地址 "),r("a",{attrs:{href:"http://47.106.80.39:7000/swagger/index.html",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://47.106.80.39:7000/swagger/index.html"),r("OutboundLink")],1)])]),t._v(" "),r("li",[r("p",[t._v("用户端 lin-cms-vvlog "),r("a",{attrs:{href:"http://47.106.80.39:8080/index",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://47.106.80.39:8080/index"),r("OutboundLink")],1)]),t._v(" "),t._m(5)]),t._v(" "),r("li",[r("p",[t._v("管理员 lin-cms-vue "),r("a",{attrs:{href:"http://47.106.80.39:8081/#/",target:"_blank",rel:"noopener noreferrer"}},[t._v("http://47.106.80.39:8081/#/"),r("OutboundLink")],1)]),t._v(" "),t._m(6)])]),t._v(" "),t._m(7),t._v(" "),r("p",[t._v("开发必备")]),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://nodejs.org/en/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Node.js 10+"),r("OutboundLink")],1),t._v(" 版本即可,我须安装12.7")]),t._v(" "),r("li",[r("a",{attrs:{href:"https://yarnpkg.com/zh-Hant/docs/install#windows-stable",target:"_blank",rel:"noopener noreferrer"}},[t._v("yarn"),r("OutboundLink")],1)])]),t._v(" "),r("p",[t._v("如果以下命令有问题,请删除yarn.lock,node_modules文件夹后,重新执行yarn,yarn serve")]),t._v(" "),t._m(8),t._m(9),t._v(" "),r("p",[t._v("scp2方便快速发布,一行命令就能快速发布成功。")]),t._v(" "),r("p",[t._v("必备条件:(参数)")]),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),r("p",[t._v("根目录新建deploy目录,创建index.js文件。")]),t._v(" "),t._m(12),r("p",[t._v("快速发布,需要安装 scp2")]),t._v(" "),t._m(13),r("p",[t._v("package.json中增加")]),t._v(" "),t._m(14),t._m(15),t._m(16),t._v(" "),t._m(17),t._v(" "),t._m(18),t._m(19),t._v(" "),t._m(20),t._v(" "),r("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"前端准备"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#前端准备","aria-hidden":"true"}},[this._v("#")]),this._v(" 前端准备")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"开源地址"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#开源地址","aria-hidden":"true"}},[this._v("#")]),this._v(" 开源地址")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"文档地址"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#文档地址","aria-hidden":"true"}},[this._v("#")]),this._v(" 文档地址")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"官方预览地址"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#官方预览地址","aria-hidden":"true"}},[this._v("#")]),this._v(" 官方预览地址")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"线上-demo"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#线上-demo","aria-hidden":"true"}},[this._v("#")]),this._v(" 线上 Demo")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("普通用户:710277267@qq.com")]),this._v(" "),e("li",[this._v("密码:123qwe")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("管理员: admin")]),this._v(" "),e("li",[this._v("密码:123qwe")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"快速上手"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#快速上手","aria-hidden":"true"}},[this._v("#")]),this._v(" 快速上手")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("# clone the project\ngit clone https://github.com/luoyunchong/lin-cms-vue.git\n\n# install dependency\nyarn\n\n# develop\nyarn serve\n\n# deploy\nyarn deploy\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"deploy-发布"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#deploy-发布","aria-hidden":"true"}},[this._v("#")]),this._v(" deploy 发布")])},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("ul",[r("li",[t._v("一台linux的服务器,ip")]),t._v(" "),r("li",[t._v("用户名")]),t._v(" "),r("li",[t._v("密码")]),t._v(" "),r("li",[t._v("端口:默认是22")]),t._v(" "),r("li",[t._v("发布的地址。这里放到/var/www/lin-cms-vue目录中。")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"步骤"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#步骤","aria-hidden":"true"}},[this._v("#")]),this._v(" 步骤")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v('\'use strict\'\n// 引入scp2模块\nvar client = require(\'scp2\');\nconst ora = require(\'ora\')\nconst chalk = require(\'chalk\')\nconst spinner = ora(\'正在发布到生产服务器...\')\nspinner.start()\nclient.scp(\'dist/\', {\n "host": "",\n "username": "",\n "password": "",\n "port": "22",\n "path": "/var/www/lin-cms-vue"\n}, function (err) {\n spinner.stop()\n if (!err) {\n console.log("npm run build-scp2: scp2工具上传完毕,远端服务路径:/var/www/lin-cms-vue")\n } else {\n console.log("err", err)\n }\n})\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("cnpm install scp2\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(' "scripts": {\n "deploy": "yarn build:production && node ./deploy",\n}\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("yarn deploy\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"nginx-配置"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#nginx-配置","aria-hidden":"true"}},[this._v("#")]),this._v(" nginx 配置")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("vue 使用history的配置")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("server {\n listen 8080;\n root /var/www/lin-cms-vue;\n\n charset utf-8;\n location / {\n try_files $uri $uri/ /index.html; \n\n }\n \n error_page 500 502 503 504 /50x.html;\n location = /50x.html {\n root html;\n }\n\n}\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"配置项"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#配置项","aria-hidden":"true"}},[this._v("#")]),this._v(" 配置项")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ol",[e("li",[e("p",[this._v("配置 api 地址: 打开配置文件 src/config/index.js 配置 baseUrl ,本地开发阶段配置本地虚拟域名(https://localhost:5001/),线上部署生产域名。")])]),this._v(" "),e("li",[e("p",[this._v("用户名:"),e("strong",[this._v("super")]),this._v(" 密码 "),e("strong",[this._v("123456")])])])])}],!1,null,null,null);e.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/33.6564e7fd.js b/assets/js/33.6564e7fd.js new file mode 100644 index 0000000..6cd6456 --- /dev/null +++ b/assets/js/33.6564e7fd.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[33],{166:function(t,e,s){"use strict";s.r(e);var r=s(0),a=Object(r.a)({},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"content"},[t._m(0),t._v(" "),s("p",[t._v("项目使用vuepress,其可专注于文档构建")]),t._v(" "),s("ul",[s("li",[t._v("该项目的文档是基于此项目 "),s("a",{attrs:{href:"https://github.com/vanoneang/blog",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/vanoneang/blog"),s("OutboundLink")],1)])]),t._v(" "),t._m(1),t._v(" "),s("p",[s("a",{attrs:{href:"https://github.com/luoyunchong/vuepress-docs",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/luoyunchong/vuepress-docs"),s("OutboundLink")],1)]),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://github.com/vuejs/vuepress",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/vuejs/vuepress"),s("OutboundLink")],1)])]),t._v(" "),t._m(4),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://luoyunchong.github.io/vuepress-docs",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://luoyunchong.github.io/vuepress-docs/"),s("OutboundLink")],1)])]),t._v(" "),t._m(5),t._v(" "),s("p",[t._v("Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套内容管理系统框架,前后端分离的完整解决方案。")]),t._v(" "),s("ul",[s("li",[s("a",{attrs:{href:"https://github.com/TaleLin/lin-cms-vue",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/TaleLin/lin-cms-vue"),s("OutboundLink")],1)])]),t._v(" "),s("p",[t._v("与本项目的关系:本项目和Lin-CMS的文档的样式效果是一样的,所以文档构建中包含“林间有风团队” 出品,意思是指UI主题来自其团队。")]),t._v(" "),t._m(6),t._v(" "),s("p",[t._v("前提")]),t._v(" "),t._m(7),t._v(" "),s("p",[t._v("最好是安装yarn "),s("a",{attrs:{href:"https://yarnpkg.com/lang/zh-hans/docs/install/#windows-stable",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://yarnpkg.com/lang/zh-hans/docs/install/#windows-stable"),s("OutboundLink")],1)]),t._v(" "),t._m(8),t._v(" "),t._m(9),s("p",[t._v("如果不能正常运行,就删除 yarn.lock、node_modules文件夹,再重新执行上面的命令")]),t._v(" "),t._m(10),t._v(" "),t._m(11),t._m(12),t._v(" "),s("p",[t._v("发布至github pages 中的gh-pages分支")]),t._v(" "),t._m(13),s("p",[t._v(".sh 也不懂,关键我本地是windows,不能正常执行,git bash 也许可以")]),t._v(" "),t._m(14),t._v(" "),s("p",[t._v("package.json有这些命令")]),t._v(" "),t._m(15),s("p",[t._v("所以我们可以 yarn dev或yarn build\n其他的,暂时还不会跑")]),t._v(" "),s("p",[t._v("bash无法推送\nssh: connect to host github.com port 22: No route to host\nfatal: Could not read from remote repository.")]),t._v(" "),t._m(16),s("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"简介"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#简介","aria-hidden":"true"}},[this._v("#")]),this._v(" 简介")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"文档源码"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#文档源码","aria-hidden":"true"}},[this._v("#")]),this._v(" 文档源码")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"vuepress"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#vuepress","aria-hidden":"true"}},[this._v("#")]),this._v(" vuepress")])},function(){var t=this.$createElement,e=this._self._c||t;return e("p",[this._v("该采用"),e("code",[this._v("vuepress")]),this._v("搭建,内置"),e("code",[this._v("md")]),this._v(",可以采用"),e("code",[this._v("vue")]),this._v("语法,vue作者出品")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"完整文档"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#完整文档","aria-hidden":"true"}},[this._v("#")]),this._v(" 完整文档")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"lincms"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#lincms","aria-hidden":"true"}},[this._v("#")]),this._v(" LinCMS")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"如何构建运行"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#如何构建运行","aria-hidden":"true"}},[this._v("#")]),this._v(" 如何构建运行")])},function(){var t=this.$createElement,e=this._self._c||t;return e("ul",[e("li",[this._v("node.js")]),this._v(" "),e("li",[this._v("yarn或npm")]),this._v(" "),e("li",[this._v("vuepress")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"安装依赖包,开发运行"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#安装依赖包,开发运行","aria-hidden":"true"}},[this._v("#")]),this._v(" 安装依赖包,开发运行")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("PS D:\\code\\github\\vuepress-docs>yarn install\nPS D:\\code\\github\\vuepress-docs>yarn dev\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"生成发布包"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#生成发布包","aria-hidden":"true"}},[this._v("#")]),this._v(" 生成发布包")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-ps1 extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("PS D:\\code\\github\\vuepress-docs>yarn build \n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"发布"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#发布","aria-hidden":"true"}},[this._v("#")]),this._v(" 发布")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language-ps1 extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("PS D:\\code\\github\\vuepress-docs>.\\deploy.ps1\n")])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("h3",{attrs:{id:"package-json介绍"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#package-json介绍","aria-hidden":"true"}},[this._v("#")]),this._v(" package.json介绍")])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v(' "scripts": {\n "dev": "node bin/vuepress dev docs",\n "build": "node bin/vuepress build docs",\n "lint": "eslint --fix --ext .js,.vue bin/ lib/ test/",\n "deploy-gh": "yarn build && bash scripts/deploy-gh.sh",\n "prepublishOnly": "conventional-changelog -p angular -r 2 -i CHANGELOG.md -s",\n "release": "bash scripts/release.sh",\n "test": "node test/prepare.js && jest --config test/jest.config.js"\n },\n')])])])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"language- extra-class"},[e("pre",{pre:!0,attrs:{class:"language-text"}},[e("code",[this._v("yarn deploy-gh\n")])])])}],!1,null,null,null);e.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/4.06774d68.js b/assets/js/4.06774d68.js new file mode 100644 index 0000000..3aa9429 --- /dev/null +++ b/assets/js/4.06774d68.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{154:function(t,n,a){},157:function(t,n,a){"use strict";var e=a(154);a.n(e).a},165:function(t,n,a){"use strict";a.r(n);var e={functional:!0,props:{text:String,status:{type:String,default:"enable"}},render:function(t,n){var a=n.props;return t("div",{class:["btn",a.status]},a.text)}},s=(a(157),a(0)),r=Object(s.a)(e,void 0,void 0,!1,null,"58baaab4",null);n.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/5.82af18d5.js b/assets/js/5.82af18d5.js new file mode 100644 index 0000000..ad987a7 --- /dev/null +++ b/assets/js/5.82af18d5.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{163:function(n,w,o){}}]); \ No newline at end of file diff --git a/assets/js/6.7b20ecca.js b/assets/js/6.7b20ecca.js new file mode 100644 index 0000000..6960690 --- /dev/null +++ b/assets/js/6.7b20ecca.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{175:function(t,n,e){"use strict";e.r(n);var s=e(0),c=Object(s.a)({},function(){var t=this.$createElement;return(this._self._c||t)("div",{staticClass:"content"})},[],!1,null,null,null);n.default=c.exports}}]); \ No newline at end of file diff --git a/assets/js/7.b71ef550.js b/assets/js/7.b71ef550.js new file mode 100644 index 0000000..f28be60 --- /dev/null +++ b/assets/js/7.b71ef550.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{174:function(t,e,r){"use strict";r.r(e);var a=r(0),i=Object(a.a)({},function(){var t=this,e=t.$createElement,r=t._self._c||e;return r("div",{staticClass:"content"},[t._m(0),t._v(" "),t._m(1),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"https://github.com/weilanwl/ColorUI",target:"_blank",rel:"noopener noreferrer"}},[t._v("https://github.com/weilanwl/ColorUI"),r("OutboundLink")],1)])]),t._v(" "),t._m(2),t._v(" "),r("p",[t._v("* 这是一个css库,相比其他vant-weapp、mpvue都是不一样的,非组件化,主要定位我觉得是美化主题,非组件化,支持uni-app、mpvue,可结合vant-weapp一起使用。")]),t._v(" "),t._m(3),t._v(" "),r("p",[t._v("很简单,官方还没出文档,使用起来,需要开发者自行去看main.css,打开手机看小程序的demo,然后再去找项目源码,我觉得太麻烦,不知道其他人的感受如何。")]),t._v(" "),r("RightMenu")],1)},[function(){var t=this.$createElement,e=this._self._c||t;return e("h1",{attrs:{id:"colorui"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#colorui","aria-hidden":"true"}},[this._v("#")]),this._v(" ColorUI")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"github"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#github","aria-hidden":"true"}},[this._v("#")]),this._v(" github")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"特点"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#特点","aria-hidden":"true"}},[this._v("#")]),this._v(" 特点")])},function(){var t=this.$createElement,e=this._self._c||t;return e("h2",{attrs:{id:"该文档作用"}},[e("a",{staticClass:"header-anchor",attrs:{href:"#该文档作用","aria-hidden":"true"}},[this._v("#")]),this._v(" 该文档作用")])}],!1,null,null,null);e.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/8.8cdfa53b.js b/assets/js/8.8cdfa53b.js new file mode 100644 index 0000000..c2d5c84 --- /dev/null +++ b/assets/js/8.8cdfa53b.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{173:function(t,n,s){"use strict";s.r(n);var c=s(0),a=Object(c.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var t=this.$createElement,n=this._self._c||t;return n("div",{staticClass:"content"},[n("h1",{attrs:{id:"button-按钮"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#button-按钮","aria-hidden":"true"}},[this._v("#")]),this._v(" Button 按钮")]),this._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v('\n\n\n')])])]),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v('.cu-btn \n.cu-btn::after\n.cu-btn:not([class*="bg-"]) \n.cu-btn[class*="line"] \n.cu-btn[class*="line"]::after\n.cu-btn.round[class*="line"]::after \n.cu-btn[class*="lines"]::after \n.cu-btn[class*="bg-"]::after \n.cu-btn.sm \n.cu-btn.lg \n.cu-btn.cuIcon.sm \n.cu-btn.cuIcon \nbutton.cuIcon.lg \n.cu-btn.shadow-blur::before \n.cu-btn.button-hover \n.block \n.cu-btn.block \n.cu-btn[disabled] \n')])])]),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v(".cu-bar.btn-group\n.cu-bar.btn-group button\n.cu-bar.btn-group button\n")])])])])}],!1,null,null,null);n.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/9.c05b688d.js b/assets/js/9.c05b688d.js new file mode 100644 index 0000000..36a4628 --- /dev/null +++ b/assets/js/9.c05b688d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{172:function(e,n,t){"use strict";t.r(n);var l=t(0),i=Object(l.a)({},function(){this.$createElement;this._self._c;return this._m(0)},[function(){var e=this.$createElement,n=this._self._c||e;return n("div",{staticClass:"content"},[n("h1",{attrs:{id:"text-文本"}},[n("a",{staticClass:"header-anchor",attrs:{href:"#text-文本","aria-hidden":"true"}},[this._v("#")]),this._v(" Text 文本")]),this._v(" "),n("div",{staticClass:"language- extra-class"},[n("pre",{pre:!0,attrs:{class:"language-text"}},[n("code",[this._v(".text-xs \n.text-sm \n.text-df \n.text-lg \n.text-xl \n.text-xxl \n.text-sl \n.text-xsl \n.text-Abc \n.text-ABC \n.text-abc \n.text-price::before \n.text-cut \n.text-bold \n.text-center \n.text-content \n.text-left \n.text-right\n.text-red\n.line-red\n.lines-red \n.text-orange\n.line-orange\n.lines-orange\n.text-yellow\n.line-yellow\n.lines-yellow \n.text-olive\n.line-olive\n.lines-olive\n.text-green\n.line-green\n.lines-green \n.text-cyan\n.line-cyan\n.lines-cyan \n.text-blue\n.line-blue\n.lines-blue \n.text-purple\n.line-purple\n.lines-purple \n.text-mauve\n.line-mauve\n.lines-mauve \n.text-pink\n.line-pink\n.lines-pink \n.text-brown\n.line-brown\n.lines-brown\n.text-grey\n.line-grey\n.lines-grey \n.text-gray\n.line-gray\n.lines-gray\n.text-black\n.line-black\n.lines-black \n.text-white\n.line-white\n.lines-white\n")])])])])}],!1,null,null,null);n.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/app.da0724c2.js b/assets/js/app.da0724c2.js new file mode 100644 index 0000000..02274e0 --- /dev/null +++ b/assets/js/app.da0724c2.js @@ -0,0 +1,8 @@ +!function(t){function e(e){for(var n,r,i=e[0],a=e[1],s=0,c=[];s0?o(r(t),9007199254740991):0}},function(t,e){t.exports=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t}},function(t,e,n){"use strict";var r=n(111),o=n(83),i=n(16),a=n(26);t.exports=n(84)(Array,"Array",function(t,e){this._t=a(t),this._i=0,this._k=e},function(){var t=this._t,e=this._k,n=this._i++;return!t||n>=t.length?(this._t=void 0,o(1)):o(0,"keys"==e?n:"values"==e?t[n]:[n,t[n]])},"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},function(t,e){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},function(t,e){var n=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++n+r).toString(36))}},function(t,e){var n=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:n)(t)}},function(t,e,n){var r=n(7).f,o=n(13),i=n(1)("toStringTag");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,i)&&r(t,i,{configurable:!0,value:e})}},function(t,e,n){var r=n(8);r(r.S+r.F,"Object",{assign:n(106)})},function(t,e,n){var r=n(79),o=n(64);t.exports=Object.keys||function(t){return r(t,o)}},function(t,e,n){var r=n(80),o=n(18);t.exports=function(t){return r(o(t))}},function(t,e,n){var r=n(18);t.exports=function(t){return Object(r(t))}},function(t,e,n){var r=n(27),o=n(25);n(82)("keys",function(){return function(t){return o(r(t))}})},function(t,e,n){"use strict";var r=n(3),o=n(27),i=n(17),a=n(22),s=n(67),c=n(68),l=Math.max,u=Math.min,f=Math.floor,p=/\$([$&`']|\d\d?|<[^>]*>)/g,d=/\$([$&`']|\d\d?)/g;n(70)("replace",2,function(t,e,n,h){return[function(r,o){var i=t(this),a=null==r?void 0:r[e];return void 0!==a?a.call(r,i,o):n.call(String(i),r,o)},function(t,e){var o=h(n,t,this,e);if(o.done)return o.value;var f=r(t),p=String(this),d="function"==typeof e;d||(e=String(e));var m=f.global;if(m){var g=f.unicode;f.lastIndex=0}for(var y=[];;){var b=c(f,p);if(null===b)break;if(y.push(b),!m)break;""===String(b[0])&&(f.lastIndex=s(p,i(f.lastIndex),g))}for(var _,x="",w=0,C=0;C=w&&(x+=p.slice(w,S)+T,w=S+k.length)}return x+p.slice(w)}];function v(t,e,r,i,a,s){var c=r+t.length,l=i.length,u=d;return void 0!==a&&(a=o(a),u=p),n.call(s,u,function(n,o){var s;switch(o.charAt(0)){case"$":return"$";case"&":return t;case"`":return e.slice(0,r);case"'":return e.slice(c);case"<":s=a[o.slice(1,-1)];break;default:var u=+o;if(0===u)return n;if(u>l){var p=f(u/10);return 0===p?n:p<=l?void 0===i[p-1]?o.charAt(1):i[p-1]+o.charAt(1):n}s=i[u-1]}return void 0===s?"":s})}})},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){},function(t,e,n){var r,o; +/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT */void 0===(o="function"==typeof(r=function(){var t,e,n={version:"0.2.0"},r=n.settings={minimum:.08,easing:"ease",positionUsing:"",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
'};function o(t,e,n){return tn?n:t}function i(t){return 100*(-1+t)}n.configure=function(t){var e,n;for(e in t)void 0!==(n=t[e])&&t.hasOwnProperty(e)&&(r[e]=n);return this},n.status=null,n.set=function(t){var e=n.isStarted();t=o(t,r.minimum,1),n.status=1===t?null:t;var c=n.render(!e),l=c.querySelector(r.barSelector),u=r.speed,f=r.easing;return c.offsetWidth,a(function(e){""===r.positionUsing&&(r.positionUsing=n.getPositioningCSS()),s(l,function(t,e,n){var o;return(o="translate3d"===r.positionUsing?{transform:"translate3d("+i(t)+"%,0,0)"}:"translate"===r.positionUsing?{transform:"translate("+i(t)+"%,0)"}:{"margin-left":i(t)+"%"}).transition="all "+e+"ms "+n,o}(t,u,f)),1===t?(s(c,{transition:"none",opacity:1}),c.offsetWidth,setTimeout(function(){s(c,{transition:"all "+u+"ms linear",opacity:0}),setTimeout(function(){n.remove(),e()},u)},u)):setTimeout(e,u)}),this},n.isStarted=function(){return"number"==typeof n.status},n.start=function(){n.status||n.set(0);var t=function(){setTimeout(function(){n.status&&(n.trickle(),t())},r.trickleSpeed)};return r.trickle&&t(),this},n.done=function(t){return t||n.status?n.inc(.3+.5*Math.random()).set(1):this},n.inc=function(t){var e=n.status;return e?("number"!=typeof t&&(t=(1-e)*o(Math.random()*e,.1,.95)),e=o(e+t,0,.994),n.set(e)):n.start()},n.trickle=function(){return n.inc(Math.random()*r.trickleRate)},t=0,e=0,n.promise=function(r){return r&&"resolved"!==r.state()?(0===e&&n.start(),t++,e++,r.always(function(){0==--e?(t=0,n.done()):n.set((t-e)/t)}),this):this},n.render=function(t){if(n.isRendered())return document.getElementById("nprogress");l(document.documentElement,"nprogress-busy");var e=document.createElement("div");e.id="nprogress",e.innerHTML=r.template;var o,a=e.querySelector(r.barSelector),c=t?"-100":i(n.status||0),u=document.querySelector(r.parent);return s(a,{transition:"all 0 linear",transform:"translate3d("+c+"%,0,0)"}),r.showSpinner||(o=e.querySelector(r.spinnerSelector))&&p(o),u!=document.body&&l(u,"nprogress-custom-parent"),u.appendChild(e),e},n.remove=function(){u(document.documentElement,"nprogress-busy"),u(document.querySelector(r.parent),"nprogress-custom-parent");var t=document.getElementById("nprogress");t&&p(t)},n.isRendered=function(){return!!document.getElementById("nprogress")},n.getPositioningCSS=function(){var t=document.body.style,e="WebkitTransform"in t?"Webkit":"MozTransform"in t?"Moz":"msTransform"in t?"ms":"OTransform"in t?"O":"";return e+"Perspective"in t?"translate3d":e+"Transform"in t?"translate":"margin"};var a=function(){var t=[];function e(){var n=t.shift();n&&n(e)}return function(n){t.push(n),1==t.length&&e()}}(),s=function(){var t=["Webkit","O","Moz","ms"],e={};function n(n){return n=n.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(t,e){return e.toUpperCase()}),e[n]||(e[n]=function(e){var n=document.body.style;if(e in n)return e;for(var r,o=t.length,i=e.charAt(0).toUpperCase()+e.slice(1);o--;)if((r=t[o]+i)in n)return r;return e}(n))}function r(t,e,r){e=n(e),t.style[e]=r}return function(t,e){var n,o,i=arguments;if(2==i.length)for(n in e)void 0!==(o=e[n])&&e.hasOwnProperty(n)&&r(t,n,o);else r(t,i[1],i[2])}}();function c(t,e){var n="string"==typeof t?t:f(t);return n.indexOf(" "+e+" ")>=0}function l(t,e){var n=f(t),r=n+e;c(n,e)||(t.className=r.substring(1))}function u(t,e){var n,r=f(t);c(t,e)&&(n=r.replace(" "+e+" "," "),t.className=n.substring(1,n.length-1))}function f(t){return(" "+(t.className||"")+" ").replace(/\s+/gi," ")}function p(t){t&&t.parentNode&&t.parentNode.removeChild(t)}return n})?r.call(e,n,e,t):r)||(t.exports=o)},function(t,e){t.exports=!1},function(t,e,n){var r=n(15),o=n(1)("toStringTag"),i="Arguments"==r(function(){return arguments}());t.exports=function(t){var e,n,a;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(n=function(t,e){try{return t[e]}catch(t){}}(e=Object(t),o))?n:i?r(e):"Object"==(a=r(e))&&"function"==typeof e.callee?"Arguments":a}},function(t,e,n){var r=n(12),o=n(2),i=o["__core-js_shared__"]||(o["__core-js_shared__"]={});(t.exports=function(t,e){return i[t]||(i[t]=void 0!==e?e:{})})("versions",[]).push({version:r.version,mode:n(54)?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},function(t,e,n){var r=n(4),o=n(2).document,i=r(o)&&r(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},function(t,e){t.exports=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}}},function(t,e){t.exports=function(t,e,n,r){if(!(t instanceof e)||void 0!==r&&r in t)throw TypeError(n+": incorrect invocation!");return t}},function(t,e,n){var r=n(11),o=n(98),i=n(99),a=n(3),s=n(17),c=n(100),l={},u={};(e=t.exports=function(t,e,n,f,p){var d,h,v,m,g=p?function(){return t}:c(t),y=r(n,f,e?2:1),b=0;if("function"!=typeof g)throw TypeError(t+" is not iterable!");if(i(g)){for(d=s(t.length);d>b;b++)if((m=e?y(a(h=t[b])[0],h[1]):y(t[b]))===l||m===u)return m}else for(v=g.call(t);!(h=v.next()).done;)if((m=o(v,y,h.value,e))===l||m===u)return m}).BREAK=l,e.RETURN=u},function(t,e,n){var r=n(10);t.exports=function(t,e,n){for(var o in e)r(t,o,e[o],n);return t}},function(t,e,n){"use strict";var r=n(2),o=n(7),i=n(5),a=n(1)("species");t.exports=function(t){var e=r[t];i&&e&&!e[a]&&o.f(e,a,{configurable:!0,get:function(){return this}})}},function(t,e,n){var r=n(56)("keys"),o=n(21);t.exports=function(t){return r[t]||(r[t]=o(t))}},function(t,e){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(t,e,n){var r=n(21)("meta"),o=n(4),i=n(13),a=n(7).f,s=0,c=Object.isExtensible||function(){return!0},l=!n(6)(function(){return c(Object.preventExtensions({}))}),u=function(t){a(t,r,{value:{i:"O"+ ++s,w:{}}})},f=t.exports={KEY:r,NEED:!1,fastKey:function(t,e){if(!o(t))return"symbol"==typeof t?t:("string"==typeof t?"S":"P")+t;if(!i(t,r)){if(!c(t))return"F";if(!e)return"E";u(t)}return t[r].i},getWeak:function(t,e){if(!i(t,r)){if(!c(t))return!0;if(!e)return!1;u(t)}return t[r].w},onFreeze:function(t){return l&&f.NEED&&c(t)&&!i(t,r)&&u(t),t}}},function(t,e,n){var r=n(7).f,o=Function.prototype,i=/^\s*function ([^ (]*)/;"name"in o||n(5)&&r(o,"name",{configurable:!0,get:function(){try{return(""+this).match(i)[1]}catch(t){return""}}})},function(t,e,n){"use strict";var r=n(118)(!0);t.exports=function(t,e,n){return e+(n?r(t,e).length:1)}},function(t,e,n){"use strict";var r=n(55),o=RegExp.prototype.exec;t.exports=function(t,e){var n=t.exec;if("function"==typeof n){var i=n.call(t,e);if("object"!=typeof i)throw new TypeError("RegExp exec method returned something other than an Object or null");return i}if("RegExp"!==r(t))throw new TypeError("RegExp#exec called on incompatible receiver");return o.call(t,e)}},function(t,e,n){"use strict";var r,o,i=n(87),a=RegExp.prototype.exec,s=String.prototype.replace,c=a,l=(r=/a/,o=/b*/g,a.call(r,"a"),a.call(o,"a"),0!==r.lastIndex||0!==o.lastIndex),u=void 0!==/()??/.exec("")[1];(l||u)&&(c=function(t){var e,n,r,o,c=this;return u&&(n=new RegExp("^"+c.source+"$(?!\\s)",i.call(c))),l&&(e=c.lastIndex),r=a.call(c,t),l&&r&&(c.lastIndex=c.global?r.index+r[0].length:e),u&&r&&r.length>1&&s.call(r[0],n,function(){for(o=1;o")}),f=function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n="ab".split(t);return 2===n.length&&"a"===n[0]&&"b"===n[1]}();t.exports=function(t,e,n){var p=s(t),d=!i(function(){var e={};return e[p]=function(){return 7},7!=""[t](e)}),h=d?!i(function(){var e=!1,n=/a/;return n.exec=function(){return e=!0,null},"split"===t&&(n.constructor={},n.constructor[l]=function(){return n}),n[p](""),!e}):void 0;if(!d||!h||"replace"===t&&!u||"split"===t&&!f){var v=/./[p],m=n(a,p,""[t],function(t,e,n,r,o){return e.exec===c?d&&!o?{done:!0,value:v.call(e,n,r)}:{done:!0,value:t.call(n,e,r)}:{done:!1}}),g=m[0],y=m[1];r(String.prototype,t,g),o(RegExp.prototype,p,2==e?function(t,e){return y.call(t,this,e)}:function(t){return y.call(t,this)})}}},function(t,e,n){"use strict";var r,o,i,a,s=n(54),c=n(2),l=n(11),u=n(55),f=n(8),p=n(4),d=n(20),h=n(59),v=n(60),m=n(74),g=n(75).set,y=n(102)(),b=n(77),_=n(103),x=n(104),w=n(105),C=c.TypeError,k=c.process,S=k&&k.versions,A=S&&S.v8||"",$=c.Promise,E="process"==u(k),O=function(){},T=o=b.f,j=!!function(){try{var t=$.resolve(1),e=(t.constructor={})[n(1)("species")]=function(t){t(O,O)};return(E||"function"==typeof PromiseRejectionEvent)&&t.then(O)instanceof e&&0!==A.indexOf("6.6")&&-1===x.indexOf("Chrome/66")}catch(t){}}(),L=function(t){var e;return!(!p(t)||"function"!=typeof(e=t.then))&&e},I=function(t,e){if(!t._n){t._n=!0;var n=t._c;y(function(){for(var r=t._v,o=1==t._s,i=0,a=function(e){var n,i,a,s=o?e.ok:e.fail,c=e.resolve,l=e.reject,u=e.domain;try{s?(o||(2==t._h&&P(t),t._h=1),!0===s?n=r:(u&&u.enter(),n=s(r),u&&(u.exit(),a=!0)),n===e.promise?l(C("Promise-chain cycle")):(i=L(n))?i.call(n,c,l):c(n)):l(r)}catch(t){u&&!a&&u.exit(),l(t)}};n.length>i;)a(n[i++]);t._c=[],t._n=!1,e&&!t._h&&M(t)})}},M=function(t){g.call(c,function(){var e,n,r,o=t._v,i=R(t);if(i&&(e=_(function(){E?k.emit("unhandledRejection",o,t):(n=c.onunhandledrejection)?n({promise:t,reason:o}):(r=c.console)&&r.error&&r.error("Unhandled promise rejection",o)}),t._h=E||R(t)?2:1),t._a=void 0,i&&e.e)throw e.v})},R=function(t){return 1!==t._h&&0===(t._a||t._c).length},P=function(t){g.call(c,function(){var e;E?k.emit("rejectionHandled",t):(e=c.onrejectionhandled)&&e({promise:t,reason:t._v})})},U=function(t){var e=this;e._d||(e._d=!0,(e=e._w||e)._v=t,e._s=2,e._a||(e._a=e._c.slice()),I(e,!0))},N=function(t){var e,n=this;if(!n._d){n._d=!0,n=n._w||n;try{if(n===t)throw C("Promise can't be resolved itself");(e=L(t))?y(function(){var r={_w:n,_d:!1};try{e.call(t,l(N,r,1),l(U,r,1))}catch(t){U.call(r,t)}}):(n._v=t,n._s=1,I(n,!1))}catch(t){U.call({_w:n,_d:!1},t)}}};j||($=function(t){h(this,$,"Promise","_h"),d(t),r.call(this);try{t(l(N,this,1),l(U,this,1))}catch(t){U.call(this,t)}},(r=function(t){this._c=[],this._a=void 0,this._s=0,this._d=!1,this._v=void 0,this._h=0,this._n=!1}).prototype=n(61)($.prototype,{then:function(t,e){var n=T(m(this,$));return n.ok="function"!=typeof t||t,n.fail="function"==typeof e&&e,n.domain=E?k.domain:void 0,this._c.push(n),this._a&&this._a.push(n),this._s&&I(this,!1),n.promise},catch:function(t){return this.then(void 0,t)}}),i=function(){var t=new r;this.promise=t,this.resolve=l(N,t,1),this.reject=l(U,t,1)},b.f=T=function(t){return t===$||t===a?new i(t):o(t)}),f(f.G+f.W+f.F*!j,{Promise:$}),n(23)($,"Promise"),n(62)("Promise"),a=n(12).Promise,f(f.S+f.F*!j,"Promise",{reject:function(t){var e=T(this);return(0,e.reject)(t),e.promise}}),f(f.S+f.F*(s||!j),"Promise",{resolve:function(t){return w(s&&this===a?$:this,t)}}),f(f.S+f.F*!(j&&n(78)(function(t){$.all(t).catch(O)})),"Promise",{all:function(t){var e=this,n=T(e),r=n.resolve,o=n.reject,i=_(function(){var n=[],i=0,a=1;v(t,!1,function(t){var s=i++,c=!1;n.push(void 0),a++,e.resolve(t).then(function(t){c||(c=!0,n[s]=t,--a||r(n))},o)}),--a||r(n)});return i.e&&o(i.v),n.promise},race:function(t){var e=this,n=T(e),r=n.reject,o=_(function(){v(t,!1,function(t){e.resolve(t).then(n.resolve,r)})});return o.e&&r(o.v),n.promise}})},function(t,e,n){t.exports=!n(5)&&!n(6)(function(){return 7!=Object.defineProperty(n(57)("div"),"a",{get:function(){return 7}}).a})},function(t,e,n){var r=n(4);t.exports=function(t,e){if(!r(t))return t;var n,o;if(e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;if("function"==typeof(n=t.valueOf)&&!r(o=n.call(t)))return o;if(!e&&"function"==typeof(n=t.toString)&&!r(o=n.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},function(t,e,n){var r=n(3),o=n(20),i=n(1)("species");t.exports=function(t,e){var n,a=r(t).constructor;return void 0===a||null==(n=r(a)[i])?e:o(n)}},function(t,e,n){var r,o,i,a=n(11),s=n(101),c=n(76),l=n(57),u=n(2),f=u.process,p=u.setImmediate,d=u.clearImmediate,h=u.MessageChannel,v=u.Dispatch,m=0,g={},y=function(){var t=+this;if(g.hasOwnProperty(t)){var e=g[t];delete g[t],e()}},b=function(t){y.call(t.data)};p&&d||(p=function(t){for(var e=[],n=1;arguments.length>n;)e.push(arguments[n++]);return g[++m]=function(){s("function"==typeof t?t:Function(t),e)},r(m),m},d=function(t){delete g[t]},"process"==n(15)(f)?r=function(t){f.nextTick(a(y,t,1))}:v&&v.now?r=function(t){v.now(a(y,t,1))}:h?(i=(o=new h).port2,o.port1.onmessage=b,r=a(i.postMessage,i,1)):u.addEventListener&&"function"==typeof postMessage&&!u.importScripts?(r=function(t){u.postMessage(t+"","*")},u.addEventListener("message",b,!1)):r="onreadystatechange"in l("script")?function(t){c.appendChild(l("script")).onreadystatechange=function(){c.removeChild(this),y.call(t)}}:function(t){setTimeout(a(y,t,1),0)}),t.exports={set:p,clear:d}},function(t,e,n){var r=n(2).document;t.exports=r&&r.documentElement},function(t,e,n){"use strict";var r=n(20);function o(t){var e,n;this.promise=new t(function(t,r){if(void 0!==e||void 0!==n)throw TypeError("Bad Promise constructor");e=t,n=r}),this.resolve=r(e),this.reject=r(n)}t.exports.f=function(t){return new o(t)}},function(t,e,n){var r=n(1)("iterator"),o=!1;try{var i=[7][r]();i.return=function(){o=!0},Array.from(i,function(){throw 2})}catch(t){}t.exports=function(t,e){if(!e&&!o)return!1;var n=!1;try{var i=[7],a=i[r]();a.next=function(){return{done:n=!0}},i[r]=function(){return a},t(i)}catch(t){}return n}},function(t,e,n){var r=n(13),o=n(26),i=n(107)(!1),a=n(63)("IE_PROTO");t.exports=function(t,e){var n,s=o(t),c=0,l=[];for(n in s)n!=a&&r(s,n)&&l.push(n);for(;e.length>c;)r(s,n=e[c++])&&(~i(l,n)||l.push(n));return l}},function(t,e,n){var r=n(15);t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},function(t,e){e.f={}.propertyIsEnumerable},function(t,e,n){var r=n(8),o=n(12),i=n(6);t.exports=function(t,e){var n=(o.Object||{})[t]||Object[t],a={};a[t]=e(n),r(r.S+r.F*i(function(){n(1)}),"Object",a)}},function(t,e){t.exports=function(t,e){return{value:e,done:!!t}}},function(t,e,n){"use strict";var r=n(54),o=n(8),i=n(10),a=n(9),s=n(16),c=n(112),l=n(23),u=n(114),f=n(1)("iterator"),p=!([].keys&&"next"in[].keys()),d=function(){return this};t.exports=function(t,e,n,h,v,m,g){c(n,e,h);var y,b,_,x=function(t){if(!p&&t in S)return S[t];switch(t){case"keys":case"values":return function(){return new n(this,t)}}return function(){return new n(this,t)}},w=e+" Iterator",C="values"==v,k=!1,S=t.prototype,A=S[f]||S["@@iterator"]||v&&S[v],$=A||x(v),E=v?C?x("entries"):$:void 0,O="Array"==e&&S.entries||A;if(O&&(_=u(O.call(new t)))!==Object.prototype&&_.next&&(l(_,w,!0),r||"function"==typeof _[f]||a(_,f,d)),C&&A&&"values"!==A.name&&(k=!0,$=function(){return A.call(this)}),r&&!g||!p&&!k&&S[f]||a(S,f,$),s[e]=$,s[w]=d,v)if(y={values:C?$:x("values"),keys:m?$:x("keys"),entries:E},g)for(b in y)b in S||i(S,b,y[b]);else o(o.P+o.F*(p||k),e,y);return y}},function(t,e,n){var r=n(3),o=n(113),i=n(64),a=n(63)("IE_PROTO"),s=function(){},c=function(){var t,e=n(57)("iframe"),r=i.length;for(e.style.display="none",n(76).appendChild(e),e.src="/service/javascript:",(t=e.contentWindow.document).open(),t.write(" + + + + + + +

Button 按钮

<button class="cu-btn">默认</button>
+<button class="cu-btn round">圆角</button>
+<button class="cu-btn icon">
+	<text class="cuIcon-emojifill"></text>
+</button>
+
.cu-btn 
+.cu-btn::after
+.cu-btn:not([class*="bg-"]) 
+.cu-btn[class*="line"] 
+.cu-btn[class*="line"]::after
+.cu-btn.round[class*="line"]::after 
+.cu-btn[class*="lines"]::after 
+.cu-btn[class*="bg-"]::after 
+.cu-btn.sm 
+.cu-btn.lg 
+.cu-btn.cuIcon.sm 
+.cu-btn.cuIcon 
+button.cuIcon.lg 
+.cu-btn.shadow-blur::before 
+.cu-btn.button-hover 
+.block 
+.cu-btn.block 
+.cu-btn[disabled] 
+
.cu-bar.btn-group
+.cu-bar.btn-group button
+.cu-bar.btn-group button
+
上次更新: 10/8/2019, 1:41:22 PM
+ + + diff --git a/colorui/docs/index.html b/colorui/docs/index.html new file mode 100644 index 0000000..6d3cd27 --- /dev/null +++ b/colorui/docs/index.html @@ -0,0 +1,35 @@ + + + + + + + ColorUI | IGeekFan的文档 + + + + + + + + +

ColorUI

github

特点

* 这是一个css库,相比其他vant-weapp、mpvue都是不一样的,非组件化,主要定位我觉得是美化主题,非组件化,支持uni-app、mpvue,可结合vant-weapp一起使用。

该文档作用

很简单,官方还没出文档,使用起来,需要开发者自行去看main.css,打开手机看小程序的demo,然后再去找项目源码,我觉得太麻烦,不知道其他人的感受如何。

    上次更新: 10/8/2019, 1:41:22 PM
    + + + diff --git a/colorui/docs/text.html b/colorui/docs/text.html new file mode 100644 index 0000000..ad07eaa --- /dev/null +++ b/colorui/docs/text.html @@ -0,0 +1,102 @@ + + + + + + + Text 文本 | IGeekFan的文档 + + + + + + + + +

    Text 文本

    .text-xs 
    +.text-sm 
    +.text-df 
    +.text-lg 
    +.text-xl 
    +.text-xxl 
    +.text-sl 
    +.text-xsl 
    +.text-Abc 
    +.text-ABC 
    +.text-abc 
    +.text-price::before 
    +.text-cut 
    +.text-bold 
    +.text-center 
    +.text-content 
    +.text-left 
    +.text-right
    +.text-red
    +.line-red
    +.lines-red 
    +.text-orange
    +.line-orange
    +.lines-orange
    +.text-yellow
    +.line-yellow
    +.lines-yellow 
    +.text-olive
    +.line-olive
    +.lines-olive
    +.text-green
    +.line-green
    +.lines-green 
    +.text-cyan
    +.line-cyan
    +.lines-cyan 
    +.text-blue
    +.line-blue
    +.lines-blue 
    +.text-purple
    +.line-purple
    +.lines-purple 
    +.text-mauve
    +.line-mauve
    +.lines-mauve 
    +.text-pink
    +.line-pink
    +.lines-pink 
    +.text-brown
    +.line-brown
    +.lines-brown
    +.text-grey
    +.line-grey
    +.lines-grey 
    +.text-gray
    +.line-gray
    +.lines-gray
    +.text-black
    +.line-black
    +.lines-black 
    +.text-white
    +.line-white
    +.lines-white
    +
    上次更新: 10/8/2019, 1:41:22 PM
    + + + diff --git a/dotnetcore/examples/Console-Hello-World.html b/dotnetcore/examples/Console-Hello-World.html new file mode 100644 index 0000000..441b6aa --- /dev/null +++ b/dotnetcore/examples/Console-Hello-World.html @@ -0,0 +1,77 @@ + + + + + + + 创建简单Hello World | IGeekFan的文档 + + + + + + + + +

    创建简单Hello World

    源码

    代码托管在GitHub上 https://github.com/luoyunchong/dotnetcore-examples/tree/master/console-hello-world

    相关阅读

    开始

    创建一个hello-word的console,会输出Hello World!

    mkdir console-hello-world
    +cd console-hello-world
    +dotnet new console
    +dotnet run
    +

    console-hello-world.csproj

    OutputType 标记指定我们要生成的可执行文件,即控制台应用程序。

    TargetFramework 标记指定要定位的 .NET 实现代码。 在高级方案中,可以指定多个目标框架,并在单个操作中生成所有目标框架。

    <Project Sdk="Microsoft.NET.Sdk">
    +
    +  <PropertyGroup>
    +    <OutputType>Exe</OutputType>
    +    <TargetFramework>netcoreapp3.0</TargetFramework>
    +    <RootNamespace>console_hello_world</RootNamespace>
    +  </PropertyGroup>
    +
    +</Project>
    +
    +

    在 console-hello-world/bin/Debug/netcoreapp3.0中生成了console-hello-world.dll

    cd console-hello-world #要先在console-hello-world目录中
    +dotnet bin/Debug/netcoreapp3.0/console-hello-world.dll
    +Hello World
    +

    修改main函数

    using System;
    +
    +namespace console_hello_world
    +{
    +    class Program
    +    {
    +        static void Main(string[] args)
    +        {
    +            if (args.Length > 0)
    +            {
    +                Console.WriteLine($"Hello {args[0]}!");
    +            }
    +            else
    +            {
    +                Console.WriteLine("Hello!");
    +            }
    +        }
    +    }
    +}
    +
    $ dotnet run -- John
    +Hello John!
    +
      上次更新: 10/8/2019, 1:41:22 PM
      + + + diff --git a/dotnetcore/examples/Console-News-Types.html b/dotnetcore/examples/Console-News-Types.html new file mode 100644 index 0000000..f95550b --- /dev/null +++ b/dotnetcore/examples/Console-News-Types.html @@ -0,0 +1,74 @@ + + + + + + + .NET Core 简单测试项目 | IGeekFan的文档 + + + + + + + + +

      .NET Core 简单测试项目

      使用PowerShell的dotnet cli命令行创建控制台项目,测试项目,测试项目引用控制台项目。

      源码

      代码托管在GitHub上 https://github.com/luoyunchong/dotnetcore-examples/tree/master/console-news-types

      相关参考

      code :https://github.com/dotnet/samples/blob/master/core/console-apps/NewTypesMsBuild/README.md

      docs: https://docs.microsoft.com/zh-cn/dotnet/core/tutorials/testing-with-cli

      dotnet-add-reference 使用文档 https://docs.microsoft.com/zh-cn/dotnet/core/tools/dotnet-add-reference

      总结如下命令行

      PS dotnetcore-examples> mkdir console-news-types
      +PS dotnetcore-examples> cd .\console-news-types\ 
      +PS dotnetcore-examples\console-news-types> mkdir src
      +
      +PS dotnetcore-examples\console-news-types> mkdir test
      +PS dotnetcore-examples\console-news-types> ls
      +Mode                 LastWriteTime         Length Name
      +----                 -------------         ------ ----
      +d----           2019/6/27    20:51                src
      +d----           2019/6/27    20:51                test
      +
      +PS dotnetcore-examples\console-news-types> cd .\src\
      +PS dotnetcore-examples\console-news-types\src> dotnet new console -n NewTypes
      +PS dotnetcore-examples\console-news-types\src> cd ..\test\  
      +PS dotnetcore-examples\console-news-types\test> dotnet new xunit -n NewTypesTests 
      +PS dotnetcore-examples\console-news-types\test> cd .\NewTypesTests\
      +PS dotnetcore-examples\console-news-types\test\NewTypesTests> dotnet add reference ../../src/NewTypes/NewTypes.csproj
      +#Reference `..\..\src\NewTypes\NewTypes.csproj` added to the project.
      +

      其他的代码就看上面的二个链接,把代码复制进去,代码很简单,一个接口,二个实现,main函数调用,测试项目引用控制台项目,

      先cd 到src\NewTypes目录中,执行 dotnet run

      PS dotnetcore-examples\console-news-types\src\NewTypes> dotnet run
      +Woof!
      +Meow!
      +

      先cd 到test\NewTypesTests目录中,执行 dotnet test,看好代码,测试类 Assert.Equal() 是成功的,如果是Assert.NotEqual() 则Failure

      PS dotnetcore-examples\console-news-types\test\NewTypesTests> dotnet test
      +dotnetcore-examples\console-news-types\test\NewTypesTests\bin\Debug\netcoreapp3.0\NewTypesTests.dll 的测试运行(.NETCoreApp,Version=v3.0)
      +Microsoft (R) 测试执行命令行工具版本 16.0.1
      +版权所有 (C) Microsoft Corporation。保留所有权利。
      +
      +正在启动测试执行,请稍候...
      +
      +总测试: 2。已通过: 2。失败: 0。已跳过: 0。
      +测试运行成功。
      +测试执行时间: 1.5134 秒
      +
        上次更新: 10/8/2019, 1:41:22 PM
        + + + diff --git a/dotnetcore/examples/FreeSql-in-asp.net-core-webapi-how-to-use.html b/dotnetcore/examples/FreeSql-in-asp.net-core-webapi-how-to-use.html new file mode 100644 index 0000000..6c8d399 --- /dev/null +++ b/dotnetcore/examples/FreeSql-in-asp.net-core-webapi-how-to-use.html @@ -0,0 +1,184 @@ + + + + + + + FreeSql+ASP.NET Core | IGeekFan的文档 + + + + + + + + +

        FreeSql+ASP.NET Core

        文章概述

        主要在介绍FreeSql在ASP.NTE Core WebApi中如何使用的过程,完成一个最简单的博客系统的后端接口。

        FreeSql 简介

        国人写的一个功能强大的ORM,FreeSql 支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite,特点:轻量级、可扩展、基于 .NET Standard 跨平台。

        源码

        代码托管在GitHub上 https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-freesql

        参考

        项目准备

        • Mysql 5.6
        • Visual Studio 2019或2017、Visual Studio code
        • .NET Core 2.2+
        • PowerShell
        • 懂点mvc,该教程不会教你如何使用 ASP .NET Core MVC、RESTful

        创建项目

        使用dotnet 命令行创建一个webapi项目,起名为RESTful.FreeSql

        PS dotnetcore-examples\asp.net-core-freesql> dotnet new webapi -n RESTful.FreeSql
        +The template "ASP.NET Core Web API" was created successfully.
        +

        然后cd 到RESTful.FreeSql目录,通过dotnet run 命令运行项目

        PS dotnetcore-examples\asp.net-core-freesql> cd .\RESTful.FreeSql\
        +PS dotnetcore-examples\asp.net-core-freesql\RESTful.FreeSql> dotnet run
        +
        +info: Microsoft.Hosting.Lifetime[0]
        +      Now listening on: https://localhost:5001
        +info: Microsoft.Hosting.Lifetime[0]
        +      Now listening on: http://localhost:5000
        +info: Microsoft.Hosting.Lifetime[0]
        +      Application started. Press Ctrl+C to shut down.
        +info: Microsoft.Hosting.Lifetime[0]
        +      Hosting environment: Development
        +info: Microsoft.Hosting.Lifetime[0]
        +      Content root path: D:\code\github\dotnetcore-examples\asp.net-core-freesql\RESTful.FreeSql
        +

        打开浏览器 https://localhost:5001 会出现404

        请打开这个地址 https://localhost:5001/api/values ,可看到如下内容。

        ["value1","value2"]
        +

        接下来我们来集成FreeSql,我们以最简单的命令和说明,详细内容去官网看具体内容

        Install

        要先cd到RESTful.FreeSql目录中。

        PS \asp.net-core-freesql\RESTful.FreeSql> dotnet add package FreeSql
        +PS \asp.net-core-freesql\RESTful.FreeSql> dotnet add package FreeSql.Provider.MySql
        +

        code first

        代码优先,使用过EntityFramework的应该很清楚这一概念,我的理解就是:在分析数据库表关系时,不通过在数据库中设计表,而是直接在代码中声明对应的类,使用导航属性代替外键关联,通过数据表字段与C#中的类库对应,从而自动生成数据表。

        db first

        数据库优先:需求分析后,直接设计数据库,通过数据库中的表,直接生成代码,类。

        开始

        分析需求

        我们以code first 为示例,学习如何使用freesql,实现一个简单的博客。将表内容分为博客表(Blog)和评论表(Post)

        Blog 表

        字段名 字段类型 说明
        BlogId int 博客id
        Title varchar(50) 博客标题
        Content varchar(500) 博客内容
        CreateTime DateTime 发布时间

        Post 表

        字段名 字段类型 说明
        PostId int 评论id
        ReplyContent varchar(50) 标题
        BlogId int 博客id
        ReplyTime DateTime 回复时间

        建一个Domain文件夹,用于存放数据库表中对应的实体类。

        基础介绍

        1. Column属性介绍,大家可以看源码,解析

        1). 比如:Blog表中指定了Title为varchar(50),我们如何通过代码指定了主键,唯一值,字形。

            public class Blog
        +    {
        +        [Column(IsIdentity = true, IsPrimary = true)]
        +        public int BlogId { get; set; }
        +        [Column(DbType = "varchar(50)")]
        +        public string Title { get; set; }
        +    }
        +

        2). Column的命名空间在

        using FreeSql.DataAnnotations;
        +

        更多属性介绍

        字段 备注
        Name 数据库列名
        OldName 指定数据库旧的列名,修改实体属性命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库字段;否则将视为【新增字段】
        DbType 数据库类型,如: varchar(255)
        IsPrimary 主键
        IsIdentity 自增标识
        IsNullable 是否可DBNull
        IsIgnore 忽略此列,不迁移、不插入
        IsVersion 设置行锁(乐观锁)版本号,每次更新累加版本号,若更新整个实体时会附带当前的版本号判断(修改失败时抛出异常)
        DbDefautValue 数据库默认值
        MapType 类型映射,比如:可将 enum 属性映射成 typeof(string)
        Uniques 唯一键,在多个属性指定相同的标识,代表联合键;可使用逗号分割多个 UniqueKey 名。

        2. Table 的使用:用于在类的上面指定这个表的属性

        [Table(Name = "t_blog")]
        +public class Blog {
        +  //...
        +}
        +

        更多属性介绍

        字段 备注
        Name 数据库表名
        OldName 指定数据库旧的表名,修改实体命名时,同时设置此参数为修改之前的值,CodeFirst才可以正确修改数据库表;否则将视为【创建新表】
        SelectFilter 查询过滤SQL,实现类似 a.IsDeleted = 1 功能
        DisableSyncStructure 禁用 CodeFirst 同步结构迁移

        3. 其他的还是看 https://github.com/2881099/FreeSql/blob/master/Docs/codefirst.md

        Blog.cs

        using FreeSql.DataAnnotations;
        +using System;
        +
        +namespace RESTful.FreeSql.Domain
        +{
        +    public class Blog
        +    {
        +        [Column(IsIdentity = true, IsPrimary = true)]
        +        public int BlogId { get; set; }
        +        [Column(DbType = "varchar(50)")]
        +        public string Title { get; set; }
        +        [Column(DbType = "varchar(500)")]
        +        public string Content { get; set; }
        +        public DateTime CreateTime { get; set; }
        +
        +
        +    }
        +}
        +

        Post.cs

        
        +using FreeSql.DataAnnotations;
        +using System;
        +
        +namespace RESTful.FreeSql.Domain
        +{
        +    public class Post
        +    {
        +        [Column(IsIdentity = true, IsPrimary = true)]
        +        public int PostId { get; set; }
        +        [Column(DbType = "varchar(50)")]
        +        public string ReplyContent { get; set; }
        +        public int BlogId { get; set; }
        +        public DateTime ReplyTime { get; set; }
        +        public Blog Blog { get; set; }
        +    }
        +}
        +

        Startup.cs

        非全部代码,这里注意点:要先在mysql中创建数据库FreeSql_Blog,否则一直提示主库xxxxx,官网未找到相关描述。

        这里初始化FreeSql,并使用单例模式,注入到默认的依赖中,这样在Controller中即可直接注入。

        namespace RESTful.FreeSql
        +{
        +    public class Startup
        +    {
        +        public Startup(IConfiguration configuration)
        +        {
        +            Fsql = new FreeSqlBuilder()
        +                        .UseConnectionString(DataType.MySql, @"Data Source=127.0.0.1;Port=3306;User ID=root;Password=123456;Initial Catalog=FreeSql_Blog;Charset=utf8;SslMode=none;Max pool size=10")
        +                        .UseAutoSyncStructure(true)
        +                        .Build();
        +        }
        +
        +        public IFreeSql Fsql { get; }
        +
        +        public void ConfigureServices(IServiceCollection services)
        +        {
        +            services.AddSingleton<IFreeSql>(Fsql);
        +
        +        }
        +    }
        +}
        +

        BlogController

        在controllers文件夹新建一个控制器BlogController

        using System;
        +using System.Collections.Generic;
        +using System.Linq;
        +using System.Threading.Tasks;
        +using FreeSql;
        +using Microsoft.AspNetCore.Mvc;
        +using RESTful.FreeSql.Domain;
        +
        +namespace RESTful.FreeSql.Controllers
        +{
        +    [Route("api/[controller]")]
        +    [ApiController]
        +    public class BlogController : ControllerBase
        +    {
        +        // GET api/Blog
        +
        +        IFreeSql _fsql;
        +        public BlogController(IFreeSql fsql)
        +        {
        +            _fsql = fsql;
        +        }
        +
        +        [HttpGet]
        +        public ActionResult<IEnumerable<Blog>> Get()
        +        {
        +            List<Blog> blogs = _fsql.Select<Blog>().OrderByDescending(r => r.CreateTime).ToList();
        +
        +            return blogs;
        +        }
        +
        +        // GET api/blog/5
        +        [HttpGet("{id}")]
        +        public ActionResult<Blog> Get(int id)
        +        {
        +            return _fsql.Select<Blog>(id).ToOne();
        +        }
        +
        +
        +        // DELETE api/blog/5
        +        [HttpDelete("{id}")]
        +        public void Delete(int id)
        +        {
        +            _fsql.Delete<Blog>(new { BlogId = id }).ExecuteAffrows();
        +        }
        +    }
        +}
        +

        重新运行,打开地址 http://localhost:5001/api/blog 会发现数据库中生成了表blog,这时候表post并没有生成。所以我们判断,只有在访问到实体类才检查是否存在表结构,然后执行相应的处理。

        手动向blog表中加一些数据,然后再次请求

        • http://localhost:5001/api/blog, 可看到相应的数据。
        • http://localhost:5001/api/blog/1 可得到单个数据。

        自动同步实体结构【开发环境必备】

        此功能默认为开启状态,发布正式环境后,请修改此设置

        Fsql = new FreeSqlBuilder()
        +          .UseConnectionString(DataType.MySql, @"连接字符串")
        +          .UseAutoSyncStructure(true)
        +          .Build();
        +                      
        +//UseAutoSyncStructure(true/false)【开发环境必备】自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改
        +
        +// 也可使用此方法指定是否自动同步结构。                  
        +Fsql.CodeFirst.IsAutoSyncStructure = true;
        +
          上次更新: 10/8/2019, 1:41:22 PM
          + + + diff --git a/dotnetcore/examples/FreeSql-sample-blog-RESTful-use-automapper.html b/dotnetcore/examples/FreeSql-sample-blog-RESTful-use-automapper.html new file mode 100644 index 0000000..7f724b4 --- /dev/null +++ b/dotnetcore/examples/FreeSql-sample-blog-RESTful-use-automapper.html @@ -0,0 +1,323 @@ + + + + + + + RESTful+FreeSql+AutoMapper | IGeekFan的文档 + + + + + + + + +

          RESTful+FreeSql+AutoMapper

          文章概述

          本文使用ASP .NET Core的WEB API,构建一个RESTful风格的接口,使用Freesql访问MySQL数据库,实现二个表的简单博客,并集成AutoMapper。

          接上一篇

          Dto作用

          当我们使用RESTful提供接口时,比如创建一个博客,修改一下博客内容时,他们的参数是有区别的。良好的设计应该是

          创建一个博客

          POST /api/blog
          +data:
          +{
          +  "title": "string",
          +  "content": "string"
          +}
          +

          修改一个博客内容

          PUT /api/blog
          +data:
          +{
          +  "blogId":"int",
          +  "title": "string",
          +  "content": "string"
          +}
          +

          但一个blog 实体如下

              public class Blog
          +    {
          +        [Column(IsIdentity = true, IsPrimary = true)]
          +        public int BlogId { get; set; }
          +        public string Title { get; set; }
          +        public string Content { get; set; }
          +        public DateTime CreateTime { get; set; }
          +        public virtual List<Post> Posts { get; set; }
          +    }
          +

          如果我们以Blog作为controllers中的参数时

                 // POST api/blog
          +        [HttpPost]
          +        public void Post([FromBody] Blog blog)
          +        {
          +            blog.CreateTime=DateTime.Now;
          +            _fsql.Insert<Blog>(blog).ExecuteAffrows();
          +        }
          +

          这时修改swagger显示的默认参数是

          {
          +  "blogId": 0,
          +  "title": "string",
          +  "content": "string",
          +  "createTime": "2019-06-30T07:33:05.524Z",
          +  "posts": [
          +    {
          +      "postId": 0,
          +      "replyContent": "string",
          +      "blogId": 0,
          +      "replyTime": "2019-06-30T07:33:05.524Z"
          +    }
          +  ]
          +}
          +

          如果我们不传递createTime,会出现异常,应该createTime是DateTime,不能为null,只有DateTime?才能为null,有?为可空类型。

          所以我们应该为POST方式传递过来时,新建一个实体类,我们称之为DTO(Data Transfer Object),即数据传输对象,因为createTime即使传递,后端为他赋了值,前台传了也无效。有了DTO,这样可让前端清楚他们应该传递的参数,而不会出现没有作用的参数。

          在根目录创建Models/Blogs文件夹,在Blogs文件夹中创建

          CreateBlogDto.cs

          namespace RESTful.FreeSql.Models.Blogs
          +{
          +    public class CreateBlogDto
          +    {
          +        public string Title { get; set; }
          +        public string Content { get; set; }
          +
          +    }
          +}
          +
          +

          UpdateBlogDto.cs

          namespace RESTful.FreeSql.Models.Blogs
          +{
          +    public class UpdateBlogDto
          +    {
          +        public int BlogId { get; set; }
          +        public string Title { get; set; }
          +        public string Content { get; set; }
          +    }
          +}
          +
          +
          +

          有了Dto后,我们会发现了新的问题,往数据库插入时,往往使用了一些ORM,他们只支持原本的实体类,如Blog,Post。但不支持CreateBlogDto、UpdateBlogDto,我们可以手动,将一个类的值,赋值给另一个类。 +如

              CreateBlogDto createBlogDto = new CreateBlogDto()
          +    {
          +        Title = "我是title",
          +        Content = "我是content"
          +    };
          +
          +    Blog newBlog=new Blog()
          +    {
          +        Title = createBlogDto.Title,
          +        Content = createBlogDto.Content
          +    };
          +

          现在只是非常简单的二个属性,我们还能忍受,但如果是十个属性、而且有着大量的类与类之间的转换呢。这时修改AutoMapper就闪亮登场了。

          AutoMapper

          作用:A convention-based object-object mapper.

          我们是在ASP .NET Core下使用AutoMapper 官网介绍,如何依赖注入中使用

          Setup

          先cd到dotnetcore-examples\asp.net-core-freesql\RESTful.FreeSql目录

          PS > dotnet add package AutoMapper
          +PS > dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection --version 6.1.1
          +

          在StartUp.cs中的ConfigureServices配置如下

             public void ConfigureServices(IServiceCollection services)
          +    {
          +        // .... Ignore code before this
          +        
          +        //AddAutoMapper会去找继承Profile的类,
          +        services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
          +
          +        // .... Ignore code after this
          +    }
          +

          Adding Profiles

          AutoMapper/BlogProfile.cs

          using AutoMapper;
          +using RESTful.FreeSql.Domain;
          +using RESTful.FreeSql.Models.Blogs;
          +
          +namespace RESTful.FreeSql.AutoMapper
          +{
          +    public class BlogProfile : Profile
          +    {
          +        public BlogProfile() 
          +        {
          +            CreateMap<CreateBlogDto, Blog>();
          +            CreateMap<UpdateBlogDto, Blog>();
          +        }
          +    }
          +}
          +

          AutoMapper/BlogProfile.cs

          using AutoMapper;
          +using RESTful.FreeSql.Domain;
          +using RESTful.FreeSql.Models.Blogs;
          +
          +namespace RESTful.FreeSql.AutoMapper
          +{
          +    public class PostProfile : Profile
          +    {
          +        public PostProfile()
          +        {
          +            CreateMap<CreatePostDto,Post>();
          +        }
          +    }
          +}
          +

          Models/Posts/SearchPostDto.cs根据博客id,得到分页的评论时,集成分页类

          using RESTful.FreeSql.Web;
          +
          +namespace RESTful.FreeSql.Models.Posts
          +{
          +    public class SearchPostDto:PageDto
          +    {
          +        public int BlogId { get; set; }
          +    }
          +}
          +
          +

          Controlers/BlogController.cs文件中,注入IMapper,

          using AutoMapper;
          +using Microsoft.AspNetCore.Mvc;
          +using System;
          +using System.Collections.Generic;
          +using RESTful.FreeSql.Domain;
          +using RESTful.FreeSql.Models.Blogs;
          +using RESTful.FreeSql.Web;
          +
          +namespace RESTful.FreeSql.Controllers
          +{
          +    [Route("api/[controller]")]
          +    [ApiController]
          +    public class BlogController : ControllerBase
          +    {
          +        // GET api/Blog
          +
          +        private readonly IFreeSql _fsql;
          +        private readonly IMapper _mapper;
          +        public BlogController(IFreeSql fsql, IMapper mapper)
          +        {
          +            _fsql = fsql;
          +            _mapper = mapper;
          +        }
          +
          +        /// <summary>
          +        /// 博客列表页 
          +        /// </summary>
          +        /// <param name="pageDto">分页参数</param>
          +        /// <returns></returns>
          +        [HttpGet]
          +        public ActionResult<PagedResultDto<Blog>> Get([FromQuery]PageDto pageDto)
          +        {
          +            List<Blog> blogs = _fsql.Select<Blog>().OrderByDescending(r => r.CreateTime).Page(pageDto.PageNumber, pageDto.PageSize).ToList();
          +            long count = _fsql.Select<Blog>().Count();
          +            return new PagedResultDto<Blog>(count, blogs);
          +        }
          +
          +        // GET api/blog/5
          +        [HttpGet("{id}")]
          +        public ActionResult<Blog> Get(int id)
          +        {
          +            // eg.1 return _fsql.Select<Blog>().Where(a => a.Id == id).ToOne();
          +            // eg.2
          +            return _fsql.Select<Blog>(id).ToOne();
          +        }
          +
          +        // POST api/blog
          +        [HttpPost]
          +        public void Post([FromBody] CreateBlogDto createBlogDto)
          +        {
          +            Blog blog = _mapper.Map<Blog>(createBlogDto);
          +            blog.CreateTime = DateTime.Now;
          +            _fsql.Insert<Blog>(blog).ExecuteAffrows();
          +        }
          +
          +        // PUT api/blog
          +        [HttpPut]
          +        public void Put([FromBody] UpdateBlogDto updateBlogDto)
          +        {
          +
          +            //eg.1 更新指定列
          +            //_fsql.Update<Blog>(updateBlogDto.BlogId).Set(a => new Blog()
          +            //{
          +            //    Title = updateBlogDto.Title,
          +            //    Content = updateBlogDto.Content
          +            //}).ExecuteAffrows();
          +
          +            //eg.2将这个实体更新到数据库中。当更新时,会把其他列的值,如CreateTime也更新掉。
          +            //使用IgnoreColumns可忽略某一些列。
          +
          +            Blog blog = _mapper.Map<Blog>(updateBlogDto);
          +            _fsql.Update<Blog>().SetSource(blog).IgnoreColumns(r => r.CreateTime).ExecuteAffrows();
          +        }
          +
          +        // DELETE api/blog/5
          +        [HttpDelete("{id}")]
          +        public void Delete(int id)
          +        {
          +            _fsql.Delete<Blog>(new { BlogId = id }).ExecuteAffrows();
          +        }
          +    }
          +}
          +

          Controlers/BlogController.cs文件中,注入IMapper,

          using FreeSql;
          +using Microsoft.AspNetCore.Mvc;
          +using System;
          +using System.Collections.Generic;
          +using System.Threading.Tasks;
          +using AutoMapper;
          +using RESTful.FreeSql.Domain;
          +using RESTful.FreeSql.Models.Posts;
          +using RESTful.FreeSql.Web;
          +
          +namespace RESTful.FreeSql.Controllers
          +{
          +    [Route("api/[controller]")]
          +    [ApiController]
          +    public class PostController : ControllerBase
          +    {
          +        // GET: api/Post
          +        private readonly IFreeSql _fsql;
          +        private readonly IMapper _mapper;
          +        public PostController(IFreeSql fsql, IMapper mapper)
          +        {
          +            _fsql = fsql;
          +            _mapper = mapper;
          +        }
          +
          +        /// <summary>
          +        /// 根据博客id、分页条件查询评论信息
          +        /// </summary>
          +        /// <param name="searchPostDto"></param>
          +        /// <returns></returns>
          +        [HttpGet]
          +        public PagedResultDto<Post> Get(SearchPostDto searchPostDto)
          +        {
          +            ISelect<Post> selectPost = _fsql
          +                .Select<Post>()
          +                .Where(r => r.BlogId == searchPostDto.BlogId);
          +
          +            List<Post> posts = selectPost.OrderByDescending(r => r.ReplyTime)
          +                .Page(searchPostDto.PageNumber, searchPostDto.PageSize)
          +                .ToList();
          +
          +            long total = selectPost.Count();
          +
          +            return new PagedResultDto<Post>(total, posts);
          +        }
          +
          +        // GET: api/Post/5
          +        [HttpGet("{id}", Name = "Get")]
          +        public Post Get(int id)
          +        {
          +            return _fsql.Select<Post>().Where(a => a.PostId == id).ToOne();
          +        }
          +
          +        // POST: api/Post
          +        [HttpPost]
          +        public void Post([FromBody] CreatePostDto createPostDto)
          +        {
          +            Post post = _mapper.Map<Post>(createPostDto);
          +            post.ReplyTime = DateTime.Now;
          +            _fsql.Insert(post).ExecuteAffrows();
          +        }
          +
          +
          +        // DELETE: api/Post/
          +        [HttpDelete("{id}")]
          +        public async Task DeleteAsync(int id)
          +        {
          +            await _fsql.Delete<Post>(new Post { PostId = id }).ExecuteAffrowsAsync();
          +        }
          +    }
          +}
          +
          +

          参考

            上次更新: 10/8/2019, 1:41:22 PM
            + + + diff --git a/dotnetcore/examples/IdentityServer4.html b/dotnetcore/examples/IdentityServer4.html new file mode 100644 index 0000000..874227e --- /dev/null +++ b/dotnetcore/examples/IdentityServer4.html @@ -0,0 +1,43 @@ + + + + + + + IdentityServer4 | IGeekFan的文档 + + + + + + + + + + + + diff --git a/dotnetcore/examples/ImCore-Chat.html b/dotnetcore/examples/ImCore-Chat.html new file mode 100644 index 0000000..284a444 --- /dev/null +++ b/dotnetcore/examples/ImCore-Chat.html @@ -0,0 +1,39 @@ + + + + + + + ImCore 即时通讯 | IGeekFan的文档 + + + + + + + + + + + + diff --git a/dotnetcore/examples/Qiniu-Object-Storage.html b/dotnetcore/examples/Qiniu-Object-Storage.html new file mode 100644 index 0000000..448a9d8 --- /dev/null +++ b/dotnetcore/examples/Qiniu-Object-Storage.html @@ -0,0 +1,125 @@ + + + + + + + 七牛云对象存储 | IGeekFan的文档 + + + + + + + + +

            七牛云对象存储

            .NET Core下集成 七牛云下的对象存储

            开源地址

            https://github.com/luoyunchong/dotnetcore-examples/tree/master/asp.net-core-qiniu

            集成类库

            基础类库集成方案 开源地址 文档 说明
            Qiniu云对象存储 .net/.net core c# sdk 由于官网未支持. net core,所以 大家看社区版解决方案

            前提

            • 本地windows 10,安装 .net core 2.2+
            • Linux 服务器 Ubuntu Server 16+
            • 服务器安装了Docker
            • 本地xftp、xshell(这二个分别是windows传文件至linux,执行命令行。)

            准备

            在七牛云中自行注册后,在个人中心,密钥管理,生成自己的密钥(https://portal.qiniu.com/user/key) +v* AK,SK分别代表:AccessKey/SecretKey

            • Bucket中的vant-ui,是创建对象存储时起的名字
            • PrefixPath中的值,随意字符串,前缀地址。
            • Host为:融合 CDN 测试域名,可自行绑定自己的域名,否则只有三十天免费使用时长。

            appsettings.json配置项

              "Qiniu": {
            +    "AK": "eUH1O-ft66S4XM2GIK7FGmj7czuYkcAyNGDAc-wq",
            +    "SK": "4dOi1daSr2-YgofhAfWb8JaLrbgozCmgD6AUmmM9",
            +    "Bucket": "vant-ui",
            +    "PrefixPath": "ui",
            +    "Host": "http://pu5vnz60k.bkt.clouddn.com/"
            +  }
            +

            安装包

            Install-Package MQiniu.Core 
            +

            代码解读

            配置swagger的过程就不说了,创建QiniuController

              /// <summary>
            +    /// 七牛云上传服务
            +    /// </summary>
            +    [Route("api/[controller]")]
            +    [ApiController]
            +    public class QiniuController : ControllerBase
            +    {
            +        private readonly IConfiguration _configuration;
            +
            +        public QiniuController(IConfiguration configuration)
            +        {
            +            _configuration = configuration;
            +        }
            +
            +        /// <summary>
            +        /// 根据后台配置项,得到请求七牛云的token值,前台也可根据此token值上传至七牛云服务
            +        /// </summary>
            +        /// <returns></returns>
            +        [HttpGet("access_token")]
            +        public string GetAccessToken()
            +        {
            +            Mac mac = new Mac(_configuration["Qiniu:AK"], _configuration["Qiniu:SK"]);
            +            PutPolicy putPolicy = new PutPolicy { Scope = _configuration["Qiniu:Bucket"] };
            +            return Auth.CreateUploadToken(mac, putPolicy.ToJsonString());
            +        }
            +
            +        /// <summary>
            +        /// 上传文件至七牛云,code为200,代表上传成功,其他代表不成功
            +        /// </summary>
            +        /// <param name="file">单个文件</param>
            +        /// <returns>new { code = 200, data ="七牛云文件地址,包括http://....mm.png", msg = "上传成功" };</returns>
            +        [HttpPost("upload")]
            +        public dynamic Upload(IFormFile file)
            +        {
            +            if (file.Length == 0)
            +            {
            +                return new { code = 1, msg = "文件为空" };
            +            }
            +
            +            FormUploader upload = new FormUploader(new Config()
            +            {
            +                Zone = Zone.ZONE_CN_South,//华南 
            +                UseHttps = true
            +            });
            +
            +            var fileName = ContentDispositionHeaderValue
            +                .Parse(file.ContentDisposition)
            +                .FileName.Trim();
            +
            +            string qiniuName = _configuration["Qiniu:PrefixPath"] + "/" + DateTime.Now.ToString("yyyyMMddHHmmssffffff") + fileName;
            +            Stream stream = file.OpenReadStream();
            +            HttpResult result = upload.UploadStream(stream, qiniuName, GetAccessToken(), null);
            +
            +            if (result.Code == 200)
            +            {
            +                return new { code = 200, data = _configuration["Qiniu:Host"] + qiniuName, msg = "上传成功" };
            +            }
            +
            +            return new { code = 1, msg = "上传失败" };
            +        }
            +    }
            +
            +

            Upload方法中,Zone.ZONE_CN_South,代表华南,所以创建对象存储时要注意,请选择与此相同的位置(华南),或根据实际情况修改Zone所在地区的枚举值即可。

            FormUploader upload = new FormUploader(new Config()
            +{
            +    Zone = Zone.ZONE_CN_South,//华南 
            +    UseHttps = true
            +});
            +
            +

            发布至Linux下的Docker

            双击 publish.bat,生成的文件夹为如下:

            image

            修改项目名为qiniu-web,复制 至linux服务器中,(xftp工具) +image

            使用xshell远程登录后,进入root权限。

            root@VM-37-104-ubuntu:/home/ubuntu/# sudo su
            +root@VM-37-104-ubuntu:/home/ubuntu/# cd qiniu-web
            +

            前置条件,在ubuntu上安装好了docker。并且正常运行。

            -d 代表后台运行,此时将对外显露5000端口运行,5000是运行后,docker对外的端口,80是这个服务对外的端口,其中Dockerfile 存在语句EXPOSE 80

            docker build -t igeekfan/qiniu .     #生成images
            +docker run -d -p 5000:80 igeekfan/qiniu  # 生成 container 并运行在5000端口
            +

            此时打开 浏览器, ip+端口5000即可访问服务,请加/swagger。

            本项目已部署至服务器 http://122.152.192.161:5000/swagger/index.html

            运行结果

            image.png

            参考

              上次更新: 10/8/2019, 1:41:22 PM
              + + + diff --git a/dotnetcore/examples/index.html b/dotnetcore/examples/index.html new file mode 100644 index 0000000..8bc1af2 --- /dev/null +++ b/dotnetcore/examples/index.html @@ -0,0 +1,44 @@ + + + + + + + .NET Core 学习示例文档 | IGeekFan的文档 + + + + + + + + +

              .NET Core 学习示例文档

              浏览左侧导航菜单以深入了解文档.

              .NET Core 学习示例文档主要是结合 ASP .NET Core,集成第三方类库的示例,运用基础组件,写好Demo。

              源码

              代码托管在GitHub上 https://github.com/luoyunchong/dotnetcore-examples

              推荐阅读

              • Microsoft Docs https://docs.microsoft.com/zh-cn

                其中包含如下

                • .NET Core 指南 https://docs.microsoft.com/zh-cn/dotnet/core/
                • ASP .NET 文档 https://docs.microsoft.com/zh-cn/aspnet/
                • .NET Core CLI 文档 https://docs.microsoft.com/zh-cn/dotnet/core/tools/?tabs=netcore2x

              本地环境说明

              • Windows 10 (18922.rs_prerelease.190614-1427)
              • .NET Core 3.0.100-preview6-012264
              • Visual Studio Code 1.35.1、Microsoft Visual Studio 2019 16.1.3
              • PowerSheel
              • MySQL 5.7.25
              • Navicat Premium 12 欢迎下载

              Install

              本地开发选择SDK安装即可,还是安装 2.2的吧,3.0(19.6.29)目前还没有发布稳定版本。

              • 安装这个 https://dotnet.microsoft.com/download/dotnet-core/2.2
              • https://dotnet.microsoft.com/download/dotnet-core/3.0

              安装后,在 PowerShell 中任一目录查看安装后的版本

              PS C:\WINDOWS\system32> dotnet --version
              +3.0.100-preview6-012264
              +# 本地安装了好几个.net core sdk版本
              +PS C:\WINDOWS\system32> dotnet --list-sdks
              +2.1.700 [C:\Program Files\dotnet\sdk]
              +2.2.300 [C:\Program Files\dotnet\sdk]
              +3.0.100-preview6-012264 [C:\Program Files\dotnet\sdk]
              +

              CLI

              全称:command-line interface,命令行界面,主要是cmd、bash(sh等等)、powershell等。

              说明 所有命令行都在windows10自带的powershell中执行。

              指定SDK版本

              .NET Core 项目默认使用最新版本的 .NET Core,在根目录使用PowerShell中执行如下命令,

              语法 :dotnet new global.json --sdk-version <SDK版本号>

              dotnet new globaljson --sdk-version 2.2.300
              +

              参考

              .NET Core

              .NET Core是什么? 官网说的太高大上了 https://docs.microsoft.com/zh-cn/dotnet/core/about

              总结,.NET Core包含如下

              • 二个RunTime +
                • .NET Core RunTime:基础类型系统 、垃圾回收、基元类型等,
                • ASP .NET RunTime :提供WEB、LOT等应用程序的框架支持。
              • .NET Core CLI工具:各种命令行工具,创建项目、编译项目,发布项目等;
              • 语言编译器:(支持C#、F#、VB等语言)
              • dotnet 工具:.NET Core运行时和库的安装程序包

              三个发布包:

              • .NET Core 运行时
              • ASP .NET Core 运行时
              • .NET Core SDK:包括上面二个内容,再加上 .NET CLI工具等

              所以本地调试时,可直接安装最全的SDK即可。

              项目文档

              目前 关于此项目的文档放到docs文件夹中,zh-Hans为中文,这样可支持多语言,欢迎翻译PR,之后会发布至

              将使用abp vnext 下的modules的docs模块。不过abp vnext 现在也不稳定,0.18.1,还是有各种问题,我还是写基础模块的使用文档吧,后期完善后,发布文档网站。

              说明

              本项目也是我的学习记录,,用于测试不同类库集成的解决方案,所以用最基础的方案,命令行来创建项目,引用包,运行,测试等。让自己对 .net core 的原理结构了解地更加深入一些。

              • 采用的都是 Visual Studio Code/VS2019 +PowerShell运行,关于如何采用Visual Studio 2019创建项目,引用包是非常简单的,不再说明。
                上次更新: 10/8/2019, 1:41:22 PM
                + + + diff --git a/dotnetcore/lin-cms/IdentityServer4-JWT.html b/dotnetcore/lin-cms/IdentityServer4-JWT.html new file mode 100644 index 0000000..f5e7a26 --- /dev/null +++ b/dotnetcore/lin-cms/IdentityServer4-JWT.html @@ -0,0 +1,92 @@ + + + + + + + 认证鉴权状态 | IGeekFan的文档 + + + + + + + + +

                认证鉴权状态

                认证分为以下情况:当前角色为管理员,该分组配置了权限,该分组未分配某一方法的权限.

                状态码(StatusCode):401 UnAuthorized

                StatusCode 含义
                401 UnAuthorized 未授权、无权限、未登录
                422 UNPROCESSABLE ENTITY 令牌失效
                200 访问正常
                1. 未登录,不带access_token,直接请求需要登录的接口、管理员接口结果一样。

                返回结果应为:状态码:401 UNAUTHORIZED

                {
                +    "error_code": 10000,
                +    "msg": "认证失败,请检查请求头或者重新登陆",
                +    "request": "GET  /cms/admin/authority"
                +}
                +
                1. 携带access_token,但非超级管理员(admin字段为2),访问的方法为角色为超管才有权限的方法。
                [LinCmsAuthorize(Roles = LinGroup.Administrator)]
                +

                返回结果应为::状态码:401 UNAUTHORIZED

                {
                +    "error_code": 10000,
                +    "msg": "只有超级管理员可操作",
                +    "request": "GET  /cms/admin/authority"
                +}
                +
                1. 携带access_token ,访问只需要登录的接口(/cms/user/auths)

                控制器或方法上指定 [Authorize][LinCmsAuthorize] 特性标签时,必须登录才能访问,否则返回第一种结果。

                返回结果应为:状态码:200

                {
                +    "active": 1,
                +    "admin": 1,
                +    "auths": [
                +        {
                +            "信息": [
                +                {
                +                    "auth": "查看lin的信息",
                +                    "module": "信息"
                +                }
                +            ]
                +        }
                +    ],
                +    "avatar": null,
                +    "create_time": 1564372600000,
                +    "email": "acs@acs.com",
                +    "group_id": 54,
                +    "id": 112,
                +    "nickname": "alan",
                +    "update_time": 1564487059000
                +}
                +
                1. 携带access_token,但此用户无访问此方法的权限(即该用户的组别未配置此权限)。

                返回结果应为:状态码:401 UNAUTHORIZED

                {
                +    "error_code": 10000,
                +    "msg": "权限不够,请联系超级管理员获得权限",
                +    "request": "GET  /cms/log/search"
                +}
                +
                1. 携带过期的access_token值 +返回结果应为:状态码:401 UNAUTHORIZED
                {
                +    "error_code": 10050,
                +    "msg": "令牌过期",
                +    "request": "GET  /cms/admin/users"
                +}
                +
                1. 携带不正常的access_token值,后台无法下正常解析出用户信息 +返回结果应为:状态码:422 UNPROCESSABLE ENTITY
                {
                +    "error_code": 10040,
                +    "msg": "令牌失效",
                +    "request": "GET  /cms/admin/users"
                +}
                +
                  上次更新: 10/8/2019, 1:41:22 PM
                  + + + diff --git a/dotnetcore/lin-cms/Newtonsoft.Json-question.html b/dotnetcore/lin-cms/Newtonsoft.Json-question.html new file mode 100644 index 0000000..3f999b8 --- /dev/null +++ b/dotnetcore/lin-cms/Newtonsoft.Json-question.html @@ -0,0 +1,160 @@ + + + + + + + Newtonsoft.Json基础问题 | IGeekFan的文档 + + + + + + + + +

                  Newtonsoft.Json基础问题

                  它是.NET下的一个序列化、反序化的基础类库,更基础的用法还是看别人的吧,这里只说一些遇到的问题。

                  设置下划线

                  ContractResolver 默认是小驼峰,我想改成下划线方式,遇到了一些问题,dictionary的键未格式化

                  in controller 创建一个控制器

                  [HttpGet("getDictionary")]
                  +public IDictionary<string, string> GetDictionary()
                  +{
                  +    IDictionary<string, string> dics = new Dictionary<string, string>();
                  +
                  +    dics.Add("Key", "Value");
                  +    dics.Add("KeyTest", "Value_Test");
                  +    return dics;
                  +}
                  +

                  In Startup.cs

                  services
                  +.AddMvc()
                  +.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                  +.AddJsonOptions(opt =>
                  +{
                  +    
                  +    // 设置下划线方式,首字母是小写
                  +    opt.SerializerSettings.ContractResolver = new DefaultContractResolver()
                  +    {
                  +        NamingStrategy = new SnakeCaseNamingStrategy()
                  +    };
                  +});
                  +

                  此时运行后,得到的是Key,而不是key,我想他的键都变成下划线方式的小写

                  {
                  +  "Key": "Value",
                  +  "KeyTest": "Value_Test"
                  +}
                  +

                  其他测试,增加多级,测试正常

                  [HttpGet("get")]
                  +public dynamic Get()
                  +{
                  + return new {
                  +            Content = new {
                  +                Url=Request.Path.Value,
                  +                NewUrlTest="test in new url test"
                  +            }
                  +        };
                  +}
                  +

                  此时运行后,满足要求,多层结构也不会影响

                  {
                  +  "content": {
                  +    "url": "/test/get",
                  +    "new_url_test": "test in new url test"
                  +  }
                  +}
                  +
                  +

                  看了Newtonsoft.Json的github,并在in this repository 搜索Dictionary,看issues中的配置项如下即可满足dictionary的键也转小写, +ProcessDictionaryKeys 功能:A flag indicating whether dictionary keys should be processed. Defaults to false.

                  services
                  +.AddMvc()
                  +.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                  +.AddJsonOptions(opt =>
                  +{
                  +    
                  +    // 设置下划线方式,首字母是小写
                  +    opt.SerializerSettings.ContractResolver = new DefaultContractResolver()
                  +    {
                  +        NamingStrategy = new SnakeCaseNamingStrategy
                  +        {
                  +            ProcessDictionaryKeys = true
                  +        }
                  +    };
                  +});
                  +
                  {
                  +  "key": "Value",
                  +  "key_test": "Value_Test"
                  +}
                  +

                  实现时间戳

                  前台要的格式为 1562904163734,只有一个数字,我搜索了一下,也没找到相关的文档,本身这个类库有一些时间戳,不过他们都包含特殊字符,如/Date(1562904163734)/,好像类似这样,他好像在逗我,为啥他要加Date,怕是有毒吧。看到 +他有Converters属性可配置,即配置自己的序列化返回格式。

                  services.AddMvc()
                  +.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
                  +.AddJsonOptions(opt =>
                  +{
                  +    //opt.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:MM:ss";
                  +    //设置时间戳格式
                  +    opt.SerializerSettings.Converters = new List<JsonConverter>()
                  +    {
                  +        new LinCmsTimeConverter()
                  +    };
                  +});
                  +

                  这里的时间戳是毫秒级别

                  /// <summary>
                  +/// 配合LinCMS中的时间戳 后台只返回 1562904163734
                  +/// </summary>
                  +public class LinCmsTimeConverter : DateTimeConverterBase
                  +{
                  +    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
                  +    {
                  +        double javaScriptTicks = 0;
                  +        if (value is DateTime dateTime)
                  +        {
                  +            javaScriptTicks = ConvertDateTimeInt(dateTime);
                  +        }
                  +        else
                  +        {
                  +            if (!(value is DateTimeOffset dateTimeOffset))
                  +                throw new JsonSerializationException("Expected date object value.");
                  +            javaScriptTicks = ConvertDateTimeInt(dateTimeOffset.ToUniversalTime().UtcDateTime);
                  +
                  +        }
                  +        writer.WriteValue(javaScriptTicks);
                  +    }
                  +
                  +    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
                  +    {
                  +        return ConvertIntDateTime(double.Parse(reader.Value.ToString()));
                  +    }
                  +    
                  +    /// <summary>
                  +    /// 日期转换为时间戳(时间戳单位毫秒)
                  +    /// </summary>
                  +    /// <param name="TimeStamp"></param>
                  +    /// <returns></returns> 
                  +    public static DateTime ConvertIntDateTime(double milliseconds)
                  +    {
                  +        return new DateTime(1970, 1, 1).AddMilliseconds(milliseconds);
                  +    }
                  +
                  +
                  +    public static double ConvertDateTimeInt(DateTime aDt)
                  +    {
                  +        return (aDt - new DateTime(1970, 1, 1)).TotalMilliseconds;
                  +    }
                  +}
                  +

                  参考

                    上次更新: 10/8/2019, 1:41:22 PM
                    + + + diff --git a/dotnetcore/lin-cms/Open-source-road.html b/dotnetcore/lin-cms/Open-source-road.html new file mode 100644 index 0000000..1ec2c97 --- /dev/null +++ b/dotnetcore/lin-cms/Open-source-road.html @@ -0,0 +1,48 @@ + + + + + + + 开源lin-cms-dotnetcore | IGeekFan的文档 + + + + + + + + +

                    开源lin-cms-dotnetcore

                    • 地址:https://github.com/luoyunchong/lin-cms-dotnetcore

                    1.什么是cms?

                    Content Management System,内容管理系统。

                    2.dotnetcore是什么

                    .NET Core,是由Microsoft开发,目前在.NET Foundation(一个非营利的开源组织)下进行管理,采用宽松的MIT协议,可构建各种软件,包括Web应用程序、移动应用程序、桌面应用程序、云服务、微服务、API、游戏和物联网应用程序

                    3.lin-cms 是什么

                    Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套内容管理系统框架。Lin-CMS 可以有效的帮助开发者提高 CMS 的开发效率, 需要前端?请访问前端仓库。官方团队产品了解请访问TaleLin

                    4.lin-cms-dotnetcore有哪些特点?

                    前后端分离,提供后端接口,更少的依赖,后续将实现模块化安装与卸载。

                    • 用户管理、分组管理、分组权限管理、日志系统、文件上传等
                    • 更多功能(自定义扩展-模块系统)

                    设计如下

                    字典管理

                    我原本想实现这样的功能:

                    BaseType 1 对BaseItem多。

                    如:标签管理,一个文章下可以设置多个标签,原本需要设计表Tag,字段也大抵为Id,Name,Sort及关联表。 +我们使用BaseType、BaseItem实现。 +BaseType中TypeCode为tag,FullName为标签,id为1时。 +BaseItem中BaseTypeId为1,ItemCode为编码,ItemName为标签。ItemCode为不重复的字符串即可。

                    另Tag与Article的关联表,需要另设计一个表。

                    当我们要实现文章类别的下拉,原本需要设计一个表ArticleType,有字段,id,name,sort等。 +我们可以通过BaseType、BaseItem来实现,从而简化这些基础数据。 +BaseType有一条数据,TypeCode为 字符串category、FullName文章类别,BaseItem存多个文章类别(Java、大数据、Python、C#等),编码不同即可。

                    1. 表结构

                    base_type (字典类别管理)

                    字段 类型 备注
                    id int
                    type_code varchar(50) 类别编码
                    full_name varchar(50) 全称
                    sort_code int 排序码

                    base_type (字典管理)

                    字段 类型 备注
                    id int
                    base_type_id int 类别id(关联base_type的id)
                    item_code varchar(50) 字典编码
                    item_name varchar(50) 字典全称
                    sort_code int 排序码

                    但现实总是事与愿违

                    后台取文章列表时,想要取出文章对应的分类,手动join时,总觉得join的表会有些奇怪。

                    当然还有其他原因,局限性

                    1. 比如使用了这个字典,分类需要增加一个图片字段,就不能满足要求,那怎么办呢,做不了。
                    2. 比如标签需要实现这个标签下有多少个文章,通过统计也能实现,

                    使用FreeSql实现时,如果二个表之间没有导航属性,是更复杂的。

                    前端cms

                    扩展实现一个博客,项目地址:https://github.com/luoyunchong/lin-cms-vue

                      上次更新: 12/9/2019, 7:14:35 PM
                      + + + diff --git a/dotnetcore/lin-cms/Reflex-Assembly-Get-Controller-Methods-Attribute.html b/dotnetcore/lin-cms/Reflex-Assembly-Get-Controller-Methods-Attribute.html new file mode 100644 index 0000000..100e4a6 --- /dev/null +++ b/dotnetcore/lin-cms/Reflex-Assembly-Get-Controller-Methods-Attribute.html @@ -0,0 +1,166 @@ + + + + + + + 获取控制器及方法特性标签 | IGeekFan的文档 + + + + + + + + +

                      获取控制器及方法特性标签

                      .NET Core 反射获取所有控制器及方法上特定标签.

                      有个需求,就是在. NET Core中,我们想在项目 启动时,获取LinCmsAuthorizeAttribute这个特性标签所有出现的地方,把他的参数,放入一个集合并缓存起来,以便后面使用此数据用于权限验证。

                      我们通过反射获取所有控制器下及方法的Attribute。

                      LinCmsAuthorizeAttribute是什么

                      其代码非常简单,用于自定义权限验证,通过重写OnAuthorizationAsync方法,实现固定权限可分配给动态角色(也能分配给动态用户)。主要就基于权限的授权的实现进行研究,实现方法级别的权限验证。

                      当然,这个只是部分代码,完整代码请查看最下方开源地址,其中LinCmsAuthorizeAttribute继承AuthorizeAttribute,拥有指定角色权限控制,当Permission未指定时,当过滤器与Authorize功能相同。Module是指模块,即多个权限,属于同一个模块,方便前台展示为树型结构。Permission属性的值不可重复。

                      [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
                      +public class LinCmsAuthorizeAttribute : AuthorizeAttribute, IAsyncAuthorizationFilter
                      +{
                      +    public string Permission { get; set; }
                      +    public string Module { get; set; }
                      +
                      +    public LinCmsAuthorizeAttribute()
                      +    {
                      +
                      +    }
                      +
                      +    public LinCmsAuthorizeAttribute(string permission,string module)
                      +    {
                      +        Permission = permission;
                      +        Module = module;
                      +    }
                      +
                      +    public LinCmsAuthorizeAttribute(string permission,string module, string policy) : base(policy)
                      +    {
                      +        Permission = permission;
                      +        Module = module;
                      +    }
                      +
                      +    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
                      +    {
                      +        if (Permission == null) return;
                      +        var authorizationService = (IAuthorizationService)context.HttpContext.RequestServices.GetService(typeof(IAuthorizationService));
                      +        var authorizationResult = await authorizationService.AuthorizeAsync(context.HttpContext.User, null, new OperationAuthorizationRequirement() { Name = Permission });
                      +        if (!authorizationResult.Succeeded)
                      +        {
                      +            context.Result = new ForbidResult();
                      +        }
                      +    }
                      +
                      +    public override string ToString()
                      +    {
                      +        return $"\"{base.ToString()}\",\"Permission:{Permission}\",\"Module:{Module}\",\"Roles:{Roles}\",\"Policy:{Policy}\",\"AuthenticationSchemes:{AuthenticationSchemes}\"";
                      +    }
                      +}
                      +

                      Controller

                      在 LinCms.Web中的Controller,至于为什么Permission为中文,目前的主要原因,此项目用于适配 Lin-CMS-VUE项目,所以于平常我们以某个字符串作为权限名不同,但不须大惊小怪,道理相同。

                      [Route("cms/log")]
                      +[ApiController]
                      +public class LogController : ControllerBase
                      +{
                      +    private readonly ILogService _logService;
                      +
                      +    public LogController(ILogService logService)
                      +    {
                      +        _logService = logService;
                      +    }
                      +
                      +    [HttpGet("users")]
                      +    [LinCmsAuthorize("查询日志记录的用户", "日志")]
                      +    public List<string> GetLoggedUsers([FromQuery]PageDto pageDto)
                      +    {
                      +        return _logService.GetLoggedUsers(pageDto);
                      +    }
                      +
                      + 
                      +    [HttpGet]
                      +    [LinCmsAuthorize("查询所有日志", "日志")]
                      +    public PagedResultDto<LinLog> GetLogs([FromQuery]LogSearchDto searchDto)
                      +    {
                      +        return _logService.GetLogUsers(searchDto);
                      +    }
                      +
                      +    [HttpGet("search")]
                      +    [LinCmsAuthorize("搜索日志", "日志")]
                      +    public PagedResultDto<LinLog> SearchLogs([FromQuery]LogSearchDto searchDto)
                      +    {
                      +        return _logService.GetLogUsers(searchDto);
                      +    }
                      +}
                      +

                      测试类获取方法上的特定标签

                      in xunit test 项目工程中,开始我们的测试

                      [Fact]
                      +public void GetAssemblyMethodsAttributes()
                      +{
                      +    var assembly = typeof(Startup).Assembly.GetTypes().AsEnumerable()
                      +        .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToList();
                      +
                      +    assembly.ForEach(r =>
                      +    {
                      +        foreach (var methodInfo in r.GetMethods())
                      +        {
                      +            foreach (Attribute attribute in methodInfo.GetCustomAttributes())
                      +            {
                      +                if (attribute is LinCmsAuthorizeAttribute linCmsAuthorize)
                      +                {
                      +                    _testOutputHelper.WriteLine(linCmsAuthorize.ToString());
                      +                }
                      +            }
                      +        }
                      +    });
                      +}    
                      +

                      方法结果

                      可在输出文本中查看,正是我们想要的东西,最后一行,是其他Controller中的内容,而且我们重写了ToString(),所以我们能看到其属性。

                      "LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:查询日志记录的用户","Module:日志","Roles:","Policy:","AuthenticationSchemes:"
                      +"LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:查询所有日志","Module:日志","Roles:","Policy:","AuthenticationSchemes:"
                      +"LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:搜索日志","Module:日志","Roles:","Policy:","AuthenticationSchemes:"
                      +"LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:查看lin的信息","Module:信息","Roles:","Policy:","AuthenticationSchemes:"
                      +
                      +

                      获取控制器上特性标签

                      /// <summary>
                      +/// 获取控制器上的LinCmsAuthorizeAttribute
                      +/// </summary>
                      +/// "LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:","Module:","Roles:Administrator","Policy:","AuthenticationSchemes:"
                      +[Fact]
                      +public void GetControllerAttributes()
                      +{
                      +    var assembly = typeof(Startup).Assembly.GetTypes().AsEnumerable()
                      +        .Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToList();
                      +
                      +    assembly.ForEach(d =>
                      +    {
                      +        var linCmsAuthorize = d.GetCustomAttribute<LinCmsAuthorizeAttribute>();
                      +        if (linCmsAuthorize != null)
                      +        {
                      +            _testOutputHelper.WriteLine(linCmsAuthorize.ToString());
                      +        }
                      +    });
                      +}
                      +

                      Controller结果

                      只有AdminController加了此标签,所以只有一行。

                      "LinCms.Zero.Authorization.LinCmsAuthorizeAttribute","Permission:","Module:","Roles:Administrator","Policy:","AuthenticationSchemes:"
                      +

                      此时Roles为Administrator,Permission及Module都是null, +这是因为只有AdminController中加了LinGroup.Administrator="Administrator"字符串,在登录过程中,已经给当前登录用户设置了 new Claim(ClaimTypes.Role,user.IsAdmin()?LinGroup.Administrator:user.GroupId.ToString()),即"Administrator,当用户访问AdminController中的方法时,LinCmsAuthorize并没有做相关验证,都是AuthorizeAttribute,实现了固定角色权限的判断及登录的判断。LinCmsAuthorize完成了固定权限设置为不同的动态角色后,判断用户是否拥有此权限。

                      [LinCmsAuthorize(Roles = LinGroup.Administrator)]
                      +public class AdminController : ControllerBase
                      +{
                      +    ...
                      +}
                      +

                      参考

                      开源地址

                        上次更新: 10/8/2019, 1:41:22 PM
                        + + + diff --git a/dotnetcore/lin-cms/StopWords.html b/dotnetcore/lin-cms/StopWords.html new file mode 100644 index 0000000..b28cabe --- /dev/null +++ b/dotnetcore/lin-cms/StopWords.html @@ -0,0 +1,194 @@ + + + + + + + 全局敏感词处理 | IGeekFan的文档 + + + + + + + + +

                        全局敏感词处理

                        基于ToolGood.Words类库,配合敏感字的文本文件,写的API接口。

                        一共二种方式

                        1.ToolGood.Words

                        类库配合敏感库

                        简单用法

                        [Fact]
                        +public void IssuesTest_17()
                        +{
                        +    var illegalWordsSearch = new IllegalWordsSearch();
                        +    string s = "中国|zg人|abc";
                        +    illegalWordsSearch.SetKeywords(s.Split('|'));
                        +    var str = illegalWordsSearch.Replace("我是中美国人厉害中国完美abcddb好的", '*');
                        +
                        +    Assert.Equal("我是中美国人厉害**完美***ddb好的", str);
                        +}
                        +

                        配合敏感库文本文件,写的工具类

                        二个文件放到wwwroot/_Illegal目录下,通过ReplaceStopWords方法调用即可对

                        public class ToolGoodUtils
                        +{
                        +    //敏感库只要这二个文件存在即可
                        +    //本地敏感库缓存-https://github.com/toolgood/ToolGood.Words/tree/master/csharp/ToolGood.Words.Test/_Illegal
                        +    //因为需要上传至github并同步gitee,安全起见,所以未上传至git,需要自行下载并复制
                        +    private const string KeywordsPath = "wwwroot/_Illegal/IllegalKeywords.txt";
                        +    private const string UrlsPath = "wwwroot/_Illegal/IllegalUrls.txt";
                        +
                        +    private const string InfoPath = "wwwroot/_Illegal/IllegalInfo.txt";
                        +    private const string BitPath = "wwwroot/_Illegal/IllegalBit.iws";
                        +
                        +    private static IllegalWordsSearch _search;
                        +    /// <summary>
                        +    /// 本地敏感库,文件修改后,重新创建缓存Bit
                        +    /// </summary>
                        +    /// <returns></returns>
                        +    public static IllegalWordsSearch GetIllegalWordsSearch()
                        +    {
                        +        if (_search == null)
                        +        {
                        +            string ipath = Path.GetFullPath(InfoPath);
                        +            if (File.Exists(ipath) == false)
                        +            {
                        +                _search = CreateIllegalWordsSearch();
                        +            }
                        +            else
                        +            {
                        +                var texts = File.ReadAllText(ipath).Split('|');
                        +                if (new FileInfo(Path.GetFullPath(KeywordsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") !=
                        +                    texts[0] ||
                        +                    new FileInfo(Path.GetFullPath(UrlsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") !=
                        +                    texts[1]
                        +                )
                        +                {
                        +                    _search = CreateIllegalWordsSearch();
                        +                }
                        +                else
                        +                {
                        +                    var s = new IllegalWordsSearch();
                        +                    s.Load(Path.GetFullPath(BitPath));
                        +                    _search = s;
                        +                }
                        +            }
                        +        }
                        +        return _search;
                        +    }
                        +
                        +    private static IllegalWordsSearch CreateIllegalWordsSearch()
                        +    {
                        +        string[] words1 = File.ReadAllLines(Path.GetFullPath(KeywordsPath), Encoding.UTF8);
                        +        string[] words2 = File.ReadAllLines(Path.GetFullPath(UrlsPath), Encoding.UTF8);
                        +        var words = new List<string>();
                        +        foreach (var item in words1)
                        +        {
                        +            words.Add(item.Trim());
                        +        }
                        +        foreach (var item in words2)
                        +        {
                        +            words.Add(item.Trim());
                        +        }
                        +
                        +        var search = new IllegalWordsSearch();
                        +        search.SetKeywords(words);
                        +
                        +        search.Save(Path.GetFullPath(BitPath));
                        +
                        +        var text = new FileInfo(Path.GetFullPath(KeywordsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") + "|"+ new FileInfo(Path.GetFullPath(UrlsPath)).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss");
                        +        File.WriteAllText(Path.GetFullPath(InfoPath), text);
                        +
                        +        return search;
                        +    }
                        +}
                        +

                        2. 循环使用Replace

                        方案:通过维护敏感库,循环replace +大佬分享给我的,稍微改成了从文件中获取敏感字。

                        public static class StopWords
                        +{
                        +
                        +    static readonly ConcurrentDictionary<string, bool> FunNlpDataSensitive = new ConcurrentDictionary<string, bool>();
                        +    static readonly ConcurrentDictionary<int, string> ReplaceNewValue = new ConcurrentDictionary<int, string>();
                        +
                        +    private const string KeywordsPath = "wwwroot/_Illegal/IllegalKeywords.txt";
                        +    private const string UrlsPath = "wwwroot/_Illegal/IllegalUrls.txt";
                        +
                        +
                        +    static StopWords()
                        +    {
                        +        LoadDataFromFile();
                        +    }
                        +
                        +    public static void LoadDataFromFile()
                        +    {
                        +        string words1 = File.ReadAllText(Path.GetFullPath(KeywordsPath), Encoding.UTF8);
                        +        string words2 = File.ReadAllText(Path.GetFullPath(UrlsPath), Encoding.UTF8);
                        +        LoadDataFromText(words1);
                        +        LoadDataFromText(words2);
                        +    }
                        +
                        +
                        +    public static void LoadDataFromText(string text)
                        +    {
                        +        int oldcount = FunNlpDataSensitive.Count;
                        +        foreach (string wd in text.Split('\n'))
                        +        {
                        +            string keykey = wd.Trim().Trim('\r', '\n').Trim();
                        +            if (string.IsNullOrEmpty(keykey)) continue;
                        +            FunNlpDataSensitive.TryAdd(keykey, true);
                        +            if (ReplaceNewValue.ContainsKey(keykey.Length) == false)
                        +                ReplaceNewValue.TryAdd(keykey.Length, "".PadRight(keykey.Length, '*'));
                        +        }
                        +        Console.WriteLine($"敏感词加载完毕,增加数量:{FunNlpDataSensitive.Count - oldcount}");
                        +    }
                        +
                        +
                        +    /// <summary>
                        +    /// 替换所有敏感词为 *
                        +    /// </summary>
                        +    /// <param name="that"></param>
                        +    /// <returns></returns>
                        +    public static string ReplaceStopWords(this string that)
                        +    {
                        +        foreach (var wd in FunNlpDataSensitive.Keys)
                        +            that = that.Replace(wd, ReplaceNewValue.TryGetValue(wd.Length, out var tryval) ? tryval : "".PadRight(wd.Length, '*'));
                        +        return that;
                        +    }
                        +}
                        +

                        FreeSql全局处理敏感词

                        使用FreeSql这个ORM时,全局处理string类型的值,进行敏感词处理。代码在StartUp.cs的构造函数中。

                        //敏感词处理
                        +IllegalWordsSearch illegalWords = ToolGoodUtils.GetIllegalWordsSearch();
                        +
                        +Fsql.Aop.AuditValue += (s, e) =>
                        +{
                        +    if (e.Column.CsType == typeof(string) && e.Value != null)
                        +    {
                        +        string oldVal = (string)e.Value;
                        +        string newVal = illegalWords.Replace(oldVal);
                        +        //第二种处理敏感词的方式
                        +        //string newVal = oldVal.ReplaceStopWords();
                        +        if (newVal != oldVal)
                        +        {
                        +            e.Value = newVal;
                        +        }
                        +    }
                        +};
                        +
                          上次更新: 12/9/2019, 7:14:35 PM
                          + + + diff --git a/dotnetcore/lin-cms/dependency-injection.html b/dotnetcore/lin-cms/dependency-injection.html new file mode 100644 index 0000000..72e4376 --- /dev/null +++ b/dotnetcore/lin-cms/dependency-injection.html @@ -0,0 +1,121 @@ + + + + + + + 依赖注入 | IGeekFan的文档 + + + + + + + + +

                          依赖注入

                          官网介绍

                          https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2

                          Scrutor

                          在ConfigServices中,我们原本需要这样子依次注入仓储,服务和其他接口及实现,当有多个仓储时,这样就过于繁琐。

                          services.AddTransient<IUserRepository, UserRepository>();
                          +services.AddTransient<IUserService, UserService>();
                          +services.AddTransient<ICurrentUser, CurrentUser>();
                          +

                          Serivce后缀服务注入DI

                          当我们有多个Service后缀的服务时,使用以下方法,可将服务扫描只留下以Serivce结尾的类,将其类型注册为提供所有公共接口生成服务,其生命周期为Transient,

                          services.Scan(scan => scan
                          +        //加载Startup这个类所在的程序集
                          +        .FromAssemblyOf<Startup>()
                          +        // 表示要注册那些类,上面的代码还做了过滤,只留下了以 Service 结尾的类
                          +        .AddClasses(classes => classes.Where(t => t.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase)))
                          +        //表示将类型注册为提供其所有公共接口作为服务
                          +        .AsImplementedInterfaces()
                          +        //表示注册的生命周期为 Transient
                          +        .WithTransientLifetime()
                          +         );
                          +
                          +

                          ITransientDependency

                          新建一个空接口,当其他类继承此接口后,统一注入到DI中,以Transient的生命周期。

                          namespace LinCms.Zero.Dependency
                          +{
                          +    public interface ITransientDependency
                          +    {
                          +    }
                          +}
                          +

                          接口

                          public interface ICurrentUser
                          +{
                          +    int? Id { get; }
                          +
                          +    int? GroupId { get; }
                          +
                          +    bool? IsAdmin { get; }
                          +}
                          +

                          模拟实现

                              public class CurrentUser : ICurrentUser, ITransientDependency
                          +    {
                          +     
                          +        public int? Id => 1;
                          +        public int? GroupId => 2;
                          +        public bool? IsAdmin => true;
                          +    }
                          +

                          扫描所有继承ITransientDependency的实现。

                             services.Scan(scan => scan
                          +       // We start out with all types in the assembly of ITransientService
                          +        .FromAssemblyOf<ITransientDependency>()
                          +        // AddClasses starts out with all public, non-abstract types in this assembly.
                          +        // These types are then filtered by the delegate passed to the method.
                          +        // In this case, we filter out only the classes that are assignable to ITransientService.
                          +        .AddClasses(classes => classes.AssignableTo<ITransientDependency>())
                          +        // We then specify what type we want to register these classes as.
                          +        // In this case, we want to register the types as all of its implemented interfaces.
                          +        // So if a type implements 3 interfaces; A, B, C, we'd end up with three separate registrations.
                          +        .AsImplementedInterfaces()
                          +        // And lastly, we specify the lifetime of these registrations.
                          +        .WithTransientLifetime()
                          +         );
                          +
                          +

                          如何使用

                          在其他类中使用此接口

                          [ApiController]
                          +[Route("cms/user")]
                          +public class UserController : ControllerBase
                          +{
                          +    private readonly ICurrentUser _currentUser;
                          +
                          +    public UserController(ICurrentUser currentUser)
                          +    {
                          +        _currentUser = currentUser;
                          +    }
                          +
                          +    [HttpGet]
                          +    public int GetUser()
                          +    {
                          +        return _currentUser.Id;
                          +    }
                          +}
                          +

                          统一注入

                          当然,我们可以统一注入,而非写二次servics.Scan

                          services.Scan(scan => scan
                          +            .FromAssemblyOf<Startup>()
                          +            .AddClasses(classes => classes.Where(t => t.Name.EndsWith("Service",StringComparison.OrdinalIgnoreCase)))
                          +            .AsImplementedInterfaces()
                          +            .WithTransientLifetime()
                          +            .FromAssemblyOf<ITransientDependency>()
                          +            .AddClasses(classes => classes.AssignableTo<ITransientDependency>())
                          +            .AsImplementedInterfaces()
                          +            .WithTransientLifetime()
                          +      );
                          +
                            上次更新: 10/8/2019, 1:41:22 PM
                            + + + diff --git a/dotnetcore/lin-cms/dev-start.html b/dotnetcore/lin-cms/dev-start.html new file mode 100644 index 0000000..99db850 --- /dev/null +++ b/dotnetcore/lin-cms/dev-start.html @@ -0,0 +1,43 @@ + + + + + + + 开发起步 | IGeekFan的文档 + + + + + + + + +
                            + + + diff --git a/dotnetcore/lin-cms/dotnetcore-start.html b/dotnetcore/lin-cms/dotnetcore-start.html new file mode 100644 index 0000000..55c7c42 --- /dev/null +++ b/dotnetcore/lin-cms/dotnetcore-start.html @@ -0,0 +1,49 @@ + + + + + + + 后端准备 | IGeekFan的文档 + + + + + + + + +

                            后端准备

                            Server 端必备环境

                            获取工程项目

                            git clone https://github.com/luoyunchong/lin-cms-dotnetcore.git
                            +

                            数据库配置

                            文件位置src/LinCms.Web/appsettings.json,当数据库中存储表情包是,Charset为utf8mb4

                            请务必根据自己的实际情况修改此配置项

                            "ConnectionStrings": {
                            +    "Default": "Data Source=127.0.0.1;Port=3306;User ID=root;Password=123456;Initial Catalog=LinCms;Charset=utf8;SslMode=none;Max pool size=10"
                            +}
                            +

                            数据迁移

                            该项目使用FreeSql,默认自动迁移数据表结构,需要自己创建数据库,名字为LinCms,无须用户操作,但无数据,而且只有访问到表时才会创建某个表,所以用户可将备份SQL放到Mysql中生成,还原表结构及数据。

                            1.vscode命令行运行

                            使用vscode打开项目,

                            切换到LinCms.Web目录

                             D:\code\github\lin-cms-dotnetcore> cd src\LinCms.Web
                            +

                            编译、安装依赖包

                            D:\code\github\lin-cms-dotnetcore\src\LinCms.Web> dotnet build
                            +

                            运行

                            D:\code\github\lin-cms-dotnetcore\src\LinCms.Web> dotnet run watch
                            +Now listening on: https://localhost:5001
                            +Now listening on: http://localhost:5000
                            +Application started. Press Ctrl+C to shut down.
                            +

                            这时候打开 https://localhost:5001/swagger/index.html,即可看到swagger页面。

                            2.visual studio 2019运行

                            双击lin-cms-dotnetcore.sln即可使用vs2019打开项目。右键解决方案,点击生成解决方案,然后,再单击LinCms .Web,即可自动启动后台服务。

                            会打开浏览器,访问https://localhosst:5001/swagger/index.html,会看到swagger的文档。

                            部署至linux(Ubuntu16.06)

                            部署至Docker

                              上次更新: 11/28/2019, 7:52:40 PM
                              + + + diff --git a/dotnetcore/lin-cms/dynamic-authorization-in-asp-net-core.html b/dotnetcore/lin-cms/dynamic-authorization-in-asp-net-core.html new file mode 100644 index 0000000..2ca8cf2 --- /dev/null +++ b/dotnetcore/lin-cms/dynamic-authorization-in-asp-net-core.html @@ -0,0 +1,43 @@ + + + + + + + 认证与授权 | IGeekFan的文档 + + + + + + + + +

                              认证与授权

                              主要就基于权限的授权的实现进行研究,实现方法级别的权限验证。

                              认证鉴权相关

                              IdentityServer4

                              JWT

                              json web token

                                上次更新: 10/8/2019, 1:41:22 PM
                                + + + diff --git a/dotnetcore/lin-cms/error-code.html b/dotnetcore/lin-cms/error-code.html new file mode 100644 index 0000000..db1d3eb --- /dev/null +++ b/dotnetcore/lin-cms/error-code.html @@ -0,0 +1,35 @@ + + + + + + + code | IGeekFan的文档 + + + + + + + + + + + + diff --git a/dotnetcore/lin-cms/index.html b/dotnetcore/lin-cms/index.html new file mode 100644 index 0000000..374f874 --- /dev/null +++ b/dotnetcore/lin-cms/index.html @@ -0,0 +1,40 @@ + + + + + + + IGeekFan的文档 + + + + + + + + +

                                + Lin CMS .NET Core +

                                +😃 A simple and practical CMS implememted by .NET Core 2.2 +

                                项目简介

                                Build Status

                                本项目是完全出于个人喜爱,看到Lin-cms有了python,node.js,社区也有人出了lin-cms-tp5的版本

                                本项目是 Lin CMS 后端的 .NET Core 2.2的 实现,

                                什么是 Lin CMS?

                                Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套内容管理系统框架。Lin-CMS 可以有效的帮助开发者提高 CMS 的开发效率, 需要前端?请访问前端仓库。官方团队产品了解请访问TaleLin

                                线上文档地址(完善中)

                                https://luoyunchong.github.io/vuepress-docs/dotnetcore/lin-cms/

                                线上 Demo

                                前端

                                Lin CMS 的特点

                                Lin CMS 的构筑思想是有其自身特点的。下面我们阐述一些 Lin 的主要特点。

                                Lin CMS 是一个前后端分离的 CMS 解决方案

                                这意味着,Lin 既提供后台的支撑,也有一套对应的前端系统,

                                首先,传统的网站开发更多的是采用服务端渲染的方式,需用使用一种模板语言在服务端完成页面渲染:比如 Razor等模板技术。

                                服务端渲染的好处在于可以比较好的支持 SEO,但作为内部使用的 CMS 管理系统,SEO 并不重要。

                                但一个不可忽视的事实是,服务器渲染的页面到底是由前端开发者来完成,还是由服务器开发者来完成?其实都不太合适。现在已经没有多少前端开发者是了解这些服务端模板语言的,而服务器开发者本身是不太擅长开发页面的。那还是分开吧,前端用最熟悉的 Vue 写 JS 和 CSS,而服务器只关注自己的 API 即可。

                                其次,单页面应用程序的体验本身就要好于传统网站。

                                框架本身已内置了 CMS 常用的功能

                                Lin 已经内置了 CMS 中最为常见的需求:用户管理、权限管理、日志系统等。开发者只需要集中精力开发自己的 CMS 业务即可

                                更多关于Lin CMS的介绍请访问Lin CMS线上文档

                                所需基础

                                由于 Lin 采用的是前后端分离的架构,所以你至少需要熟悉 C# 和 Vue。

                                后端 C#

                                该项目的Lin 的服务端框架是基于.NET Core 2.2构建的,所以如果你比较熟悉Mvc、WebAPI、过滤器等概念,或者是 有.NET Framework中Mvc开发经验,相信你一定很容易写出代码。

                                后端主要技术

                                • 数据库:FreeSql+MySQL5.6
                                • ASP.NET Core2.2+MVC
                                • 简化对象映射:AutoMapper
                                • 身份认证框架:IdentityServer4
                                • Json Web令牌:JWT
                                • 文档:Swagger
                                • 序列化:Newtonsoft.Json
                                • 测试框架:Xunit
                                • 日志 NLog
                                • 简化注入服务:Scrutor
                                • 通用扩展方法 Z.ExtensionMethods

                                前端

                                前端需要开发者比较熟悉 Vue 的,另外需要了解 ES6,axios,ElementUi、webpack、Vuex、Vue-Router等等等

                                讨论交流

                                QQ 交流群

                                • 林间有风(lin-cms-vue) QQ 群号:643205479
                                • .NET Core搬砖队(lin-cms-dotnetcore) QQ群号:762828442

                                微信公众号

                                微信搜索:林间有风

                                下个版本开发计划

                                1. 迁移Mock.Luo项目至lin-cms-dotnetcore,
                                2. 实现模块化开发,支持基础组件安装与卸载。
                                3. 实现abp vnext的文档的功能 https://docs.abp.io/en/abp/latest
                                4. 写文档。
                                • [x] 创建时间、创建人、修改时间、修改人、删除人、删除时间、是否删除(软删除)
                                • [x] 部署
                                • [ ] 系统访问日志、错误日志可视化
                                • [ ] 完善文档
                                • [ ] 重构核心库结构
                                • [ ] 基于lin-cms-vue的基础,将之前Mock.luo项目中的博客迁移过来 +
                                  • [x] 博客随笔发布/编辑/删除
                                  • [ ] 留言板
                                  • [x] 评论回复、审核、点赞等
                                  • [x] 前端展示博客效果
                                  • [x] 基础资料 BaseItem
                                  • [x] 类别管理 BaseType

                                开源地址

                                上次更新: 12/9/2019, 7:14:35 PM
                                + + + diff --git a/dotnetcore/lin-cms/pm-design-comment.html b/dotnetcore/lin-cms/pm-design-comment.html new file mode 100644 index 0000000..1b63c7e --- /dev/null +++ b/dotnetcore/lin-cms/pm-design-comment.html @@ -0,0 +1,44 @@ + + + + + + + 产品设计-评论模块的设计 | IGeekFan的文档 + + + + + + + + +

                                产品设计-评论模块的设计

                                主题式

                                将评论分为二级,第一级采用时间倒序,二级按照时间正序,有助于理解上下文关系。

                                用户操作:

                                • 评论随笔(内容支持超链接、emoji)
                                • 点赞评论/取消点赞
                                • 回复评论
                                • 删除自己的评论

                                运营操作:

                                • 审核通过/拉黑评论
                                • 删除任何评论
                                • 拉黑后的显示逻辑。(保留当前区块、显示内容为:该评论因违规被拉黑)
                                • 删除:(如果是二级评论,直接软删除,如果是一级评论,软删除子评论和当前评论-需要提前提醒用户) +交互设计
                                • 评论的字数长度(500)、emoji。
                                • 点赞交互-动画、消息通知/推送
                                • 评论区域元素,需要有明确可点击的区域,会跳转到哪个地方。

                                优化

                                • 精选评论
                                  上次更新: 12/9/2019, 7:14:35 PM
                                  + + + diff --git a/dotnetcore/lin-cms/pm-design-modules.html b/dotnetcore/lin-cms/pm-design-modules.html new file mode 100644 index 0000000..6f776a7 --- /dev/null +++ b/dotnetcore/lin-cms/pm-design-modules.html @@ -0,0 +1,72 @@ + + + + + + + 功能模块的设计 | IGeekFan的文档 + + + + + + + + +

                                  功能模块的设计

                                  基础权限模块

                                  • 用户信息:邮件、用户名(唯一)、昵称、头像、分组、是否激活、手机号、是否是Admin、个性签名 +
                                    • [x] 注册/登录
                                    • [x] 上传头像
                                    • [x] 修改密码
                                    • 用户基本信息修改
                                    • [x] 配置分组
                                    • 第三方账号绑定/登录
                                  • 绑定信息:功能(QQ快速登录,GitHub快速登录)。
                                  • 分组信息:是否静态分组(无法删除,无法修改分组编码)、名称可以修改 +
                                    • [x] 分组增删改
                                    • [x] 组别配置权限
                                  • 文件管理 +
                                    • [x] 本地文件上传
                                    • 七牛云存储
                                    • 文件去重,秒传
                                  • [x] 系统日志:请求方法、路径、http返回码、时间、用户昵称、用户id、访问哪个权限、 日志信息 +
                                    • 记录系统请求的日志

                                  cms 管理员维护模块

                                  • [x] 标签管理:名称、图片,是否启用/禁用,排序、文章数量、用户关注数量。 +
                                    • [x] 标签增删改
                                    • [x] 标签列表,禁用
                                  • 随笔管理: +
                                    • 审核随笔/拉黑
                                  • 评论管理 +
                                    • 后台审核通过/拉黑
                                    • 管理员删除评论
                                  • 字典管理 +
                                    • [x] 字典类别管理
                                    • [x] 字典管理:如随笔类型(原创、转载、翻译)

                                  cms 用户端模块

                                  • 分类专栏管理:发布随笔时可选择单个分类。 +
                                    • [x] 分类增删改(随笔数量、图片、名称、排序)
                                    • [x] 分类列表,仅查看、编辑自己创建的分类专栏
                                  • 标签:统计每个标签下多少个文章、多少人关注 +
                                    • [x] 标签列表
                                    • [x] 无限加载
                                    • [x] 最新/最热 根据名称模糊查询
                                    • 我关注的标签
                                  • 随笔 +
                                    • [x] 支持markdown,增删改(仅自己的随笔),修正分类专栏中的随笔数量
                                    • [x] 列表无限加载,按标签查询随笔
                                    • [x] 点赞随笔
                                    • 随笔详情页 +
                                      • [x] 支持目录导航(滚动时,固定至顶部位置),展示字数统计、预计阅读时长;
                                      • [x] 作者介绍:头像,昵称,签名,随笔数;
                                      • [x] 展示文章类型:原创、转载、翻译
                                      • 相关文章
                                      • 推荐文章
                                  • 评论 +
                                    • 评论随笔(内容支持超链接、emoji)
                                    • [x] 删除自己的评论
                                    • [x] 点赞评论
                                    • [x] 回复评论
                                  • 关注 +
                                    • 关注/取消关注用户
                                    • 关注/取消关注标签

                                  个人主页

                                  • 用户专栏分类展示
                                  • 最新发布、最热的随笔
                                  • 关注的人/标签
                                  • 粉丝
                                  • 个人主页详细页:设置个人资料,绑定邮件,

                                  更多

                                  • 消息通知:如点赞随笔,评论随笔、点赞评论、回复评论后的消息通知。

                                  脑图分享

                                  image

                                  分组

                                  分为三种

                                  id  name        info
                                  +1	Admin	    系统管理员
                                  +2	CmsAdmin	内容管理员
                                  +3	User	    普通用户
                                  +

                                  审计日志

                                  大多数表存在如下8个字段,用于记录行的变化状态,is_deleted为软删除,执行删除操作时,将其状态置为true,默认实体类继承 FullAduitEntity 即可拥有以下8个字段。该设计参考ABP中的实现。FullAduitEntity为泛型,默认id为long类型,FullAduitEntity<Guid>,即可改变主键类型,默认LinUser表主键long,保持create_user_id,delete_user_id,update_user_id都与LinUser的主键相同

                                  
                                  +id	                bigint
                                  +create_user_id  	bigint
                                  +create_time	        datetime
                                  +is_deleted	        bit
                                  +delete_user_id  	bigint
                                  +delete_time	        datetime
                                  +update_user_id	    bigint
                                  +update_time	        datetime
                                  +
                                  +
                                  +

                                  lin-cms 开源地址分享

                                    上次更新: 12/9/2019, 7:14:35 PM
                                    + + + diff --git a/dotnetcore/lin-cms/spa-github-login.html b/dotnetcore/lin-cms/spa-github-login.html new file mode 100644 index 0000000..28abcd5 --- /dev/null +++ b/dotnetcore/lin-cms/spa-github-login.html @@ -0,0 +1,273 @@ + + + + + + + GitHub第三方授权登录 | IGeekFan的文档 + + + + + + + + +

                                    GitHub第三方授权登录

                                    使用SPA+.NET Core3.1实现 GitHub第三方授权登录 类似使用AspNet.Security.OAuth.GitHub,前端使用如下:VUE+Vue-Router+axios

                                    AspNet.Security.OAuth.GitHub

                                    GitHub授权登录

                                    什么配置的过程不说了。。有一推。

                                    下面为示例

                                    client_id:0be6b05fc717bfc4fb67
                                    +client_secret:dcaced9f176afba64e89d88b9b06ffc4a887a609
                                    +

                                    Get

                                    https://github.com/login/oauth/authorize?client_id=0be6b05fc717bfc4fb67&redirect_uri=https://localhost:5001/signin-github
                                    +

                                    会重定向到

                                    https://localhost:5001/signin-github?code=07537a84d12bbae08361

                                    这个code放到下面的请求中,获取access_token +POST方式(PostMan去请求)

                                    https://github.com/login/oauth/access_token?client_id=0be6b05fc717bfc4fb67&client_secret=dcaced9f176afba64e89d88b9b06ffc4a887a609&code=07537a84d12bbae08361
                                    +

                                    Get方式

                                    https://api.github.com/user?access_token=787506afa3271d077b98f18af56d7cfdc8db43b4
                                    +

                                    然后就能获取用户信息

                                    {
                                    +   "login": "luoyunchong",
                                    +   "id": 18613266,
                                    +   "node_id": "MDQ6VXNlcjE4NjEzMjY2",
                                    +   "avatar_url": "https://avatars1.githubusercontent.com/u/18613266?v=4",
                                    +   "gravatar_id": "",
                                    +   "url": "https://api.github.com/users/luoyunchong",
                                    +   "html_url": "https://github.com/luoyunchong",
                                    +   "followers_url": "https://api.github.com/users/luoyunchong/followers",
                                    +   "following_url": "https://api.github.com/users/luoyunchong/following{/other_user}",
                                    +   "gists_url": "https://api.github.com/users/luoyunchong/gists{/gist_id}",
                                    +   "starred_url": "https://api.github.com/users/luoyunchong/starred{/owner}{/repo}",
                                    +   "subscriptions_url": "https://api.github.com/users/luoyunchong/subscriptions",
                                    +   "organizations_url": "https://api.github.com/users/luoyunchong/orgs",
                                    +   "repos_url": "https://api.github.com/users/luoyunchong/repos",
                                    +   "events_url": "https://api.github.com/users/luoyunchong/events{/privacy}",
                                    +   "received_events_url": "https://api.github.com/users/luoyunchong/received_events",
                                    +   "type": "User",
                                    +   "site_admin": false,
                                    +   "name": "IGeekFan",
                                    +   "company": null,
                                    +   "blog": "https://blog.igeekfan.cn",
                                    +   "location": null,
                                    +   "email": "luoyunchong@foxmail.com",
                                    +   "hireable": null,
                                    +   "bio": "学习之路漫漫无期。",
                                    +   "public_repos": 14,
                                    +   "public_gists": 0,
                                    +   "followers": 16,
                                    +   "following": 11,
                                    +   "created_at": "2016-04-22T10:33:44Z",
                                    +   "updated_at": "2019-12-21T14:49:33Z"
                                    +}
                                    +

                                    .NET Core3.1

                                    以下代码为主要代码,完整代码看下面的DEMO链接。

                                    使用WebApi时,看了一些项目,全是基于MVC结构的,都不是我想要的。看了一些博客上面介绍 ,步骤都是千篇一律,都是配合前后端分离的。

                                    • 前端运行在:http://localhost:8081
                                    • 后端运行在:https://localhost:5001

                                    前后端分离的SPA 配合第三方授权登录流程如下

                                    本地测试时,gitHub回调地址设置 http(s)😕/ip:端口/signin-github

                                    • 如: https://localhost:5001/signin-github。

                                    1. 上面这个明明填写的后端的地址,那后端怎么把结果通知前端呢?

                                    前端请求https://localhost:5001/signin?provider=GitHub&redirectUrl=http://localhost:8080/login-result

                                    • 提供参数provider为GitHub,
                                    • redirectUrl为GitHub授权登录后,回调signin-github后,后端再去重定向的地址,这里填前端的一个路由。

                                    2. 后端只提供了signin,signin-callback路由,没有signin-github,那github上配置的路由是怎么回调回来呢?

                                    google-登录,微软文档,其中有一个更改默认回调 URI,通过 AddGitHub中的CallbackPath属性配置。

                                    介绍了回调地址应配置signin-google,所以这里应该是signin-github,他是可以配置的,不需要自己写程序处理signin-google这个路由,内部有中间件已经处理了。

                                    3. 回调到signin-github后,后端怎么处理,才能让前端刷新。获取登录后的信息呢。

                                    具体上面的根据code获取access_token,根据access_token获取用户的信息的过程,这些处理的过程,都不需要我们自己处理。我们可以用直接获取用户信息。

                                    一个方法SignIn,只要return Challenge(properties, provider);

                                    • provider 为 GitHub,
                                    • properties var properties = new AuthenticationProperties { RedirectUri = url };

                                    这个url为另一个获取用户信息的路由,只要拼接好地址即可。

                                    var authenticateResult = await _contextAccessor.HttpContext.AuthenticateAsync(provider);
                                    +string email = authenticateResult.Principal.FindFirst(ClaimTypes.Email)?.Value;
                                    +string name = authenticateResult.Principal.FindFirst(ClaimTypes.Name)?.Value;
                                    +

                                    需要注入

                                    private readonly IHttpContextAccessor _contextAccessor;
                                    +public AuthenticationController( IHttpContextAccessor contextAccessor)
                                    +{
                                    +    _contextAccessor = contextAccessor;
                                    +}
                                    +

                                    代码部署(简化)

                                    打开NuGet包管理,安装包

                                    Install-Package AspNet.Security.OAuth.GitHub
                                    +

                                    appSettings.json

                                    "Authentication": {
                                    +    "GitHub": {
                                    +      "ClientId": "0be6b05fc717bfc4fb67",
                                    +      "ClientSecret": "dcaced9f176afba64e89d88b9b06ffc4a887a609"
                                    +    }
                                    +}
                                    +

                                    add扩展方法

                                    public static class JwtConfiguration
                                    +{
                                    +    public static void AddJwtConfiguration(this IServiceCollection services, IConfiguration configuration)
                                    +    {
                                    +
                                    +        services.AddAuthentication(opts =>
                                    +            {
                                    +                opts.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                                    +                opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                                    +            }).AddCookie(options =>
                                    +        {
                                    +            options.LoginPath = "/signin";
                                    +            options.LogoutPath = "/signout";
                                    +        }).AddGitHub(options =>
                                    +        {
                                    +            options.ClientId = configuration["Authentication:GitHub:ClientId"];
                                    +            options.ClientSecret = configuration["Authentication:GitHub:ClientSecret"];
                                    +        });
                                    +    }
                                    +}
                                    +

                                    默认情况下,如头像,email,是没有获取的。

                                    .AddGitHub(options =>
                                    +{
                                    +    options.ClientId = configuration["Authentication:GitHub:ClientId"];
                                    +    options.ClientSecret = configuration["Authentication:GitHub:ClientSecret"];
                                    +    //options.CallbackPath = new PathString("~/signin-github");//与GitHub上的回调地址相同,默认即是/signin-github
                                    +    options.Scope.Add("user:email");
                                    +    //authenticateResult.Principal.FindFirst(LinConsts.Claims.AvatarUrl)?.Value;  得到GitHub头像
                                    +    options.ClaimActions.MapJsonKey(LinConsts.Claims.AvatarUrl, "avatar_url");
                                    +    options.ClaimActions.MapJsonKey(LinConsts.Claims.BIO, "bio");
                                    +    options.ClaimActions.MapJsonKey(LinConsts.Claims.BlogAddress, "blog");
                                    +});
                                    +
                                    +#其中LinConsts类为静态常量
                                    +public static class LinConsts
                                    +{
                                    +    public static class Claims
                                    +    {
                                    +        public const string BIO = "urn:github:bio";
                                    +        public const string AvatarUrl = "urn:github:avatar_url";
                                    +        public const string BlogAddress = "urn:github:blog";
                                    +    }
                                    +}
                                    +

                                    startup.cs

                                    ConfigureServices中配置此服务

                                        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
                                    +    services.AddJwtConfiguration(Configuration);
                                    +

                                    创建AuthenticationController.cs +增加SignIn,用于处理用户授权成功后,重定回signin-callback,并将参数带回。

                                            private readonly IHttpContextAccessor _contextAccessor;
                                    +        private readonly IConfiguration _configuration;
                                    +
                                    +        public AuthenticationController(IHttpContextAccessor contextAccessor, IConfiguration configuration)
                                    +        {
                                    +            _contextAccessor = contextAccessor;
                                    +            _configuration = configuration;
                                    +        }
                                    +        
                                    +        [HttpGet("~/signin")]
                                    +        public async Task<IActionResult> SignIn(string provider, string redirectUrl)
                                    +        {
                                    +            var request = _contextAccessor.HttpContext.Request;
                                    +            var url =
                                    +                $"{request.Scheme}://{request.Host}{request.PathBase}{request.Path}-callback?provider={provider}&redirectUrl={redirectUrl}";
                                    +            var properties = new AuthenticationProperties { RedirectUri = url };
                                    +            properties.Items["LoginProviderKey"] = provider;
                                    +            return Challenge(properties, provider);
                                    +
                                    +        }
                                    +

                                    在signin方法中,用户点击授权后(第一次),会根据其传递的URL,重定向到这个地址,signin-callback,参数也会一同携带。provider为GitHub,redirectUrl为:http://localhost:8081/login-result.

                                    [HttpGet("~/signin-callback")]
                                    +public async Task<IActionResult> Home(string provider = null, string redirectUrl = "")
                                    +{
                                    +    var authenticateResult = await _contextAccessor.HttpContext.AuthenticateAsync(provider);
                                    +    if (!authenticateResult.Succeeded) return Redirect(redirectUrl);
                                    +    var openIdClaim = authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier);
                                    +    if (openIdClaim == null || string.IsNullOrWhiteSpace(openIdClaim.Value))
                                    +        return Redirect(redirectUrl);
                                    +
                                    +    //TODO 记录授权成功后的信息 
                                    +
                                    +    string email = authenticateResult.Principal.FindFirst(ClaimTypes.Email)?.Value;
                                    +    string name = authenticateResult.Principal.FindFirst(ClaimTypes.Name)?.Value;
                                    +    string gitHubName = authenticateResult.Principal.FindFirst(GitHubAuthenticationConstants.Claims.Name)?.Value;
                                    +    string gitHubUrl = authenticateResult.Principal.FindFirst(GitHubAuthenticationConstants.Claims.Url)?.Value;
                                    +    //startup 中 AddGitHub配置项  options.ClaimActions.MapJsonKey(LinConsts.Claims.AvatarUrl, "avatar_url");
                                    +    string avatarUrl = authenticateResult.Principal.FindFirst(LinConsts.Claims.AvatarUrl)?.Value;
                                    +
                                    +    return Redirect($"{redirectUrl}?openId={openIdClaim.Value}");
                                    +}
                                    +

                                    这时候我们能获取用户信息了。那么前端怎么办呢。我们写个方法,获取用户信息,看看效果。

                                    • 浏览器直接打开能得到github的id。
                                    • axios GET请求 https://localhost:5001/OpenId 得到null
                                    [HttpGet("~/OpenId")]
                                    +public async Task<string> OpenId(string provider = null)
                                    +{
                                    +   var authenticateResult = await _contextAccessor.HttpContext.AuthenticateAsync(provider);
                                    +   if (!authenticateResult.Succeeded) return null;
                                    +   var openIdClaim = authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier);
                                    +   return openIdClaim?.Value;
                                    +}
                                    +

                                    我记得之前传Token时,后台是可以这样获取的。

                                    [HttpGet("~/GetOpenIdByToken")]
                                    +public string GetOpenIdByToken()
                                    +{
                                    +    return User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
                                    +}
                                    +

                                    LoginResult.vue在created生命周期中。都是得到null

                                    axios({
                                    +  methods: "get",
                                    +  url: "https://localhost:5001/OpenId?provider=GitHub"
                                    +})
                                    +  .then(function(response) {
                                    +    // handle success
                                    +    console.log(response);
                                    +  })
                                    +
                                    +axios({
                                    +  methods: "get",
                                    +  url: "https://localhost:5001/GetOpenIdByToken"
                                    +})
                                    +  .then(function(response) {
                                    +    // handle success
                                    +    console.log(response);
                                    +  })
                                    +

                                    为什么呢???

                                    因为前后端分离,不是基于Cookies的。http是无状态的。每次请求无法区分用户的。我们可以根据当前的ClaimsPrincipal,根据JWT生成相应的Token,axios请求时,放到headers中。

                                    安装包

                                    Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
                                    +

                                    AppSettings.json配置改成

                                    "Authentication": {
                                    +"JwtBearer": {
                                    +  "SecurityKey": "JWTStudyWebsite_DI20DXU3",
                                    +  "Issuer": "JWTStudy",
                                    +  "Audience": "JWTStudyWebsite"
                                    +},
                                    +"GitHub": {
                                    +  "ClientId": "0be6b05fc717bfc4fb67",
                                    +  "ClientSecret": "dcaced9f176afba64e89d88b9b06ffc4a887a609"
                                    +}
                                    +}
                                    +

                                    AddJwtConfiguration改成如下内容

                                    public static void AddJwtConfiguration(this IServiceCollection services, IConfiguration configuration)
                                    +{
                                    +
                                    +    services.AddAuthentication(opts =>
                                    +        {
                                    +            opts.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                                    +            opts.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                                    +        }).AddCookie(options =>
                                    +    {
                                    +        options.LoginPath = "/signin";
                                    +        options.LogoutPath = "/signout";
                                    +    }).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
                                    +    {
                                    +        options.Audience = configuration["Authentication:JwtBearer:Audience"];
                                    +
                                    +        options.TokenValidationParameters = new TokenValidationParameters
                                    +        {
                                    +            // The signing key must match!
                                    +            ValidateIssuerSigningKey = true,
                                    +            IssuerSigningKey = new SymmetricSecurityKey(
                                    +                Encoding.ASCII.GetBytes(configuration["Authentication:JwtBearer:SecurityKey"])),
                                    +
                                    +            // Validate the JWT Issuer (iss) claim
                                    +            ValidateIssuer = true,
                                    +            ValidIssuer = configuration["Authentication:JwtBearer:Issuer"],
                                    +
                                    +            // Validate the JWT Audience (aud) claim
                                    +            ValidateAudience = true,
                                    +            ValidAudience = configuration["Authentication:JwtBearer:Audience"],
                                    +
                                    +            // Validate the token expiry
                                    +            ValidateLifetime = true,
                                    +
                                    +            // If you want to allow a certain amount of clock drift, set that here
                                    +            //ClockSkew = TimeSpan.Zero
                                    +        };
                                    +    }).AddGitHub(options =>
                                    +    {
                                    +        options.ClientId = configuration["Authentication:GitHub:ClientId"];
                                    +        options.ClientSecret = configuration["Authentication:GitHub:ClientSecret"];
                                    +        //options.CallbackPath = new PathString("~/signin-github");//与GitHub上的回调地址相同,默认即是/signin-github
                                    +        options.Scope.Add("user:email");
                                    +        //authenticateResult.Principal.FindFirst(LinConsts.Claims.AvatarUrl)?.Value;  得到GitHub头像
                                    +        options.ClaimActions.MapJsonKey(LinConsts.Claims.AvatarUrl, "avatar_url");
                                    +        options.ClaimActions.MapJsonKey(LinConsts.Claims.BIO, "bio");
                                    +        options.ClaimActions.MapJsonKey(LinConsts.Claims.BlogAddress, "blog");
                                    +    });
                                    +}
                                    +

                                    前端LoginResult.vue代码

                                    前端运行

                                    yarn install
                                    +yarn serve
                                    +

                                    点击GitHub登录

                                    GetOpenIdByToken根据生成的token值,解析出了用户id,这样前端在login-result这个组件中,把token保存好,并重定向自己的主页,获取用户所有信息即可。

                                    data: 18613266
                                    +status: 200
                                    +config: {url: "https://localhost:5001/GetOpenIdByToken"}
                                    +

                                    OpenId?provider=GitHub则得不到数据,只能浏览器直接请求https://localhost:5001/OpenId?provider=GitHub,才能到github 的id。这个适应于前后端不分离,或者属于之前我们经常使用MVC结构,同一域名下,同一端口,基于Cookies登录的判断。

                                    参考

                                    Demo 示例

                                    上次更新: 12/27/2019, 11:24:45 PM
                                    + + + diff --git a/dotnetcore/lin-cms/vue-start.html b/dotnetcore/lin-cms/vue-start.html new file mode 100644 index 0000000..2d6a303 --- /dev/null +++ b/dotnetcore/lin-cms/vue-start.html @@ -0,0 +1,96 @@ + + + + + + + 前端准备 | IGeekFan的文档 + + + + + + + + +

                                    前端准备

                                    开源地址

                                    文档地址

                                    官方预览地址

                                    线上 Demo

                                    快速上手

                                    开发必备

                                    如果以下命令有问题,请删除yarn.lock,node_modules文件夹后,重新执行yarn,yarn serve

                                    # clone the project
                                    +git clone https://github.com/luoyunchong/lin-cms-vue.git
                                    +
                                    +# install dependency
                                    +yarn
                                    +
                                    +# develop
                                    +yarn serve
                                    +
                                    +# deploy
                                    +yarn deploy
                                    +

                                    deploy 发布

                                    scp2方便快速发布,一行命令就能快速发布成功。

                                    必备条件:(参数)

                                    • 一台linux的服务器,ip
                                    • 用户名
                                    • 密码
                                    • 端口:默认是22
                                    • 发布的地址。这里放到/var/www/lin-cms-vue目录中。

                                    步骤

                                    根目录新建deploy目录,创建index.js文件。

                                    'use strict'
                                    +// 引入scp2模块
                                    +var client = require('scp2');
                                    +const ora = require('ora')
                                    +const chalk = require('chalk')
                                    +const spinner = ora('正在发布到生产服务器...')
                                    +spinner.start()
                                    +client.scp('dist/', {
                                    +    "host": "",
                                    +    "username": "",
                                    +    "password": "",
                                    +    "port": "22",
                                    +    "path": "/var/www/lin-cms-vue"
                                    +}, function (err) {
                                    +    spinner.stop()
                                    +    if (!err) {
                                    +        console.log("npm run build-scp2: scp2工具上传完毕,远端服务路径:/var/www/lin-cms-vue")
                                    +    } else {
                                    +        console.log("err", err)
                                    +    }
                                    +})
                                    +

                                    快速发布,需要安装 scp2

                                    cnpm install scp2
                                    +

                                    package.json中增加

                                      "scripts": {
                                    +    "deploy": "yarn build:production && node ./deploy",
                                    +}
                                    +
                                    yarn deploy
                                    +

                                    nginx 配置

                                    • vue 使用history的配置
                                    server {
                                    +    listen 8080;
                                    +    root /var/www/lin-cms-vue;
                                    +
                                    +   charset utf-8;
                                    +   location / {
                                    +       try_files $uri $uri/ /index.html; 
                                    +
                                    +     }
                                    +        
                                    +    error_page   500 502 503 504  /50x.html;
                                    +    location = /50x.html {
                                    +        root   html;
                                    +    }
                                    +
                                    +}
                                    +

                                    配置项

                                    1. 配置 api 地址: 打开配置文件 src/config/index.js 配置 baseUrl ,本地开发阶段配置本地虚拟域名(https://localhost:5001/),线上部署生产域名。

                                    2. 用户名:super 密码 123456

                                      上次更新: 12/27/2019, 11:24:45 PM
                                      + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..eaa53e4 --- /dev/null +++ b/index.html @@ -0,0 +1,52 @@ + + + + + + + 简介 | IGeekFan的文档 + + + + + + + + +

                                      简介

                                      项目使用vuepress,其可专注于文档构建

                                      文档源码

                                      https://github.com/luoyunchong/vuepress-docs

                                      vuepress

                                      该采用vuepress搭建,内置md,可以采用vue语法,vue作者出品

                                      完整文档

                                      LinCMS

                                      Lin-CMS 是林间有风团队经过大量项目实践所提炼出的一套内容管理系统框架,前后端分离的完整解决方案。

                                      与本项目的关系:本项目和Lin-CMS的文档的样式效果是一样的,所以文档构建中包含“林间有风团队” 出品,意思是指UI主题来自其团队。

                                      如何构建运行

                                      前提

                                      • node.js
                                      • yarn或npm
                                      • vuepress

                                      最好是安装yarn https://yarnpkg.com/lang/zh-hans/docs/install/#windows-stable

                                      安装依赖包,开发运行

                                      PS D:\code\github\vuepress-docs>yarn install
                                      +PS D:\code\github\vuepress-docs>yarn dev
                                      +

                                      如果不能正常运行,就删除 yarn.lock、node_modules文件夹,再重新执行上面的命令

                                      生成发布包

                                      PS D:\code\github\vuepress-docs>yarn build 
                                      +

                                      发布

                                      发布至github pages 中的gh-pages分支

                                      PS D:\code\github\vuepress-docs>.\deploy.ps1
                                      +

                                      .sh 也不懂,关键我本地是windows,不能正常执行,git bash 也许可以

                                      package.json介绍

                                      package.json有这些命令

                                        "scripts": {
                                      +    "dev": "node bin/vuepress dev docs",
                                      +    "build": "node bin/vuepress build docs",
                                      +    "lint": "eslint --fix --ext .js,.vue bin/ lib/ test/",
                                      +    "deploy-gh": "yarn build && bash scripts/deploy-gh.sh",
                                      +    "prepublishOnly": "conventional-changelog -p angular -r 2 -i CHANGELOG.md -s",
                                      +    "release": "bash scripts/release.sh",
                                      +    "test": "node test/prepare.js && jest --config test/jest.config.js"
                                      +  },
                                      +

                                      所以我们可以 yarn dev或yarn build +其他的,暂时还不会跑

                                      bash无法推送 +ssh: connect to host github.com port 22: No route to host +fatal: Could not read from remote repository.

                                      yarn deploy-gh
                                      +
                                        上次更新: 10/8/2019, 1:41:22 PM
                                        + + + diff --git a/left-logo.png b/left-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..24e7ff51ed5ef3b2c3c96350f8268957dc8e477b GIT binary patch literal 9141 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 literal 0 HcmV?d00001 diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..566296b1667114cd3e066f3c9f1596d7cf29e222 GIT binary patch literal 1396 zcmV-)1&jKLP)Px)E=fc|R9FeUS8HrkMHK$#-fg#!U6#_W6t(iICOisa0Hvh``XEtEBvIlINbm=& zqQ)3WND&hoL$nA<2?mV?{9#0rCMcS~kG9laXd9z7f(5ZuN?Tf6D70N$y4&vV9nW0c z+q>PnyInBOk2`bTXU?2CXD%?sQN_7jT3xS{*4FRd*U(^(CRX55a-z|VG-d>%t;z7~ zQ2F6|JZz8z4_XuWp|W-Bh6FC}&4!1$$Ge4tZ3Xy3iAgUu9xePPnodERk^|*6%?>vo zY2)Bdqe8C2-E|q&omT?WI8Xu(NvAXRXO84pSXsH;r{hc(786_;Y6v5HXji)Np?;2A z*E#xcaEITIO<@88!Fl#&)%^yo4w};cMaGQ4dx|pGKc-&sjBxAC4&Y{|3hziXl?EEa zfSh2>Zul&J(0~nDMs=Xr)$DLC7_J@c3oLiv4w0KOFfARYmla!%CnfN2DPO#k1;ZTJ zRK?}LYUsn0#v>#Acz}bH#$w?1>osdps5!hVRhT?b7!bu-Xz&3nASBO#??uOic!cWUGu>8A<5u z8#Q1fFRx$ZP$>|!(T1|z+yTKE;y|i{-H9w7PMQBmDk-C|^-QfWct<(9TPX^RY2goX zV2`3?Wi7zV;btOZo?e^|zhA}0t6DpV%xIu={jLUj+Y{=7C@lh_)2UlM86%(&r7skaHy@#+S}26O!R?)J}L%ESk=PGeYv0q2)gLb zKa-P=iW99Ai7~8Pl#V6Y4m_5b0*kB%5|=1BT^}`vY{UpiURaQ-tA}&9)l$o3`oBOh8dQHQwi#>%6)V#O7JLpT2Iel2{|L%xGvlT|C=r!} z2u}h+f73#V5@WkQELp47`A~{EWO@YVtP|Ms;6VF>UZ3)$$tPJ8M_Q@r9Mvp0000xh8> literal 0 HcmV?d00001