Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 成年人小视频网站,性bbbb美女,麻豆网站在线观看

          整合營(yíng)銷(xiāo)服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢(xún)熱線:

          Vue 初級(jí)入門(mén)代碼示例

          Vue 初級(jí)入門(mén)代碼示例

          本表達(dá)式語(yǔ)法

          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <script src="../node_modules/vue/dist/vue.min.js"></script>
          </head>
          <div id="app">
              <!--簡(jiǎn)單表達(dá)式  [類(lèi)型一樣直接加]=25-->
              <h1>{{5+5}}</h1>
              <!-- +:運(yùn)算,字符串連接  【類(lèi)型不一樣就是拼接】=5v5,55-->
              <h1>{{5+"v5"}}</h1>
              <h1>{{5+"5"}}</h1>
              <!-- -:減法 "5"-"5" 兩個(gè)雙引號(hào) 自動(dòng)解析【類(lèi)型一樣直接算】=0,25-->
              <h1>{{"5"-"5"}}</h1>
              <h1>{{5*5}}</h1>
              <!-- *:乘 【一樣類(lèi)型一樣直接乘】=25-->
              <h1>{{"5"*"5"}}</h1>
              <!-- / 除  【不說(shuō)了一樣】=1,1-->
              <h1>{{5/5}}</h1>
              <h1>{{"5"/"5"}}</h1>
          </div>
          </body>
          <script>
              var app=new Vue({
                 el:"#app"//掛載到id
              });
          </script>

          三目操作

          <script src="../node_modules/vue/dist/vue.min.js"></script>
          <body>
              <div class="app">
                  {{show?"GG":"MM"}}
              </div>
          </body>
          <script>
              var app=new Vue({
                  el:".app",
                  data:{
                      show:true//true就是MM,false就是GG
                  }
              });
          </script>
          </html>

          字符串操作

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <script src="../node_modules/vue/dist/vue.min.js"></script>
          </head>
          <body>
              <div id="app">
                  {{message}}<br>
                  <!--長(zhǎng)度-->
                  {{message.length}}<br>
                  <!--截取根據(jù)下標(biāo)-->
                  {{message.substring(0,3)}}
                  <!--根據(jù)下標(biāo)從哪里開(kāi)始   【3456】-->
                  {{message.substring(2).toUpperCase()}}<br>
                  <!--獲取到下標(biāo)  【3】-->
                  {{message.charAt(2)}}
              </div>
          </body>
          <script>
              var app=new Vue({
                 el:"#app",
                 data:{
                     message:"123456"
                 }
              });
          </script>
          </html>

          對(duì)象操作

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <script src="../node_modules/vue/dist/vue.min.js"></script>
          </head>
          <body>
              <div id="app">
                  <!--123456-->
                  {{message}}<br>
                  <!--{ "name": "華雄", "age": 69 }重寫(xiě)toString,就變了-->
                  {{user}}<br>
                  <!--華雄-->
                  {{user.name}}<br>
                  <!--getName(){return this.name}-->
                  {{user.getName}}<br>
                  <!--toString(){return this.name}-->
                  {{user.toString}}<br>
                  <!--{"name":"華雄","age":69} -->
                  {{JSON.stringify(user)}}
                  <!--22  json轉(zhuǎn)成字符串了-->
                  {{JSON.stringify(user).length}}
              </div>
          </body>
          <script>
              var sss={
                  name:"華雄",
                  age:69,
                  getName(){return this.name},//{ "name": "華雄", "age": 69 }
                  //原toString---function toString() { [native code] }
                  toString(){return this.name}//重寫(xiě)toString,這樣獲取到就是華雄
              }
              var app=new Vue({
                 el:"#app",
                 data:{
                     message:"123456",
                     user:sss
                 }
              });
          </script>
          </html>

          數(shù)組操作

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <script src="../node_modules/vue/dist/vue.min.js"></script>
          </head>
          <body>
              <div id="app">
                  {{woman}}<br>
                  {{woman[0]}}<br>
                  {{woman.length}}<br>
                  {{woman.toString()}}<br>
                  {{woman.join(" + ")}}
              </div>
          </body>
          <script>
              var app=new Vue({
                 el:"#app",
                 data:{
                     woman:["黃月英","蔡文姬","孫尚香","甄宓"]
                 }
              });
          </script>
          </html>

          v-text 文本展示/ v-html 變大變粗

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <script src="../node_modules/vue/dist/vue.min.js"></script>
          </head>
          <body>
          <div id="app">
              <!--純文本 是什么樣就展示什么-->
              <span v-text="msg"></span><br>
              <!--解析標(biāo)簽 會(huì)自動(dòng)解析標(biāo)簽-->
              <span v-html="msg"></span>
          </div>
          </body>
          <script>
              new Vue({
                  el:"#app",
                  data:{
                      msg:"<h3>你好!中國(guó)</h3>"
                  }
              })
          </script>

          v-for 循環(huán)

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <script src="../node_modules/vue/dist/vue.min.js"></script>
          </head>
          <body>
          <div id="app">
              <!--就是直接循環(huán)-->
              <ul>
                  <li v-for="a in woman">{{a}}</li>
              </ul>
              <!--循環(huán)a和下標(biāo)index-->
              <ul>
                  <li v-for="(a,index) in woman">{{a}}---{{index}}</li>
              </ul>
              <!--搞一個(gè)表-->
              <table border="1px black">
                  <!--表頭-->
                  <tr>
                      <th>名字</th>
                      <th>年齡</th>
                  </tr>
                  <!--循環(huán)里面的東西-->
                  <tr v-for="key in users">
                      <!--
                          aa in key  aa:value值
                          aa,bb in key  aa:value值 bb:屬性名
                          aa,bb,index,index aa:value值 bb:屬性名 index:下標(biāo)
                      -->
                      <td v-for="(aa,bb,index) in key">
                          {{aa}}----{{bb}}---{{index+1}}
                      </td>
                  </tr>
              </table>
          </div>
          </body>
          <script>
              new Vue({
                  el:"#app",
                  data:{
                      woman:["黃月英","蔡文姬","孫尚香","甄宓"],
          
                      <!--List<user>-->
                      users:[{
                          name:"張三",
                          age:10
                      },{
                          name:"李四",
                          age:20
                      }]
                  }
              })
          </script>

          v-bind 綁定圖片,圖片自己可以寫(xiě)活

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <script src="../node_modules/vue/dist/vue.min.js"></script>
          </head>
          <body>
              <div id="app">
                  <!--原版以前這么寫(xiě)-->
                  <img src="123456.JPG" title="">
                  <!--新版  可以實(shí)現(xiàn)綁定,這樣就能寫(xiě)活了-->
                  <img v-bind:src="src" v-bind:title="sss">
                  <!--title就是鼠標(biāo)提示-->
                  <img :src="src" v-bind:title="sss">
              </div>
          </body>
          <script>
              new Vue({
                  el:"#app",
                  data:{
                      //下面的值現(xiàn)在是寫(xiě)死,以后從后臺(tái)獲取。
                      src:"123456.JPG",
                      sss:"手放哪呢?"
                  }
              });
          </script>
          </html>

          v-model 綁定輸入框(雙向綁定)

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <script src="../node_modules/vue/dist/vue.min.js"></script>
          </head>
          <!--這個(gè)都是一些雙向綁定的案例,不好解釋?zhuān)闊┝耍⌒枰臅r(shí)候代碼考過(guò)去自己一看就明白了了-->
          <body>
          <div id="app">
              <h3>綁定到type=text的input表單元素</h3>
              姓名:<input type="text" v-model="inputValue"><br/>
              data中的值:{{inputValue}}
          
              <h3>綁定到type=checkbox的input表單元素</h3>
          
              <!--v-model="checkboxValue" checkboxValue數(shù)組包含了當(dāng)前value值 就會(huì)默認(rèn)選中-->
              打籃球:<input type="checkbox" v-model="checkboxValue" value="打籃球"><br/>
              踢足球:<input type="checkbox" v-model="checkboxValue" value="踢足球"><br/>
              data中的值:{{checkboxValue}}
          
              <h3>綁定到type=radio的input表單元素</h3>
              男:<input type="radio" v-model="radioValue" value="男"><br/>
              女:<input type="radio" v-model="radioValue" value="女"><br/>
              data中的值:{{radioValue}}
          
              <h3>綁定到textarea的元素</h3>
              個(gè)人簡(jiǎn)介:<textarea v-model="textareaValue"></textarea><br/>
              data中的值:{{textareaValue}}
              <h3>綁定到單選的select的元素</h3>
          
              技能:<select v-model="skills">
              <option value="java">java</option>
              <option value="php">php</option>
              <option value=".net">.net</option>
          </select><br/>
              data中的值:{{skills}}
          </div>
          </body>
          <script>
              var vue=new Vue({
                  el:"#app",
                  data:{
                      inputValue:"輸入框的值",
                      checkboxValue:["打籃球"],
                      radioValue:"女",
                      textareaValue:"文本域的值",
                      skills:"php"
                  }
              });
          </script>
          </html>
          

          v-show true 顯示 、false 不顯示

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>Title</title>
              <script src="../node_modules/vue/dist/vue.min.js"></script>
          </head>
          <body>
          <div id="app">
              <span v-show="show">顯示</span><br>
              <span v-show="hidden">不顯示</span><br>
              <span v-show="score<60">小于60分顯示</span><br>
              <span v-show="score>60">大于60分顯示</span>
          </div>
          </body>
          <script>
              var app=new Vue({
                 el:"#app",
                 data:{
                     show:true,
                     hidden:false,
                     score:59
                 }
              });
          </script>
          </html>

          v-if v-else v-else-if

          內(nèi)容是《Web前端開(kāi)發(fā)之Javascript視頻》的課件,請(qǐng)配合大師哥《Javascript》視頻課程學(xué)習(xí)。

          為了便于操作基本類(lèi)型值,ECMAScript還提供了3個(gè)特殊的引用類(lèi)型:Boolean、Number和String;這些類(lèi)型與以上所說(shuō)的引用類(lèi)型相似,但同時(shí)也具有與各自的基本類(lèi)型相應(yīng)的特殊行為;實(shí)際上,每當(dāng)讀取一個(gè)基本類(lèi)型值的時(shí)候,后臺(tái)就會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的基本包裝類(lèi)型的對(duì)象,從而讓我們能夠用一些方法來(lái)操作這些數(shù)據(jù),如:

          var s1="zero network";
          var s2=s1.substring(4);
          alert(s2);

          基本數(shù)據(jù)類(lèi)型不是對(duì)象,所以從邏輯上它們不應(yīng)該有方法(實(shí)際上有)(原理:為了能夠?qū)崿F(xiàn)這種直觀的操作,后臺(tái)已經(jīng)自動(dòng)完成了一系列的處理:創(chuàng)建String對(duì)象實(shí)例,同樣適用于Boolean和Number類(lèi)型對(duì)應(yīng)的布爾值和數(shù)字值。

          基本數(shù)據(jù)類(lèi)型與基本包裝類(lèi)型有本質(zhì)上的不同,產(chǎn)生從類(lèi)型的角度來(lái)講,它們就是不同的;其保存的位置也不同。

          引用類(lèi)型與基本包裝類(lèi)型的區(qū)別就是對(duì)象的生存期:使用new操作符創(chuàng)建的引用類(lèi)型對(duì)象,在執(zhí)行流離開(kāi)當(dāng)前作用域之前都一直保存在內(nèi)存中,而自動(dòng)創(chuàng)建的基本包裝類(lèi)型的對(duì)象,則只存在于一行代碼的執(zhí)行瞬間,然后立即被銷(xiāo)毀;這意味著不能在運(yùn)行時(shí)為基本類(lèi)型值添加屬性和方法,如:

          var s1="zero network";
          s1.color="red";
          alert(s1.color); // undefined

          當(dāng)然可以使用Boolean, Number和String顯示的來(lái)創(chuàng)建基本包裝類(lèi)型的對(duì)象,但絕大部分情況下不需要,除非在絕對(duì)必要的情況下,因?yàn)檫@種做法很容易讓開(kāi)發(fā)者分不清是在處理基本類(lèi)型還是引用類(lèi)型的值;

          對(duì)基本包裝類(lèi)型的實(shí)例調(diào)用typeof會(huì)返回object,而且所有基本包裝類(lèi)型的對(duì)象在轉(zhuǎn)換為布爾類(lèi)型時(shí)值都是true;如:

          var s1=new Number(0);
          // var s1=0;
          alert(typeof s1);
          alert(Boolean(s1));

          Object構(gòu)造函數(shù),僅接受一個(gè)參數(shù),其會(huì)根據(jù)傳入值的類(lèi)型返回相應(yīng)基本包裝類(lèi)型的實(shí)例,如:

          var obj=new Object("zeronetwork");
          alert(obj instanceof String);  // true
          var n=new Object(5);
          alert(n instanceof Number); // true
          console.log(n.constructor);

          說(shuō)明:傳遞的值是動(dòng)態(tài)的,直到運(yùn)行時(shí)才確定其類(lèi)型,Object()可能會(huì)調(diào)用另一個(gè)內(nèi)置函數(shù)來(lái)創(chuàng)建對(duì)象,并且返回了一個(gè)以不同構(gòu)造函數(shù)所創(chuàng)建的對(duì)象。

          ES在必要的時(shí)會(huì)將包裝對(duì)象轉(zhuǎn)換成原始值,但不總是這樣;如:

          var s1="zeronetwork";
          var s2=new String("zeronetwork");
          console.log(s1==s2);  // true
          console.log(s1===s2); // false
          var n1=18;
          var n2=new Number(18);
          console.log(n1==n2);  // true
          console.log(n1===n2); // false

          說(shuō)明:“==”運(yùn)算符會(huì)將原始值與包裝對(duì)象視為相等,但“===”全等會(huì)將它們視為不等,因?yàn)樗鼈兊念?lèi)型不同,可以使用typeof查看。

          注意:使用new調(diào)用基本包裝類(lèi)型的構(gòu)造函數(shù),與直接調(diào)用同名的轉(zhuǎn)型函數(shù)是不一樣的,如:

          var value="25";
          var number=Number(value);  // 轉(zhuǎn)型函數(shù)
          alert(typeof number);  // number
          var obj=new Number(value);  // 構(gòu)造函數(shù)
          alert(typeof obj); // object

          注:null與undefined沒(méi)有包裝對(duì)象,訪問(wèn)它們的屬性會(huì)造成一個(gè)類(lèi)型錯(cuò)誤。

          盡管在實(shí)際場(chǎng)景中不太使用基本包裝類(lèi)型,并且也不建議使用,但它們操作基本類(lèi)型值的能力還是相當(dāng)重要的,而每個(gè)基本包裝類(lèi)型都提供了操作相應(yīng)值的便捷方法。

          Boolean類(lèi)型:

          Boolean類(lèi)型是與布爾值對(duì)應(yīng)的引用類(lèi)型,Boolean 對(duì)象表示兩個(gè)值:"true" 或 "false"。

          創(chuàng)建Boolean對(duì)象:var oBool=new Boolean(true); // 傳入true或false值

          注:如果邏輯對(duì)象無(wú)初始值或者其值為 0、-0、null、""、false、undefined 或者 NaN,那么對(duì)象的值為 false,否則,其值為 true;

          在實(shí)際場(chǎng)景中,Boolean對(duì)象的用處不大,因?yàn)榻?jīng)常會(huì)造成一些誤解,其中最常見(jiàn)的問(wèn)題就是在布爾表達(dá)式中使用Boolean對(duì)象,如:

          var bObj=new Boolean(false);
          alert(bObj && true);  // true
          var bValue=false;
          alert(bValue && true); // false

          基本類(lèi)型與引用類(lèi)型還有兩個(gè)區(qū)別,首先,typeof操作符對(duì)基本類(lèi)型返回boolean,而對(duì)引用類(lèi)型返回object;其次,由于Boolean類(lèi)型的實(shí)例,所以使用instanceof操作符測(cè)試Boolean對(duì)象會(huì)返回true,而測(cè)試基本類(lèi)型的布爾值則返回false,如:

          var falseObject=new Boolean(false);
          var falseValue=false;
          alert(typeof falseObject);  // object
          alert(typeof falseValue); // boolean
          alert(falseObject instanceof Boolean); // true
          alert(falseValue instanceof Boolean); // false

          總結(jié):理解基本類(lèi)型的布爾值與Boolean對(duì)象之間的區(qū)別非常重要,建議不要使用,最好使用Boolean原始值;

          可以使用Boolean(參數(shù))進(jìn)行數(shù)據(jù)類(lèi)型轉(zhuǎn)換;

          Number對(duì)象:

          Number 對(duì)象,是原始數(shù)值的包裝對(duì)象。在必要時(shí),JavaScript 會(huì)自動(dòng)地在原始數(shù)據(jù)和對(duì)象之間轉(zhuǎn)換;

          可以用構(gòu)造函數(shù) Number() 明確地創(chuàng)建一個(gè) Number 對(duì)象:

          var numberObject=new Number(10);

          Number對(duì)象屬性:

          • MAX_VALUE:表示的最大的數(shù),靜態(tài)屬性;
          • MIN_VALUE:表示的最小的數(shù),靜態(tài)屬性;
          • NaN:非數(shù)字值,靜態(tài)屬性;
          • NEGATIVE_INFINITY:負(fù)無(wú)窮大,溢出時(shí)返回該值,靜態(tài)屬性;
          • POSITIVE_INFINITY:正無(wú)窮大,溢出時(shí)返回該值,靜態(tài)屬性;
          console.log(Number.MAX_VALUE);
          console.log(Number.MIN_VALUE);
          var x=-Number.MAX_VALUE * 2;
          if(x==Number.NEGATIVE_INFINITY)
              console.log("X的值:" + x);
          var y=Number.MAX_VALUE * 2;
          if(y==Number.POSITIVE_INFINITY)
              console.log("Y的值:" + y);

          注: Number.NaN 是一個(gè)特殊值,說(shuō)明某些算術(shù)運(yùn)算(如求負(fù)數(shù)的平方根)的結(jié)果不是數(shù)字。方法 parseInt() 和 parseFloat() 在不能解析指定的字符串時(shí)就返回這個(gè)值。

          Number類(lèi)或?qū)ο蠓椒ǎ?/p>

          • parseInt() 和 parseFloat()方法,靜態(tài)方法;
          • isNaN() 來(lái)判斷是否為數(shù)字,靜態(tài)方法;
          • isFinite:是否為有限,靜態(tài)方法;
          • isInteger:是否為整形,靜態(tài)方法;
          • toString()方法: 如:NumberObject.toString(radix),radix可選,規(guī)定表示數(shù)字的基數(shù),使 2 ~ 36 之間的整數(shù),若省略該參數(shù),則使用基數(shù) 10。
          var number=new Number(1337);
          console.log(number.toString(8));
          • toLocaleString() 方法可把一個(gè) Number 對(duì)象轉(zhuǎn)換為本地格式的字符串。
          • toFixed() 方法:把 Number 四舍五入為指定小數(shù)位數(shù)的數(shù)字,類(lèi)型是字符串;參數(shù)規(guī)定了小數(shù)的位數(shù),是 0 ~ 20 之間的值;有些實(shí)現(xiàn)可以支持更大的數(shù)值范圍。如果省略了該參數(shù),將用 0 代替。
          var num=new Number(12.345678);
          console.log(num.toFixed(2));  // 12.35
          • toExponential()方法可把對(duì)象的值轉(zhuǎn)換成指數(shù)計(jì)數(shù)法。如:NumberObject.toExponential(num) ,num規(guī)定指數(shù)計(jì)數(shù)法中的小數(shù)位數(shù),是 0 ~ 20 之間的值;
          var num=1200.00;
          console.log(num.toExponential(2));
          • toPrecision() 方法可在對(duì)象的值超出指定位數(shù)時(shí)將其轉(zhuǎn)換為指數(shù)計(jì)數(shù)法;其可能會(huì)返回固定大小格式,也可能返回指數(shù)格式,具體規(guī)則是看哪種格式最合適;如:NumberObject.toPrecision(num) ,num規(guī)定必須被轉(zhuǎn)換為指數(shù)計(jì)數(shù)法的最小位數(shù),該參數(shù)是 1 ~ 21;
          var num=99;
          console.log(num.toPrecision(1));    // 1e+2
          console.log(num.toPrecision(2));    // 99
          console.log(num.toPrecision(3));    // 99.0
          num=10000;
          console.log(num.toPrecision(1));    // 1e+4
          console.log(num.toPrecision(4));    // 1.000e+4

          注意:與Boolean對(duì)象類(lèi)似,Number對(duì)象也以后臺(tái)方式為數(shù)值提供了重要的功能;不建議直接實(shí)例化Number類(lèi)型,原因與Boolean一樣;一般情況下是使用數(shù)字的原始表示法;

          var numberObject=new Number(10);
          var numberValue=10;
          alert(typeof numberObject);  // object
          alert(typeof numberValue);  // number
          alert(numberObject instanceof Number); // true
          alert(numberValue instanceof Number); // false

          String對(duì)象:

          String類(lèi)型是字符串對(duì)象包裝類(lèi)型,用于處理文本(字符串)。語(yǔ)法:var str=new String(str); 如:

          var oStr=new String();
          var oStr=new String("零點(diǎn)程序員");
          var oStr=String("525");

          length屬性:返回字符串中的字符個(gè)數(shù) 如: str.length ;

          注:即使字符串包含雙字節(jié)字符(不是占一個(gè)字節(jié)的ASCII字符),每個(gè)字符也仍然算一個(gè)字符;

          String 類(lèi)定義了大量操作字符串的方法,例如從字符串中提取字符或子串,或者檢索字符或子串。

          需要注意的是,JavaScript 的字符串是不可變的(immutable),String 類(lèi)定義的方法都不能改變字符串的內(nèi)容。像 String.toUpperCase() 這樣的方法,返回的是全新的字符串,而不是修改原始字符串。

          1)字符方法:

          用于訪問(wèn)字符串中特定字符的方法:charAt()和charCodeAt();這兩個(gè)方法都接收一個(gè)參數(shù),即基于0的字符位置;其中,charAt()方法以單字符字符串的形式返回給定位置的那個(gè)字符(ECMAScript中沒(méi)有字符類(lèi)型),如:

          var str="zero network";
          alert(str.charAt(2));

          如果想得到字符編碼而不是字符時(shí),使用charCodeAt(),如:

          var str="zero network";
          alert(str.charCodeAt(2));  // 114

          ECMAScript還定義了另一個(gè)訪問(wèn)字符的方法,即使用括號(hào)(類(lèi)似于訪問(wèn)數(shù)組元素)語(yǔ)法,使用數(shù)字索引來(lái)訪問(wèn)字符串中的特定字符,如:

          var str="zero network";
          alert(str[2]);  // 2

          2)字符串操作方法:

          concat()方法:用于將一或多個(gè)字符串拼接起來(lái),返回拼接得到的新字符串,如:

          var str = "zero";
          var result = str.concat("network");
          alert(str);
          alert(result);

          concat()方法可以接受多個(gè)任意參數(shù),即可以通過(guò)它可以拼接任意多個(gè)字符串,如:

          var str = "zero";
          var result = str.concat("network","!");
          alert(result);

          說(shuō)明:雖然concat()是專(zhuān)門(mén)用來(lái)拼接字符串的,但在實(shí)際場(chǎng)景中使用更多的還是加號(hào)操作符(+);而且,使用加號(hào)操作符在大多數(shù)情況下都比使用concat方法要簡(jiǎn)便易行,特別是在拼接多個(gè)字符串的情況下;

          ECMAScript提供了三個(gè)基于字符串創(chuàng)建新字符串的方法:slice()、substr()和substring();這三個(gè)方法都會(huì)返回被操作字符串的一個(gè)子字符串,而且也都接受一或兩個(gè)參數(shù);第一個(gè)參數(shù)指定子字符串的開(kāi)始位置,第二個(gè)參數(shù)表示子字符串到哪里結(jié)束;具體來(lái)說(shuō),slice()和substring()的第二個(gè)參數(shù)指定的是子字符串最后一個(gè)字符后面的位置;而substr()的第二個(gè)參數(shù)指定的則是返回的字符個(gè)數(shù);如果沒(méi)有給這些方法傳遞第二個(gè)參數(shù),則將字符串的末尾作為結(jié)束位置,如:

          var str = "zero network";
          alert(str.slice(3));
          alert(str.substring(3));
          alert(str.substr(3));
          alert(str.slice(3,6));
          alert(str.substring(3,6));
          alert(str.substr(3,6));

          同時(shí)這些方法的值可以是負(fù)值;其中,slice()會(huì)將傳入的負(fù)值與字符串的長(zhǎng)度相加,substr()將負(fù)的第一個(gè)參數(shù)加上字符串的長(zhǎng)度,而將負(fù)數(shù)的第二個(gè)參數(shù)轉(zhuǎn)換為0,substring()方法會(huì)把所有負(fù)值參數(shù)都轉(zhuǎn)換為0,如:

          var str = "zero network";
          alert(str.slice(-3));
          alert(str.substring(-3));
          alert(str.substr(-3));
          alert(str.slice(3,-4));
          alert(str.substring(3,-4));
          alert(str.substr(3,-4));

          一個(gè)小示例:

          <style>
          #mydiv{width: 100px; height: 1.5em; border: 1px solid; overflow: hidden;}
          </style>
          <div id="mydiv"></div>
          <script>
          var msg="北京零點(diǎn)網(wǎng)絡(luò)科技有限公司";
          var spacer="...";
          var pos=0;
          var mydiv = document.getElementById("mydiv");
          function ScrollMsg(){
              mydiv.innerHTML = msg.substring(pos,msg.length) + spacer + msg.substring(0, pos);
              pos++;
              if(pos>msg.length) pos=0;
              window.setTimeout("ScrollMsg()",200);
          }
          ScrollMsg();
          </script>

          3)字符串位置方法:

          有兩個(gè)可以從字符串中查找子字符串的方法:indexOf()和lastIndexOf();這兩個(gè)方法都是從一個(gè)字符串中搜索給定的子字符串,然后返回子字符串的位置,如果沒(méi)有找到子字符串,則返回-1;兩個(gè)方法的區(qū)別在于,一個(gè)從開(kāi)頭向后搜索子字符串,而lastIndexOf是從末尾向前搜索,如:

          var str = "zero network";
          alert(str.indexOf("o"));
          alert(str.lastIndexOf("o"));

          說(shuō)明:如果o在字符串只出現(xiàn)了一次,則兩個(gè)方法會(huì)返回相同的位置值;

          這兩個(gè)方法都可以接受可選的第二個(gè)參數(shù),表示從字符串中的哪個(gè)位置開(kāi)始搜索;如:

          var str = "zero network";
          alert(str.indexOf("o",6));
          alert(str.lastIndexOf("o",6));

          在使用第二個(gè)參數(shù)的情況下,可以循環(huán)調(diào)用這兩個(gè)方法來(lái)找到所有匹配的子字符串,如:

          var str = "lorem ipsum dolor sit amet, consectetur adipisicing elit";
          var positions = new Array();
          var pos = str.indexOf("e");
          while(pos>-1){
              positions.push(pos);
              pos = str.indexOf("e",pos + 1);
          }
          alert(positions);  // 3,24,32,35,52

          4)trim()方法:

          ECMAScript5為所有字符串定義了trim()方法;該方法會(huì)創(chuàng)建一個(gè)字符串的副本,刪除前置及后綴的所有空格,然后返回結(jié)果,如:

          var str = "  zero network    ";
          var trimStr = str.trim();
          alert(str);
          alert(trimStr);

          此外,還有兩個(gè)非標(biāo)準(zhǔn)的trimLeft()和trimRight()方法,分別用于刪除字符串開(kāi)頭和末尾的空格;

          var trimStr = str.trimLeft();
          var trimStr = str.trimRight();

          5)字符串大小寫(xiě)轉(zhuǎn)換方法:

          涉及字符串大小寫(xiě)轉(zhuǎn)換的方法有4個(gè):

          • toLocaleLowerCase()把字符串轉(zhuǎn)換為小寫(xiě);
          • toLocaleUpperCase()把字符串轉(zhuǎn)換為大寫(xiě);
          • toLowerCase()把字符串轉(zhuǎn)換為小寫(xiě);
          • toUpperCase()把字符串轉(zhuǎn)換為大寫(xiě);

          其中toLowerCase()和toUpperCase()是最常用的方法;而toLocaleLowerCase()和toLocaleUpperCase()則是針對(duì)特定地區(qū)的實(shí)現(xiàn),對(duì)有些地區(qū)來(lái)說(shuō),針對(duì)地區(qū)的方法與其通用方法得到的結(jié)果相同,但少數(shù)語(yǔ)言(如土耳其語(yǔ))會(huì)為Unicode大小寫(xiě)轉(zhuǎn)換應(yīng)用特殊的規(guī)則,這時(shí)候就必須使用針對(duì)地區(qū)的方法來(lái)保證實(shí)現(xiàn)正確的轉(zhuǎn)換,如:

          var str = "ZERO network";
          alert(str.toLocaleLowerCase());
          alert(str.toLocaleUpperCase());
          alert(str.toLowerCase());
          alert(str.toUpperCase());

          6)字符串的模式匹配方法:

          String類(lèi)型定義了幾個(gè)用于在字符串中匹配模式的方法;

          match()方法:本質(zhì)上與RegExp的exec()方法相同;其只接受一個(gè)參數(shù),要么是一個(gè)正則表達(dá)式,要么是一個(gè)RegExp對(duì)象,如:

          var text = "cat, bat, sat, fat";
          var pattern = /.at/;
          var matches = text.match(pattern);
          alert(matches);
          alert(matches.index);
          alert(matches[0]);
          alert(pattern.lastIndex);

          說(shuō)明:match()返回了一個(gè)數(shù)組,其第一項(xiàng)是與整個(gè)模式匹配的字符串,之后的每一項(xiàng)(如果有)保存著與正則表達(dá)式中的捕獲組匹配的字符串;

          search()方法:接受的參數(shù)與match()一樣;該方法返回字符串中第一個(gè)匹配項(xiàng)的索引;如果沒(méi)有找到匹配項(xiàng),則返回-1;如:

          var text = "cat, bat, sat, fat";
          var pos = text.search(/at/);
          alert(pos);  // 1

          replace()方法:此方法的目的是為了簡(jiǎn)化替換子字符串的操作;該方法接受兩個(gè)參數(shù),第一個(gè)參數(shù)可以是一個(gè)RegExp對(duì)象或一個(gè)字符串(這個(gè)字符串不會(huì)被轉(zhuǎn)換成正則表達(dá)式),第二個(gè)參數(shù)可以是一個(gè)字符串或一個(gè)函數(shù);如果第一個(gè)參數(shù)是字符串,那么只會(huì)替換第一個(gè)子字符串,要想替換所有子字符串,唯一的辦法就是提供一個(gè)正則表達(dá)式,而且要指定全局g標(biāo)志,如:

          var text = "cat, bat, sat, fat";
          var result = text.replace("at","ond");
          alert(result);  // cond, bat, sat, fat
          result = text.replace(/at/g,"ond");
          alert(result);  // cond, bond, sond, fond

          split()方法:

          可以基于指定的分隔符將一個(gè)字符串分割成多個(gè)子字符串,并將結(jié)果放在一個(gè)數(shù)組中;分隔符可以是字符串,也可以是RegExp對(duì)象;其可以接受可選的第二個(gè)參數(shù),用于指定數(shù)組的大小,以便確保返回的數(shù)組不會(huì)超過(guò)既定大小,如:

          var colorText = "red,blue,green,yellow";
          var colors1 = colorText.split(",");
          var colors2 = colorText.split(",",2);
          var colors3 = colorText.split(/[^\,]+/);
          alert(colors1);  // red,blue,green,yellow
          alert(colors2);  // red,blue
          alert(colors3);  // ,,,,,,,

          7)localeCompare()方法:

          localeCompare()用本地特定的順序來(lái)比較兩個(gè)字符串,默認(rèn)返回下列值中的一個(gè):-1、0、1,如:

          var str = "yellow";
          alert(str.localeCompare("brick")); // 1
          alert(str.localeCompare("yellow")); // 0
          alert(str.localeCompare("zoo")); // -1
          注:利用localeCompare()可以自定義返回的值,如:
          function determineOrder(value){
              var result = stringValue.localeCompare(value);
              if(result  < 0){
                  alert("yellow在'"+value+"'之前");
              }else if(result > 0){
                  alert("yellow在'"+value+"'之后");
              }else{
                  alert("yellow與'"+value+"'相等");
              }
          }
          determineOrder("brick");
          determineOrder("yellow");
          determineOrder("zoo");

          localeCompare()方法比較與眾不同的地方,就是實(shí)現(xiàn)所支持的地區(qū)(國(guó)家和語(yǔ)言)決定了這個(gè)方法的行為;如,美國(guó)以英語(yǔ)作為ECMAScript實(shí)現(xiàn)的標(biāo)準(zhǔn)語(yǔ)言,因此localeCompare()就是區(qū)分大小寫(xiě)的,于是大寫(xiě)字母在字母表中排在小寫(xiě)字母前頭就成為了一項(xiàng)決定性的比較規(guī)則;在其他地區(qū)有可能就不是這種情況了。

          8)fromCharCode()方法:

          String構(gòu)造函數(shù)本身還有一個(gè)靜態(tài)方法:fromCharCode(),這個(gè)方法的任務(wù)是接受一或多個(gè)字符編碼,然后將它們轉(zhuǎn)換成一個(gè)字符串;從本質(zhì)上看,這個(gè)方法與實(shí)例方法charCodeAt()執(zhí)行的是相反的操作,如:

          alert(String.fromCharCode(104,101,108,108,111));  // hello

          9)HTML方法:

          早期的Web瀏覽器可以使用Javascript動(dòng)態(tài)格式化HTML,其擴(kuò)展了字符串的標(biāo)準(zhǔn),實(shí)現(xiàn)了一些專(zhuān)門(mén)用于簡(jiǎn)化常見(jiàn)HTML格式化任務(wù)的方法;但是,盡量不要使用這些方法,因?yàn)樗鼈儎?chuàng)建的標(biāo)記通常無(wú)法表達(dá)語(yǔ)義;

          • anchor(name)創(chuàng)建 HTML 錨,輸出如:<a name=”name”>string</a>
          • big()用大號(hào)字體顯示字符串,如:<big>string</big>
          • small()使用小字號(hào)來(lái)顯示字符串,如:<small>string</small>
          • blink()顯示閃動(dòng)字符串;
          • bold()使用粗體顯示字符串,如:<b>string></b>
          • fontcolor(color)使用指定的顏色來(lái)顯示字符串,如:<font color=”color>string</font>
          • fontsize(size)使用指定的尺寸來(lái)顯示字符串,如:<font size=”size”>string</font>
          • italics()使用斜體顯示字符串,如:<i>string</i>
          • link(url)將字符串顯示為鏈接,如:<a href=”url”>string</a>
          • fixed()以打字機(jī)文本顯示字符串,如:<tt>string></tt>
          • sup()把字符串顯示為上標(biāo),如:<sup>string</sup>
          • sub()把字符串顯示為下標(biāo),如:<sub>string</sub>
          • strike()使用刪除線來(lái)顯示字符串,如:<strike>string</strike>

          示例:檢測(cè)上傳文件后綴名:

          <input type="file" id="myFile"/>
          <input type="button" value="上傳" onclick="upFile()" />
          <script>
          function upFile(){
              var fileName = document.getElementById('myFile').value;
              if(fileName){
                  // console.log(fileName);
                  var pos = fileName.lastIndexOf('.');  // 找到路徑中最后出現(xiàn)“.”的位置
                  // console.log(pos);
                  var suffix = fileName.substr(pos + 1);  // 找到后綴名
                  // console.log(suffix);
                  var suffixArr = ['jpg','png','gif'];  // 圖片格式數(shù)組
                  // console.log((suffixArr.indexOf(suffix)));
                  if(suffixArr.indexOf(suffix) >= 0){  // 后綴名與格式數(shù)組作比較
                      // 上傳圖片的操作處理
                      console.log("上傳成功");
                  }else{
                      console.log("圖片格式不正確");
                      return;
                  }
              }
          }
          </script>

          示例:過(guò)濾臟話:

          // 簡(jiǎn)單過(guò)濾
          var str = "不要相信女人,女人太壞了";
          var arr = ["壞","笨","傻"];  // 敏感字
          for(var i = 0; i<arr.length; i++){
              str = str.replace(arr[i],"*");
          }
          console.log(str);
          // 復(fù)雜的過(guò)濾
          var arrStr = [
              "傻子,你的腦子是不是有病?你就是傻子一個(gè)!",
              "你的眼睛是不是瞎了?",
              "你是一個(gè)大壞蛋!"
          ];
          var arr = ["病","傻子","瞎了","壞蛋"];
          for(var i =0;i<arrStr.length; i++){
              for(var j=0;j<arr.length; j++){
                  arrStr[i] = arrStr[i].replace(arr[j],"**");
          // var reg = new RegExp(arr[j],"img");
                 // arrStr[i] = arrStr[i].replace(reg,"**");
           
              }
          }
          console.log(arrStr);
           


          Web前端開(kāi)發(fā)之Javascript-零點(diǎn)程序員-王唯


          文源代碼獲取:文末

          啟動(dòng)一個(gè)新項(xiàng)目并從頭開(kāi)始處理身份驗(yàn)證和授權(quán)可能會(huì)筋疲力盡。它通常涉及創(chuàng)建登錄和注冊(cè)功能,這可能很耗時(shí)。管理刷新令牌和實(shí)現(xiàn)雙因素身份驗(yàn)證等挑戰(zhàn)增加了復(fù)雜性。

          值得慶幸的是,隨著 .NET 8 的到來(lái),這些任務(wù)大大簡(jiǎn)化,只需要最少的配置。

          在本文中,我將指導(dǎo)你完成各種標(biāo)識(shí)配置方案,包括:

          • 基本注冊(cè)和登錄。

          • 使用持有者令牌保護(hù)終結(jié)點(diǎn)。

          • 檢索用戶(hù)信息。

          • 電子郵件確認(rèn)。

          • 重新發(fā)送電子郵件確認(rèn)。

          • 更改默認(rèn)設(shè)置并添加自定義用戶(hù)屬性。

          • 配置雙因素身份驗(yàn)證 (MVC)。

          先決條件:.NET 8 或更高版本。

          在繼續(xù)之前,讓我概述一下我當(dāng)前的環(huán)境設(shè)置:

          • .NET Web API 應(yīng)用程序。

          • Visual Studio 2022 年。

          • 利用SQLite模擬真實(shí)數(shù)據(jù)庫(kù),而不是內(nèi)存數(shù)據(jù)庫(kù)。

          接下來(lái),讓我們安裝必要的 NuGet 包。

          dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore 
          dotnet add package Microsoft.EntityFrameworkCore.Design
          dotnet add package Microsoft.EntityFrameworkCore.Sqlite
          dotnet add package Microsoft.EntityFrameworkCore.Tools

          基本注冊(cè)和登錄。

          首先,首先創(chuàng)建所有身份生成的表的存儲(chǔ)位置。

          // IdentityUser is the Microsoft base identity class. 
          // creates empty scheme with just all the identity tables.
          class AppDbContext : IdentityDbContext<IdentityUser>
          {
          public AppDbContext(DbContextOptions options) : base(options)
          {
          }
          }

          接下來(lái),我們將注冊(cè)身份驗(yàn)證和授權(quán)中間件,從而靈活地在持有者令牌或 Cookie 之間進(jìn)行選擇。在此示例中,我們將選擇持有者令牌,以便于在登錄時(shí)檢索持有者令牌。

          // Program.cs 
          builder.Services.AddAuthentication()
          .AddBearerToken(IdentityConstants.BearerScheme);

          builder.Services.AddAuthorizationBuilder();

          之后,我們將注冊(cè)我們的 ,在此示例中,它是一個(gè) SQLite 數(shù)據(jù)庫(kù)。

          // Program.cs 
          builder.Services.AddDbContext<AppDbContext>(options=>
          {
          options.UseSqlite("DataSource=app.db");
          });

          之后,我們需要將 作為身份存儲(chǔ)連接,以便所有操作都指向 SQLite 數(shù)據(jù)庫(kù)。

          // Program.cs 
          builder.Services.AddIdentityCore\<IdentityUser>()
          .AddEntityFrameworkStores\<AppDbContext>()
          .AddApiEndpoints();

          最后,我們需要將所有端點(diǎn)添加為應(yīng)用程序端點(diǎn)的一部分。這可以通過(guò)在構(gòu)建生成器后映射標(biāo)識(shí)終結(jié)點(diǎn)來(lái)實(shí)現(xiàn)。

          app.MapIdentityApi<IdentityUser>();

          您的Program.cs文件現(xiàn)在應(yīng)類(lèi)似于此結(jié)構(gòu)。

          using Microsoft.AspNetCore.Identity; 
          using Microsoft.EntityFrameworkCore;

          var builder=WebApplication.CreateBuilder(args);

          builder.Services.AddControllers();
          builder.Services.AddEndpointsApiExplorer();
          builder.Services.AddSwaggerGen();

          builder.Services.AddAuthentication()
          .AddBearerToken(IdentityConstants.BearerScheme);

          builder.Services.AddAuthorizationBuilder();

          builder.Services.AddDbContext<AppDbContext>(options=>
          {
          options.UseSqlite("DataSource=app.db");
          });

          builder.Services.AddIdentityCore<IdentityUser>()
          .AddEntityFrameworkStores<AppDbContext>()
          .AddApiEndpoints();

          var app=builder.Build();

          app.MapIdentityApi<IdentityUser>();


          app.UseSwagger();
          app.UseSwaggerUI();
          app.UseHttpsRedirection();
          app.UseAuthorization();
          app.MapControllers();

          app.Run();

          在運(yùn)行應(yīng)用程序之前,請(qǐng)不要忘記添加新的遷移,并通過(guò)從包管理器控制臺(tái)執(zhí)行以下命令來(lái)更新數(shù)據(jù)庫(kù)。

          Add-Migration initial 
          Update-Database

          現(xiàn)在,運(yùn)行應(yīng)用程序并打開(kāi) Swagger。您應(yīng)該會(huì)看到自動(dòng)生成的終結(jié)點(diǎn)集合。

          生成的身份終結(jié)點(diǎn)。

          嘗試注冊(cè)新用戶(hù)。如果嘗試使用無(wú)效的電子郵件地址或不符合條件的密碼創(chuàng)建新用戶(hù),則標(biāo)識(shí)將返回錯(cuò)誤以及發(fā)生的具體詳細(xì)信息。

          現(xiàn)在,讓我們繼續(xù)登錄過(guò)程。如果提供的電子郵件或密碼無(wú)效,則身份將返回 401 未授權(quán)狀態(tài)。

          使用持有者令牌保護(hù)終結(jié)點(diǎn)。

          將身份驗(yàn)證配置為返回持有者令牌后,我們需要將此持有者令牌用于任何需要授權(quán)的受保護(hù)終結(jié)點(diǎn)。

          必須注意的是,生成的令牌不是標(biāo)準(zhǔn)的 JSON Web 令牌 (JWT)。此決定是有意為之的,因?yàn)閮?nèi)置標(biāo)識(shí)主要用于簡(jiǎn)單方案。令牌選項(xiàng)并不意味著充當(dāng)功能齊全的身份服務(wù)提供商或令牌服務(wù)器,而是無(wú)法使用 cookie 的客戶(hù)端的 cookie 選項(xiàng)的替代方案。

          現(xiàn)在,讓我們嘗試訪問(wèn)授權(quán)端點(diǎn),特別是 /manage/info 端點(diǎn)。如果您嘗試從 Swagger 訪問(wèn)它,它將顯示未經(jīng)授權(quán)的錯(cuò)誤。接下來(lái),使用 Postman 登錄,保存 accessToken,并在調(diào)用 /manage/info 端點(diǎn)時(shí)包含它。這一次,它應(yīng)該返回所需的結(jié)果。

          檢索用戶(hù)信息

          要隨時(shí)檢索當(dāng)前登錄用戶(hù)的信息,請(qǐng)使用存儲(chǔ)當(dāng)前登錄用戶(hù)信息的類(lèi)。ClaimsPrincipal

          要使用最小 API 方法實(shí)現(xiàn)此目的,請(qǐng)執(zhí)行以下操作:

          app.Map("/", (ClaimsPrincipal user)=> $"Hello {user.Identity!.Name}") 
          .RequireAuthorization();

          對(duì)于控制器方法:

          [ApiController] 
          [Route("[controller]")]
          public class TestController: ControllerBase
          {
          [HttpGet]
          [Authorize]
          public string Get()
          {
          return User.Identity!.Name;
          }
          }

          請(qǐng)務(wù)必注意,上述示例使用“**!”**運(yùn)算符,這意味著標(biāo)識(shí)應(yīng)始終存在,而不可能為 。此假設(shè)成立,因?yàn)閮烧叨际切枰孪鹊卿浀氖跈?quán)端點(diǎn)。但是,建議驗(yàn)證標(biāo)識(shí)是否存在,以防止?jié)撛诘?引用異常。

          電子郵件確認(rèn)

          Microsoft建議使用SendGrid或其他電子郵件服務(wù)發(fā)送電子郵件,而不是SMTP。SMTP 很難保護(hù)和正確設(shè)置。

          在本教程中,SendGrid 用于發(fā)送電子郵件。發(fā)送電子郵件需要 SendGrid 帳戶(hù)和密鑰。請(qǐng)參閱免費(fèi)開(kāi)始使用 SendGrid,注冊(cè)免費(fèi)的 SendGrid 帳戶(hù)。

          1- 在文件中配置 SendGrid 所需信息。避免將這些設(shè)置直接添加到appsettings.json文件中,以降低安全風(fēng)險(xiǎn)。secrets.json

          { 
          "SendGridKey": "your send-grid key",
          "From": "your registered email",
          "Name": "your registered name"
          }

          注冊(cè)電子郵件和姓名

          SendGrid API 密鑰

          2- 安裝必要的 NuGet 包。

          dotnet add package SendGrid 
          dotnet add package SendGrid.Extensions.DependencyInjection

          3-實(shí)現(xiàn)IEmailSender

          要實(shí)現(xiàn),請(qǐng)使用類(lèi)似于以下內(nèi)容的代碼進(jìn)行創(chuàng)建:IEmailSenderEmailSender.cs

          using Microsoft.AspNetCore.Identity.UI.Services; 
          using SendGrid;
          using SendGrid.Helpers.Mail;

          namespace Identity;

          public class EmailSender : IEmailSender
          {
          private readonly ILogger _logger;
          private readonly IConfiguration _configuration;

          public EmailSender(IConfiguration configuration, ILogger<EmailSender> logger)
          {
          _configuration=configuration;
          _logger=logger;
          }

          public async Task SendEmailAsync(string toEmail, string subject, string message)
          {
          var sendGridKey=_configuration["SendGridKey"];
          ArgumentException.ThrowIfOrEmpty(sendGridKey, nameof(sendGridKey));
          await Execute(sendGridKey, subject, message, toEmail);
          }

          public async Task Execute(string apiKey, string subject, string message, string toEmail)
          {
          var client=new SendGridClient(apiKey);

          var msg=new SendGridMessage()
          {
          From=new EmailAddress(_configuration["From"], _configuration["Name"]),
          Subject=subject,
          PlainTextContent=message,
          HtmlContent=message
          };

          msg.AddTo(new EmailAddress(toEmail));

          // Disable click tracking.
          // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
          msg.SetClickTracking(false, false);

          var response=await client.SendEmailAsync(msg);
          _logger.LogInformation(response.IsSuccessStatusCode
          ? $"Email to {toEmail} queued successfully!"
          : $"Failure Email to {toEmail}");
          }
          }

          4- 將 EmailService 注入其中以啟用其使用。Program.cs

          // Program.cs 
          builder.Services.AddTransient<IEmailSender, EmailSender>();

          5- 強(qiáng)制登錄確認(rèn)并集成 SendGrid 服務(wù)。

          現(xiàn)在,如果您嘗試再次注冊(cè),它將起作用,但您會(huì)注意到?jīng)]有發(fā)送電子郵件確認(rèn)。此外,用戶(hù)無(wú)需任何確認(rèn)即可登錄。

          為了解決這個(gè)問(wèn)題并確保正確的流程,我們需要首先在簽名過(guò)程中要求電子郵件確認(rèn)。然后,我們需要集成 SendGrid 服務(wù),以便標(biāo)識(shí)框架可以利用它。

          // Program.cs 

          builder.Services.AddIdentityCore<IdentityUser>(options=>
          {
          options.SignIn.RequireConfirmedEmail=true;
          })

          builder.Services.AddSendGrid(options=>
          options.ApiKey=builder.Configuration["SendGridKey"]!
          );

          您的文件現(xiàn)在應(yīng)類(lèi)似于此結(jié)構(gòu)。Program.cs

          using Identity; 
          using Microsoft.AspNetCore.Identity;
          using Microsoft.AspNetCore.Identity.UI.Services;
          using Microsoft.EntityFrameworkCore;
          using SendGrid.Extensions.DependencyInjection;
          using System.Security.Claims;

          var builder=WebApplication.CreateBuilder(args);

          builder.Services.AddControllers();
          builder.Services.AddEndpointsApiExplorer();
          builder.Services.AddSwaggerGen();

          builder.Services.AddAuthentication()
          .AddBearerToken(IdentityConstants.BearerScheme);

          builder.Services.AddSendGrid(options=>
          options.ApiKey=builder.Configuration["SendGridKey"]!
          );

          builder.Services.AddTransient<IEmailSender, EmailSender>();

          builder.Services.AddAuthorizationBuilder();

          builder.Services.AddDbContext<AppDbContext>(options=>
          {
          options.UseSqlite("DataSource=app.db");
          });

          builder.Services.AddIdentityCore<IdentityUser>(options=>
          {
          options.SignIn.RequireConfirmedEmail=true;
          })
          .AddEntityFrameworkStores<AppDbContext>()
          .AddApiEndpoints();

          var app=builder.Build();

          app.MapIdentityApi<IdentityUser>();

          app.Map("/", (ClaimsPrincipal user)=> $"Hello {user.Identity!.Name} from minimal apis.")
          .RequireAuthorization();

          app.UseSwagger();
          app.UseSwaggerUI();
          app.UseHttpsRedirection();
          app.UseAuthorization();
          app.MapControllers();

          app.Run();

          現(xiàn)在,是時(shí)候測(cè)試我們的應(yīng)用程序了。嘗試注冊(cè)一個(gè)新用戶(hù),并檢查您的電子郵件以獲取確認(rèn)鏈接。單擊該鏈接應(yīng)將數(shù)據(jù)庫(kù)中的確認(rèn)狀態(tài)更改為 TRUE。

          請(qǐng)記住檢查您的垃圾郵件文件夾,因?yàn)殡娮余]件通常最終會(huì)在那里。此外,請(qǐng)注意,電子郵件在發(fā)送之前會(huì)排隊(duì),因此預(yù)計(jì)會(huì)有幾秒鐘的輕微延遲。

          注冊(cè)新用戶(hù)

          Visual Studio 日志

          在數(shù)據(jù)庫(kù)中創(chuàng)建的用戶(hù)

          確認(rèn)鏈接電子郵件

          通過(guò)單擊鏈接“單擊此處”,它應(yīng)該將您重定向回 localhost,并將用戶(hù)在數(shù)據(jù)庫(kù)中的確認(rèn)狀態(tài)更新為 TRUE。

          確認(rèn)消息

          確認(rèn)狀態(tài)更改為 TRUE

          最后,通過(guò)注入 SendGrid 服務(wù),我們可以簡(jiǎn)化 EmailService,從而減少代碼行數(shù)。

          這是該版本的更新版本,可以有效處理所有測(cè)試方案。EmailService

          using Microsoft.AspNetCore.Identity.UI.Services; 
          using SendGrid;
          using SendGrid.Helpers.Mail;

          public class EmailSender : IEmailSender
          {
          private readonly ILogger _logger;
          private readonly IConfiguration _configuration;
          private readonly ISendGridClient _sendGridClient;


          public EmailSender(IConfiguration configuration, ILogger<EmailSender> logger, ISendGridClient sendGridClient)
          {
          _configuration=configuration;
          _logger=logger;
          _sendGridClient=sendGridClient;
          }

          public async Task SendEmailAsync(string toEmail, string subject, string message)
          {
          var msg=new SendGridMessage()
          {
          From=new EmailAddress(_configuration["From"], _configuration["Name"\]),
          Subject=subject,
          PlainTextContent=message,
          HtmlContent=message
          };
          msg.AddTo(new EmailAddress(toEmail));

          var response=await _sendGridClient.SendEmailAsync(msg);
          _logger.LogInformation(response.IsSuccessStatusCode
          ? $"Email to {toEmail} queued successfully!"
          : $"Failure Email to {toEmail}");
          }
          }

          重新發(fā)送電子郵件確認(rèn)

          您當(dāng)然需要重新發(fā)送確認(rèn)電子郵件的選項(xiàng),這對(duì)任何應(yīng)用程序都至關(guān)重要。.NET 8 Identity 提供重新發(fā)送確認(rèn)終結(jié)點(diǎn):只需調(diào)用并設(shè)置正文,如下所示:.NET 8 Identity provides a resend confirmation endpoint: just call and set the body as follows:/resendConfirmationEmail

          {
          "Email": "user email"
          }

          更改默認(rèn)設(shè)置并添加自定義用戶(hù)屬性

          Microsoft 標(biāo)識(shí)還提供修改默認(rèn)設(shè)置或引入新的自定義屬性的功能。

          調(diào)整默認(rèn)設(shè)置。

          builder.Services.Configure<IdentityOptions>(options=> 
          {
          // Password settings.
          options.Password.RequireDigit=true;
          options.Password.RequireLowercase=true;
          options.Password.RequireNonAlphanumeric=true;
          options.Password.RequireUppercase=true;
          options.Password.RequiredLength=6;
          options.Password.RequiredUniqueChars=1;

          // Lockout settings.
          options.Lockout.DefaultLockoutTimeSpan=TimeSpan.FromMinutes(5);
          options.Lockout.MaxFailedAccessAttempts=5;
          options.Lockout.AllowedForNewUsers=true;

          // User settings.
          options.User.AllowedUserNameCharacters="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.\_@+";
          options.User.RequireUniqueEmail=false;
          });

          將新的自定義屬性添加到用戶(hù)屬性中。這可以通過(guò)創(chuàng)建一個(gè)新的用戶(hù)類(lèi)來(lái)實(shí)現(xiàn),該類(lèi)繼承自 。

          class AppUser : IdentityUser 
          {
          public int MyProperty { get; set; }
          }

          隨后,需要添加新的遷移,并且應(yīng)將新屬性集成到表中。

          請(qǐng)記住將應(yīng)用程序中出現(xiàn)的每個(gè)項(xiàng)更新為IdentityUserAppUser

          配置雙因素身份驗(yàn)證 (MVC)

          在我們開(kāi)始之前,這里有一些概念,在我們開(kāi)始之前最好知道。

          什么是多重身份驗(yàn)證 (MFA)?

          多重身份驗(yàn)證 (MFA) 通過(guò)要求用戶(hù)在登錄過(guò)程中提供其他形式的身份標(biāo)識(shí)來(lái)增強(qiáng)安全性。這可能包括從手機(jī)輸入代碼、使用 FIDO2 密鑰或提供指紋掃描。通過(guò)要求第二種形式的身份驗(yàn)證,MFA 使攻擊者更難獲得未經(jīng)授權(quán)的訪問(wèn),因?yàn)楦郊右蛩夭蝗菀撰@得或復(fù)制。

          什么是雙因素身份驗(yàn)證 (2FA)?

          雙因素身份驗(yàn)證 (2FA) 類(lèi)似于 MFA 的子集,但不同之處在于 MFA 可能需要兩個(gè)或多個(gè)因素來(lái)證明身份。

          什么是TOTP(基于時(shí)間的一次性密碼算法)?

          使用 ASP.NET Core Identity 時(shí),默認(rèn)支持使用 TOTP 的 MFA。此方法可與任何合規(guī)的身份驗(yàn)證器應(yīng)用一起使用,包括:

          • Microsoft 身份驗(yàn)證器

          • 谷歌身份驗(yàn)證器

          什么是 MFA 短信?

          與密碼身份驗(yàn)證(單因素)相比,帶有 SMS 的 MFA 大大提高了安全性。但是,不再建議使用 SMS 作為第二個(gè)因素。對(duì)于此類(lèi)實(shí)現(xiàn),存在太多已知的攻擊媒介。

          什么是在線快速身份識(shí)別 (FIDO)?
          FIDO 是無(wú)密碼身份驗(yàn)證的開(kāi)放標(biāo)準(zhǔn),允許用戶(hù)在沒(méi)有密碼的情況下登錄。FIDO2 密鑰(通常是 USB,但也包括藍(lán)牙或 NFC)通過(guò)消除密碼風(fēng)險(xiǎn)來(lái)增強(qiáng)安全性。
          它們目前被認(rèn)為是最安全的 MFA 方法。

          現(xiàn)在,讓我們開(kāi)始配置 2FA。

          我們將利用 2FA 與 MVC 項(xiàng)目的集成。首次創(chuàng)建新的 MVC 項(xiàng)目時(shí),可以選擇從一開(kāi)始就生成標(biāo)識(shí)系統(tǒng),包括“注冊(cè)”、“登錄”和“2FA”。我們的第一步是創(chuàng)建一個(gè)新的 MVC 項(xiàng)目。

          在 Visual Studio 2022 中,可以完成以下操作:

          選擇個(gè)人帳戶(hù)標(biāo)識(shí)選項(xiàng)。

          或者,您可以使用命令行。

          dotnet new mvc -n "2FA" -au individual

          創(chuàng)建的項(xiàng)目應(yīng)如下所示。

          現(xiàn)在,運(yùn)行創(chuàng)建的項(xiàng)目。應(yīng)顯示一個(gè)帶有登錄和注冊(cè)按鈕的歡迎頁(yè)面。

          單擊“注冊(cè)”以創(chuàng)建新用戶(hù)。

          單擊“單擊此處確認(rèn)您的帳戶(hù)”以確認(rèn)您的電子郵件。

          接下來(lái),注銷(xiāo),然后再次登錄。點(diǎn)擊右上角的電子郵件。

          導(dǎo)航到“雙因素身份驗(yàn)證”部分。

          單擊“添加身份驗(yàn)證器應(yīng)用程序” 應(yīng)顯示雙因素身份驗(yàn)證設(shè)置屏幕。

          如您所見(jiàn),它是開(kāi)箱即用的,但沒(méi)有掃描二維碼的選項(xiàng);它僅適用于代碼。

          現(xiàn)在,讓我們添加二維碼功能,允許用戶(hù)只需掃描二維碼即可自動(dòng)添加密鑰。

          我們需要訪問(wèn)為我們創(chuàng)建的生成的 cshtml 頁(yè)面。默認(rèn)情況下,這些頁(yè)面位于目錄中。但是,如果尚未展開(kāi)目錄,則 dotnet 不會(huì)顯示文件,除非生成了文件。Areas/Identity/Pages

          為此,請(qǐng)運(yùn)行該命令。

          dotnet tool install -g dotnet-aspnet-codegenerator

          接下來(lái),我們需要安裝所有必需的 NuGet 包。

          dotnet add pacakge Microsoft.EntityFrameworkCore.Design 
          dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design

          然后,運(yùn)行代碼生成器并指定要檢索的頁(yè)面。默認(rèn)情況下,如果將其留空,它將生成許多頁(yè)面。但是,為了避免不必要的頁(yè)面使您的項(xiàng)目混亂,我們將指定我們需要的特定頁(yè)面。

          cd "your project directory" 
          dotnet aspnet-codegenerator identity -dc _2FA.Data.ApplicationDbContext --files "Account.Manage.EnableAuthenticator"

          若要查看可生成的所有文件,請(qǐng)運(yùn)行該命令。

          dotnet aspnet-codegenerator identity -dc _2FA.Data.ApplicationDbContext -lf

          以下是可以生成的所有文件的完整列表。

          File List: 
          Account.\_StatusMessage
          Account.AccessDenied
          Account.ConfirmEmail
          Account.ConfirmEmailChange
          Account.ExternalLogin
          Account.ForgotPassword
          Account.ForgotPasswordConfirmation
          Account.Lockout
          Account.Login
          Account.LoginWith2fa
          Account.LoginWithRecoveryCode
          Account.Logout
          Account.Manage.\_Layout
          Account.Manage.\_ManageNav
          Account.Manage.\_StatusMessage
          Account.Manage.ChangePassword
          Account.Manage.DeletePersonalData
          Account.Manage.Disable2fa
          Account.Manage.DownloadPersonalData
          Account.Manage.Email
          Account.Manage.EnableAuthenticator
          Account.Manage.ExternalLogins
          Account.Manage.GenerateRecoveryCodes
          Account.Manage.Index
          Account.Manage.PersonalData
          Account.Manage.ResetAuthenticator
          Account.Manage.SetPassword
          Account.Manage.ShowRecoveryCodes
          Account.Manage.TwoFactorAuthentication
          Account.Register
          Account.RegisterConfirmation
          Account.ResendEmailConfirmation
          Account.ResetPassword
          Account.ResetPasswordConfirmation

          返回到 Visual Studio,現(xiàn)在應(yīng)該會(huì)看到已添加的文件。EnableAuthenticator.cshtml

          在編輯文件之前,我們需要下載qrcode.js JavaScript庫(kù),它將為我們渲染并生成QR碼。將其保存在文件夾中。EnableAuthenticator.cshtmlwwwroot\lib

          在這里,我將其重命名為qrcodejs

          接下來(lái),創(chuàng)建一個(gè)新文件,使用以下代碼在 中調(diào)用它。qr.jswwwroot\js

          window.addEventListener("load", ()=> { 
          const uri=document.getElementById("qrCodeData").getAttribute('data-url');
          new QRCode(document.getElementById("qrCode"),
          {
          text: uri,
          width: 150,
          height: 150
          });
          });

          最后,打開(kāi)文件,然后:EnableAuthenticator.cshtml

          • 更新該部分以添加對(duì)以前下載的庫(kù)的引用。Scriptsqrcode.js

          • 添加帶有調(diào)用的文件以生成二維碼。qr.js

          @section Scripts { 
          @await Html.PartialAsync("_ValidationScriptsPartial")

          <script type="text/javascript" src="~/lib/qrcodejs/qrcode.js"></script>
          <script type="text/javascript" src="~/js/qr.js"></script>
          }

          再次運(yùn)行應(yīng)用程序,QR 碼現(xiàn)在應(yīng)該出現(xiàn)。

          使用Authenticator應(yīng)用程序掃描它,它應(yīng)該可以完美運(yùn)行。

          通過(guò)將其添加到身份驗(yàn)證器應(yīng)用,可以從應(yīng)用中生成的代碼中驗(yàn)證代碼。它應(yīng)該顯示 2FA 確認(rèn)。

          現(xiàn)在,嘗試注銷(xiāo),然后重新登錄。將出現(xiàn) 2FA 表單,提示您輸入 2FA 代碼。

          恭喜,您的 2FA 設(shè)置完美。

          源代碼獲取:公眾號(hào)回復(fù)消息【code:19129


          主站蜘蛛池模板: 久久国产精品视频一区| 亚洲一区精彩视频| 国产高清一区二区三区| 国产精品无码一区二区三区在| 中文字幕亚洲一区二区va在线| 色一乱一伦一区一直爽| 精品国产免费一区二区| 日本免费一区尤物| 免费无码AV一区二区| 亚洲中文字幕丝袜制服一区| 日本一区视频在线播放| 丝袜无码一区二区三区| 无码人妻久久久一区二区三区| 老熟妇仑乱视频一区二区 | 国产伦精品一区二区三区视频小说 | 国产福利电影一区二区三区,亚洲国模精品一区 | 成人精品视频一区二区三区尤物| 在线成人一区二区| 国产成人精品亚洲一区 | 国产91大片精品一区在线观看| 亚洲国产精品一区二区三区在线观看 | 日韩在线不卡免费视频一区| 亚洲影视一区二区| 国产萌白酱在线一区二区| 精品国产AⅤ一区二区三区4区 | 夜夜添无码一区二区三区| 色偷偷一区二区无码视频| 影院成人区精品一区二区婷婷丽春院影视| 国产精品盗摄一区二区在线| 久久一区二区免费播放| 色一乱一伦一图一区二区精品 | 国产成人精品一区二区三区免费 | 91视频一区二区| 国模大胆一区二区三区| 99久久精品国产免看国产一区 | 国产成人久久一区二区不卡三区| 人妻体内射精一区二区| 精品一区二区三区免费毛片爱| 日韩一区在线视频| 一区二区在线视频| 日本一区午夜艳熟免费|