ssh框架-Struts2(二)

来源:https://www.cnblogs.com/itxz/archive/2018/04/10/8776262.html
-Advertisement-
Play Games

上篇文章我們瞭解了怎麼配置struts.xml文件,以及前端控制器配置怎麼配置,,Action進階,Result結果配置,Struts2中的Servlet的API的訪問,以及怎麼獲得請求參數.今天我們在深入講解一下OGNL表達式,OGNL中的符號,和常用的攔截器,標簽庫 一,OGNL表達式 1.概述 ...


 

 上篇文章我們瞭解了怎麼配置struts.xml文件,以及前端控制器配置怎麼配置,,Action進階,Result結果配置,Struts2中的Servlet的API的訪問,以及怎麼獲得請求參數.今天我們在深入講解一下OGNL表達式,OGNL中的符號,和常用的攔截器,標簽庫

一,OGNL表達式

1.概述

1.1什麼是OGNL

​ OGNL是Object-Graph Navigation Language的縮寫,俗稱對象圖導航語言. 它是一種功能強大的表達式語言,通過它簡單一致的表達式語法,可以存取對象的任意屬性,調用對象的方法,遍歷整個對象的結構圖,實現欄位類型轉化等功能。

​ Eg: hibernate 查詢方式 : 對象導航查詢.

​ 其實就是查詢出來了一個對象之後,通過對象裡面的getXXX() 來獲取關聯的對象。

​ 它是一個開源項目,並不是struts發明出來的,只是struts裡面預設的表達式是使用OGNL表達式.

1.2OGNL作用
  • 獲取對象的成員(支持對象方法的調用,支持對象對象屬性訪問,支持靜態方法的調用,支持集合對象操作)

  • 進行運算

2.OGNL要素

​ OGNL 有三個核心元素 ,我們只有理解這三個核心元素,才能去更好的學習OGNL表達式

2.1表達式 (Expression)

​ 表達式用於表示我們到底想執行什麼操作 。 比如:想獲取成員 ,直接用對象.成員名字就可以得到該成員的值

2.2 根元素 (Root)

​ OGNL 表述的是對象導航語言、那麼必須的指明根元素是什麼。 好比我們要在樹上找果子、必須指定到底是那一棵樹是一樣的道理。

2.3上下文 (Context)

​ 上下文其實就是我們的那個root寄存的位置,它在記憶體當中其實就是一個Map .OGNL 有一個類專門用來表示上下文環境 叫做OGNLContext , 它其實就是一個Map.

    • EG:Map裡面存放了很多的User對象, 那麼我們必須指明是在哪一個根上面找這些user對象。

3.OGNL入門

​ 大家在使用OGNL的時候,要在腦海中有一個想像,就是記憶體中已經有了一個對象,我們需要去操作這個對象,獲取裡面的成員,或者去調用它的某個方法。那麼表達式應該怎麼寫。這和我們的EL 表達式有異曲同工之妙,都是用於獲取對象、然後再關聯到具體的某一個成員.當然我們現在給大家在代碼裡面演示OGNL的一些用法,一般我們使用OGNL 最多的地方還是在jsp頁面上。

​ 使用OGNL先導入兩個Jar包ognl-xxx.jar,javassist-xxx.jar

3.1表達式與根對象
    • 訪問根對象屬性

/**
     * OGNL表達式和根對象: 訪問屬性
     * @throws OgnlException 
     */
    @Test
    public void  fun01() throws OgnlException{
        //根對象
        User root = new User("張三", 18);
        //表達式(想得到什麼)
        String expression = "username";
        //通過Ognl方式獲得根對象中的表達式結果
        Object value = Ognl.getValue(expression , root);
        System.out.println("value="+value);
        
    }
    • 訪問根對象方法
/**
         * OGNL表達式和根對象: 訪問方法
     * @throws OgnlException 
     */
    @Test
    public void  fun02() throws OgnlException{
        //根對象
        User root = new User("張三", 18);
        //表達式(想得到什麼)
        String expression = "getUsername()";
        //通過Ognl方式獲得根對象中的表達式結果
        Object value = Ognl.getValue(expression , root);
        System.out.println("value="+value);
        
    }        
  3.2表達式和上下文
  •  結合上下文訪問根對象屬性
    @Test
    public void  fun03() throws OgnlException{
        User user1 = new User("張三", 18);
        User user2 = new User("李四", 19);
        //上下文
        Map<String, User> context  = new HashMap<String, User>();
        context.put("user1", user1);
        context.put("user2", user2);
        
        //根對象(指定user1為根對象)
        Object root = user1;
        
        //表達式(想得到什麼)
        String expression = "username";
        //通過Ognl方式獲得根對象中的表達式結果(獲得根對象的username的值,也就是user1的)
        Object value = Ognl.getValue(expression, context, root);
        System.out.println("value="+value);
        
        //獲得非根對象的值(user2的)
        expression = "#user2.username";
        value = Ognl.getValue(expression, context, root);
        System.out.println("value="+value);
        
    }
        
  • 翻譯上面的getValue方法的含義是: 在上下文 context裡面找到一個 根user1 然後取它的name值

    ​ 如果想要獲取user2的數據 因為user2不是根對象,所以要想取它的值,需要寫成 #key.name 。 String expression = "#user2.name"

二,ValueStack值棧

  1.概述

   1.1什麼是值棧

​     value stack : 翻譯過來是值棧的意思。

      ​ 回想以前我們在學習servlet的時候,我們要想 servlet操作完成後,生成了一個集合或者對象數據, 在頁面上顯示,我們通常的做法是。把這個對象或者集合存儲在作用域裡面,然後到頁面上再取出來。

     strtus 框架是對我們的servlet進行了包裝 ,它內部自己也有一套存值和取值的機制 ,這一套機制就是現在說的 值棧。

2.值棧創建的時機

2.1 Servlet和Action的區別
  • Servlet: Servlet只會創建一次實例,以後再過來請求,不會創建實例

  • Action: Action是多例,來一次請求就創建一次實例。 創建一次Action的實例,就創建一次ActionContext實例,並且就創建出來一個值棧的實例。

2.2什麼時候創建值棧
  • 請求到來的時候才會創建值棧, 當來了請求,會執行前端控制器的doFilter方法,在doFilter方法裡面,有如下代碼

prepare.createActionContext(request, response);
  • 在createActionContext()這個方法內部

//從ThreadLocal裡面獲取ActionContext實例,一開始是沒有的,所以該對象是 null
 ActionContext oldContext = ActionContext.getContext();
    if (oldContext != null) {
      // detected existing context, so we are probably in a forward
      ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
    } else {
​
      //創建值棧對象  是OgnlValueStack 對象
     ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
​
    //給值棧裡面的上下文區域存東西 。 存request \ response \session \application...
    stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
    //創建ActionContext的對象。 然後跟值棧關聯上
    ctx = new ActionContext(stack.getContext());
}

3.值棧的內部結構

​ 我們查看值棧的內部結構,其實是想研究值棧存值的時候,是存到了什麼地方去的。

  • 值棧其實就是一個類 OgnlValueStack ,我們如果往值棧存值, 其實是存到了這個類中的兩個集合去。

CompoundRoot root;  //這其實是一個集合  list
transient Map<String, Object> context;  //這是OGNL上下文  OGNLContext
//底下還有一行代碼 
 context.setRoot(root);
//也就是設置了OGNL上下文的根其實就是一個list集合 。  說的具體一點。 就是那個CompoundRoot對象。

我們使用OGNL表達式取值的時候,都是去上下文裡面取值。

​ root 區,根對象。具體類型為CompoundRoot。CompoundRoot實現了List介面,其實就是一個list集合。其實我們存值的話都是存在root區域.

​ context 區:上下文對象。具體類型為OgnlContext。OgnlContext內部操控的是一個Map集合,其實context區可以理解為一個Map集合。

4.獲得ValueStack

4.1通過ActionContext對象的實例獲得

​ 由於前面創建好值棧後,讓它和ActionContext關聯上了,所以我們只要通過ActionContext去獲取它即可。

  • 獲得值棧代碼

  ValueStack valueStack = ActionContext.getContext().getValueStack();  

4.2通過request域獲得ValueStack

​ 在執行完我們的Action方法前,struts會把值棧往request對象裡面存起來,所以我們使用request對象即可獲取到.

在前端過濾器的doFilter()方法裡面有 execute.executeAction(request, response, mapping);這行代碼,執行Action.

具體源碼參見: Dispatcher類中的serviceAction方法 , 位於540行:request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());所以後續我們也只要使用request對象然後結合對應的STRUTS_VALUESTACK_KEY 這個key即可獲取到值棧

  • 獲得值棧代碼

 ValueStack valueStack = (ValueStack) ServletActionContext.getRequest().getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY); 

5.值棧存取值

5.1push方式

​ 直接放置在棧頂,沒有什麼key與之對應。所以取值的話,直接寫屬性名即可。 但是如果push 了多個,並且還想獲取的不是棧頂的值,是棧頂下來的某一個位置的值,那麼可以採用[0] \ [1] 這種做法

  • 存值Java代碼

User user = new User("admin" ,"10086");
ValueStack stack = ActionContext.getContext().getValueStack();
//執行的是壓棧的動作。 放置的東西永遠會在棧頂。
stack.push(user );
​
//第二次的時候,user02已經位於棧頂 ,之前的那個user就要往下壓一格
User user02 = new User("zhangsan" ,"1000000");
stack.push(user02 );
  • 取值(演示[0]和[1]的區別)

取push放的值<br>
<s:property value="username"/> , <s:property value="password"/><br><br>取push的值 就取第二個,不取棧頂<br>
<s:property value="[1].top.username"/>  , <s:property value="[1].password"/><br>
5.2set方式

​ set 和 push 的區別在於, push 是直接把數據壓倒棧頂 , 但是set方式是用一個map集合來包裝數據,然後才把map壓倒棧頂。 所以取值手法也有點不一樣。

  • 存值Java代碼

 User user = new User("admin" ,"10086"); ValueStack stack = ActionContext.getContext().getValueStack(); stack.set("user01", user); 

  • 取值

取set放的值<br>
<s:property value="user01.username"/> , <s:property value="user01.password"/><br>
5.3屬性驅動方式

​ 和之前的獲得請求參數屬性驅動類似.

  • 存值Java代碼

public class ActionDemo01 extends ActionSupport {
    private String str;
    public String fun01(){
        str = "hello";
        return "success";
    }
    //提供get方法
    public String getStr() {
        return str;
    }
​
}
  • 取值

取屬性封裝的值<br> <s:property value="str"/>

5.4模型驅動方式
  • 存值

public class ActionDemo01 extends ActionSupport implements ModelDriven<User> {
    private User user;
    public String fun01(){
        return "success";
    }
    @Override
    public User getModel() {
        if(user == null){
            user = new User("李四", "66666666");
        }
        return user;
    }
​
}
  • 取值

<s:property value="model.username"/> , <s:property value="model.password"/><br>
或者:
<s:property value="username"/> , <s:property value="password"/><br>

由於使用模型驅動封裝,存值的時候,也還是存到action的範圍裡面去.

5.5使用EL表達式取值

​ EL表達式也可以取到值棧的值,本來EL表達式是用於取作用域的值,但是值棧和作用域是兩個東西。 為什麼EL 表達式也能去值棧的值呢?原因是 : struts對EL 表達式取值的代碼進行了擴展,如果從作用域取不到值就會去值棧找。

​ reuqest.setAttribute("user" , user); ${user.name} ------> pageContext.findAttrbute(); --> 先從page範圍找, 沒有,就找request, 還沒有就找session。request類型被包裝成了 StrutsRequestWrapper 在裡面的getAttribute 做了判定,如果從作用域中去不到值,就去值棧取值

6.OGNL中的符號

6.1#號的作用
  • 獲取Context中的數據,非根對象裡面的數據



<s:property value="#request.name"/>
<s:property value="#session.name"/>
  • 構建一個Map集合

//var變數會存一份到root也會存一份到上下文
<s:iterator var="entry" value="#{ 'aa':'11','bb':'22','cc':'33' }"> 
<s:property value="key"/>--<s:property value="value"/><br/>
<s:property value="#entry.key"/>--<s:property value="#entry.value"/><br/>
</s:iterator>
6.2%號的作用
  • 強制解析OGNL表達式

<s:textfield value="%{#request.name}" name="name"/>
  • 強制不解析OGNL表達式

<s:property value="%{'#request.name'}"/>

三,攔截器

1.概述

1.1什麼是攔截器

​ 在struts2中,攔截器(Interceptor)是用來動態攔截Action執行的對象。

​ 攔截器有點類似以前Servlet階段的Filter(過濾器) , 能夠在請求到達Action之前進行攔截操作, 可以在裡面進行判斷校驗。 典型的例子: 登錄攔截.

註:過濾器可以過濾servlet, 攔截器可以攔截Action , 過濾器除了可以過濾servlet之外,還可以過濾網路資源(html / jsp ) , 攔截器只能攔截Action

1.2 攔截器執行流程

​ 客戶端請求Action,執行核心過濾器,在核心過濾器內部創建了Action的代理類,調用代理類的execute方法,在execute方法內部執行ActionInvocation的invoke方法。在invoke方法內部遞歸調用攔截器的攔截的方法。如果沒有下一個攔截器執行目標Action,Action執行結束後根據Result進行頁面跳轉,執行攔截器的後面相應的代碼,最後由response對象生成響應。

  • 流程圖

 

 

 

  • 時序圖

 

 

2.自定義攔截器

2.1方式一:實現Interceptor介面

​ 最原始的方式,也是最基本的方式. 要實現三個方法,其中init 和 destroy ,平常我們都不太用。

  • 創建一個類實現Interceptor介面

public class Interceptor01 implements Interceptor {
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        System.out.println("Interceptor01 執行了...");
        //放行
        invocation.invoke();
        return null;
    }
    @Override
    public void destroy() {
    }
    @Override
    public void init() {
    }
}
  • 在struts.xml裡面聲明攔截器

<package name="test" extends="struts-default" namespace="/">
    <!-- 聲明攔截器 -->
    <interceptors>
        <interceptor name="interceptor01" class="demo.web.interceptor.Interceptor01">                </interceptor>
    </interceptors>
</package>
  • 在action裡面使用攔截器

 

<action name="demo_*" class="demo.web.action.ActionDemo"  method="{1}">
        <interceptor-ref name="interceptor01"></interceptor-ref>
</action>
2.2方式二:繼承AbstractInterceptor

​ 是Interceptor的一個子類, 只需要重寫intercept()方法.

  • 創建一個類繼承AbstractInterceptor



public class Interceptor02 extends AbstractInterceptor {
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        System.out.println("Interceptor01 執行了...");
        //放行
        invocation.invoke();
        return null;
    }
}
  • 在struts.xml裡面聲明攔截器

<package name="test" extends="struts-default" namespace="/">
    <!-- 聲明攔截器 -->
    <interceptors>
        <interceptor name="interceptor02" class="demo.web.interceptor.Interceptor02">                </interceptor>
    </interceptors>
</package>
  • 在action裡面使用攔截器

<action name="demo_*" class="demo.web.action.ActionDemo"  method="{1}">
    <interceptor-ref name="interceptor02"></interceptor-ref>
</action>
2.3方式三:繼承MethodFilterInterceptor

​ 是AbstractInterceptor的一個子類.可以精確的控制攔截或者不攔截哪一個方法

  • 創建一個類繼承MethodFilterInterceptor

public class Interceptor03 extends MethodFilterInterceptor {
​
    @Override
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        System.out.println("Interceptor03 執行了...");
        //放行
        invocation.invoke();
        return null;
    }
    
}
  • 在struts.xml裡面聲明攔截器

<package name="test" extends="struts-default" namespace="/">
    <!-- 聲明攔截器 -->
    <interceptors>
        <interceptor name="interceptor03" class="demo.web.interceptor.Interceptor03">                </interceptor>
    </interceptors>
</package>
  • 在action裡面使用攔截器

<action name="demo_*" class="demo.web.action.ActionDemo"  method="{1}">
        <interceptor-ref name="interceptor03">
            <!--不攔截fun02()方法 -->
            <param name="excludeMethods">fun02</param>
        </interceptor-ref>
</action>

3.自定義攔截器相關的操作

3.1攔截器不放行

​ 攔截器的處理結果,莫過於兩種:

​ 放行: 如果後面還有攔截器就執行下一個攔截器,如果後面沒有了攔截器,就執行action

​ 攔截: 但是註意,攔截後也需要返回到一個具體的頁面。 所以需要配置result標簽

  • 放行, 調用invoke()方法即可

  • 攔截,跳轉其它頁面

3.2自定義攔截器的問題

​ 一旦自定義了自己的攔截器,那麼struts預設的哪一套 攔截器就不會走。 有點像有參構造和無參構造的關係。

  • struts預設的攔截器都有哪些呢?在struts-default.xml裡面有一個package 名字也叫作 struts-default 在這個包中定義了一個攔截器棧,名字叫做 defaultStack.

  <interceptor-stack name="defaultStack">
            <interceptor-ref name="exception"/>
            <interceptor-ref name="alias"/>
            <interceptor-ref name="servletConfig"/>
            <interceptor-ref name="i18n"/>
            <interceptor-ref name="prepare"/>
            <interceptor-ref name="chain"/>
            <interceptor-ref name="scopedModelDriven"/>
            <interceptor-ref name="modelDriven"/>
            <interceptor-ref name="fileUpload"/>
            <interceptor-ref name="checkbox"/>
            <interceptor-ref name="datetime"/>
            <interceptor-ref name="multiselect"/>
            <interceptor-ref name="staticParams"/>
            <interceptor-ref name="actionMappingParams"/>
            <interceptor-ref name="params"/>
            <interceptor-ref name="conversionError"/>
            <interceptor-ref name="validation">
                <param name="excludeMethods">input,back,cancel,browse</param>
            </interceptor-ref>
            <interceptor-ref name="workflow">
                <param name="excludeMethods">input,back,cancel,browse</param>
            </interceptor-ref>
            <interceptor-ref name="debugging"/>
            <interceptor-ref name="deprecation"/>
    </interceptor-stack>
        ....
    <default-interceptor-ref name="defaultStack"/>
  • 如果我們給自己的action使用了自己定義的攔截器,那麼上面預設的這些攔截器都不會生效了,失去了這些功能,如果還想使用預設的這些攔截器。那麼可以在action裡面再引用它即可。

<action name="actionDemo_*" class="demo.action.ActionDemo" method="{1}">
       <!-- 執行這個action,就必然要走名字叫做myInterceptor02的攔截器 --><!-- 使用自定義的攔截器 -->
       <interceptor-ref name="myInterceptor"></interceptor-ref><!-- 使用預設的攔截器 -->
       <interceptor-ref name="defaultStack"></interceptor-ref><result name="login">/index.jsp</result>
 </action>

四,標簽庫

1.概述

1.1 Struts2標簽庫介紹

​ Struts2標簽庫是一個比較完善,且功能強大的標簽庫,它將所有標簽都統一到一個標簽庫中,從而簡化了標簽的使用,它還提供主題和模板的支持,極大地簡化了視圖頁面代碼的編寫。

1.2Struts2標簽庫分類

1.3使用步驟
  • 如果使用Struts2的標簽庫,必須導入庫到頁面



<%@ taglib uri="/struts-tags" prefix="s" %>
  • 根據語法書寫標簽庫...

2.流程式控制制標簽庫

  • if標簽elseif標簽...類似JSP裡面的c:if

取值:
<s:property value="#request.a"/><br/><s:if test="#request.a > 10">
    a 大於10
</s:if>
<s:else>
    a 小於或者等於10
</s:else>
  • iterator標簽類似JSP裡面的c:foreach

<s:iterator value="list" var="u">
    <s:property value="username"/>
    <s:property value="password"/>
</s:iterator>

3.UI標簽

3.1表單標簽

  • 和HTML標簽對比



<h1>一,HTML的表單標簽</h1>
<form action="${pageContext.request.contextPath }/demo03_fun03" method="post">
    用戶名:<input type="text" name="username"/><br/>
    密碼: <input type="password" name="password"/><br/>
    性別:<input type="radio" name="sex" value="1"/><input type="radio" name="sex" value="0"/><br/>
    愛好:
    <input type="checkbox" name="hobby" value="lq"/>籃球
    <input type="checkbox" name="hobby" value="zq"/>足球
    <input type="checkbox" name="hobby" value="ppq"/>
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在學習Vue2,遇到有些頁面請求數據需要用戶登錄許可權、伺服器響應不符預期的問題,但是總不能每個頁面都做單獨處理吧,於是想到axios提供了攔截器這個好東西,再於是就出現了本文。用戶鑒權與重定向:使用Vue提供的路由導航鉤子;請求數據序列化:使用axios提供的請求攔截器;介面報錯信息處理:使用a... ...
  • 一、場景描述 (一)問題 系統中最初使用Crystal Report(水晶報表)工具生成報表,並將報表發送給客戶端查看,此時定義一CrystalReport工具類即可完成水晶報表的生成工作。 後續報表工具增加SSRS報表(SQL Server Report Service),此時可定義SSRSRep ...
  • 最近在讀《Head First設計模式》一書,此系列會引用源書內容,但文章內容會更加直接,以及加入一些自己的理解。 觀察者模式(有時又被稱為模型-視圖(View)模式、源-收聽者(Listener)模式或從屬者模式)。在此種模式中,一個目標物件管理所有相依於它的觀察者物件,並且在它本身的狀態改變時主 ...
  • 責任鏈的目的是通過特定的設計對請求者和接收者之間進行解耦,請求者調用操作的對象,接收者接收請求並執行相關操作,通過解耦請求者不需要關心接收者的介面,同時也可增強職責的靈活性,通過改變鏈內的成員或調用次序,允許動態新增或刪除責任。 作用 責任鏈模式通過將多個對象連成鏈式模式,並沿著這個鏈傳遞命令或者請 ...
  • 工作流模塊 1.模型管理 :web線上流程設計器、預覽流程xml、導出xml、部署流程 2.流程管理 :導入導出流程資源文件、查看流程圖、根據流程實例反射出流程模型、激活掛起 3.運行中流程:查看流程信息、當前任務節點、當前流程圖、作廢暫停流程、指派待辦人 4.歷史的流程:查看流程信息、流程用時、流 ...
  • 1、一個".java"源文件中是否可以包括多個類(不是內部類)?有什麼限制? 可以有多個類,但只能有一個public的類,並且public的類名必須與文件名相一致。 2、Java有沒有goto? java中的保留字,現在沒有在java中使用。 3、說說&和&&的區別。 &和&&都可以用作邏輯與的運算 ...
  • Nexus2可以通過管理界面來上傳jar包到私庫中,而最新的Nexus3卻找不到了上傳界面,只能通過以下方式來發佈到私庫。 ...
  • 在java中要想實現多線程,有兩種手段,一種是繼續Thread類,另外一種是實現Runable介面。 對於直接繼承Thread的類來說,代碼大致框架是: ? 1 2 3 4 5 6 7 8 9 10 11 class 類名 extends Thread{ 方法1; 方法2; … public voi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...