11.Django基礎九之中間件

来源:https://www.cnblogs.com/changxin7/archive/2019/09/30/11610813.html
-Advertisement-
Play Games

一 前戲 我們在前面的課程中已經學會了給視圖函數加裝飾器來判斷是用戶是否登錄,把沒有登錄的用戶請求跳轉到登錄頁面。我們通過給幾個特定視圖函數加裝飾器實現了這個需求。但是以後添加的視圖函數可能也需要加上裝飾器,這樣是不是稍微有點繁瑣。 學完今天的內容之後呢,我們就可以用更適宜的方式來實現類似給所有請求 ...


一 前戲

  

  我們在前面的課程中已經學會了給視圖函數加裝飾器來判斷是用戶是否登錄,把沒有登錄的用戶請求跳轉到登錄頁面。我們通過給幾個特定視圖函數加裝飾器實現了這個需求。但是以後添加的視圖函數可能也需要加上裝飾器,這樣是不是稍微有點繁瑣。

  學完今天的內容之後呢,我們就可以用更適宜的方式來實現類似給所有請求都做相同操作的功能了

  

二 中間件介紹

  

  中間件顧名思義,是介於request與response處理之間的一道處理過程,相對比較輕量級,並且在全局上改變django的輸入與輸出。因為改變的是全局,所以需要謹慎實用,用不好會影響到性能。

  Django的中間件的定義:

Middleware is a framework of hooks into Django’s request/response processing. <br>It’s a light, low-level “plugin” system for globally altering Django’s input or output.

  如果你想修改請求,例如被傳送到view中的HttpRequest對象。 或者你想修改view返回的HttpResponse對象,這些都可以通過中間件來實現。

  可能你還想在view執行之前做一些操作,這種情況就可以用 middleware來實現。

  說的直白一點中間件是幫助我們在視圖函數執行之前和執行之後都可以做一些額外的操作,它本質上就是一個自定義類,類中定義了幾個方法,Django框架會在請求的特定的時間去執行這些方法。

  我們一直都在使用中間件,只是沒有註意到而已,打開Django項目的Settings.py文件,看到下麵的MIDDLEWARE配置項,django預設自帶的一些中間件:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

  MIDDLEWARE配置項是一個列表,列表中是一個個字元串,這些字元串其實是一個個類,也就是一個個中間件。

  我們之前已經接觸過一個csrf相關的中間件了?我們一開始讓大家把他註釋掉,再提交post請求的時候,就不會被forbidden了,後來學會使用csrf_token之後就不再註釋這個中間件了。

    img

  那接下來就學習中間件中的方法以及這些方法什麼時候被執行。

三 自定義中間件

想多瞭解中間,在開源中國中有很多關於中間件的詳細解釋

  中間件可以定義五個方法,分別是:(主要的是process_request和process_response)

  • process_request(self,request)
  • process_view(self, request, view_func, view_args, view_kwargs)
  • process_template_response(self,request,response)
  • process_exception(self, request, exception)
  • process_response(self, request, response)

  以上方法的返回值可以是None或一個HttpResponse對象,如果是None,則繼續按照django定義的規則向後繼續執行,如果是HttpResponse對象,則直接將該對象返回給用戶。

  當用戶發起請求的時候會依次經過所有的的中間件,這個時候的請求時process_request,最後到達views的函數中,views函數處理後,在依次穿過中間件,這個時候是process_response,最後返回給請求者。

      img

  上述截圖中的中間件都是django中的,我們也可以自己定義一個中間件,我們可以自己寫一個類,但是必須繼承MiddlewareMixin

自定義一個中間件示例

    目錄:

      在項目中創建一個包,隨便起名字,一般都放在一個叫做utils的包裡面,表示一個公用的組件,創建一個py文件,隨便起名字,例如叫做:middlewares.py,內容如下

from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
    #自定義中間件,不是必須要有下麵這兩個方法,有request方法說明請求來了要處理,有response方法說明響應出去時需要處理,不是非要寫這兩個方法,如果你沒寫process_response方法,那麼會一層一層的往上找,哪個中間件有process_response方法就將返回對象給哪個中間件
    def process_request(self, request):
        print("MD1裡面的 process_request")

    def process_response(self, request, response):
        print("MD1裡面的 process_response")
        return response

      

process_request

      process_request有一個參數,就是request,這個request和視圖函數中的request是一樣的。

      它的返回值可以是None也可以是HttpResponse對象。返回值是None的話,按正常流程繼續走,交給下一個中間件處理,如果是HttpResponse對象,Django將不執行視圖函數,而將相應對象返回給瀏覽器。

      我們來看看多個中間件時,Django是如何執行其中的process_request方法的。

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1裡面的 process_request")


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2裡面的 process_request")
        pass

      在settings.py的MIDDLEWARE配置項中註冊上述兩個自定義中間件:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'middlewares.MD1',  # 自定義中間件MD1,這個寫的是你項目路徑下的一個路徑,例如,如果你放在項目下,文件夾名成為utils,那麼這裡應該寫utils.middlewares.MD1
    'middlewares.MD2'  # 自定義中間件MD2
]

      此時,我們訪問一個視圖,會發現終端中列印如下內容:

MD1裡面的 process_request
MD2裡面的 process_requestapp01 中的 index視圖

      把MD1和MD2的位置調換一下,再訪問一個視圖,會發現終端中列印的內容如下:

MD2裡面的 process_request
MD1裡面的 process_requestapp01 中的 index視圖

      看結果我們知道:視圖函數還是最後執行的,MD2比MD1先執行自己的process_request方法。

      在列印一下兩個自定義中間件中process_request方法中的request參數,會發現它們是同一個對象。

      由此總結一下:

        中間件的process_request方法是在執行視圖函數之前執行的。

        當配置多個中間件時,會按照MIDDLEWARE中的註冊順序,也就是列表的索引值,從前到後依次執行的。

        不同中間件之間傳遞的request都是同一個對象

      多個中間件中的process_response方法是按照MIDDLEWARE中的註冊順序倒序執行的,也就是說第一個中間件的process_request方法首先執行,而它的process_response方法最後執行,最後一個中間件的process_request方法最後一個執行,它的process_response方法是最先執行。

process_response

      它有兩個參數,一個是request,一個是response,request就是上述例子中一樣的對象,response是視圖函數返回的HttpResponse對象。該方法的返回值也必須是HttpResponse對象。

      給上述的M1和M2加上process_response方法:

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1裡面的 process_request")
        #不必須寫return值
    def process_response(self, request, response):#request和response兩個參數必須有,名字隨便取
        print("MD1裡面的 process_response")        #print(response.__dict__['_container'][0].decode('utf-8')) #查看響應體裡面的內容的方法,或者直接使用response.content也可以看到響應體裡面的內容,由於response是個變數,直接點擊看源碼是看不到的,你列印type(response)發現是HttpResponse對象,查看這個對象的源碼就知道有什麼方法可以用了。
     return response  #必須有返回值,寫return response  ,這個response就像一個接力棒一樣
        #return HttpResponse('瞎搞') ,如果你寫了這個,那麼你視圖返回過來的內容就被它給替代了

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2裡面的 process_request")
        pass

    def process_response(self, request, response): #request和response兩個參數必須要有,名字隨便取
        print("MD2裡面的 process_response") 
        return response  #必須返回response,不然你上層的中間件就沒有拿到httpresponse對象,就會報錯

      訪問一個視圖,看一下終端的輸出:

MD2裡面的 process_request
MD1裡面的 process_request
app01 中的 index視圖
MD1裡面的 process_response
MD2裡面的 process_response

      看結果可知:

      process_response方法是在視圖函數之後執行的,並且順序是MD1比MD2先執行。(此時settings.py中 MD2比MD1先註冊)

      多個中間件中的process_response方法是按照MIDDLEWARE中的註冊順序倒序執行的,也就是說第一個中間件的process_request方法首先執行,而它的process_response方法最後執行,最後一個中間件的process_request方法最後一個執行,它的process_response方法是最先執行。

      再看一個例子:

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class Md1(MiddlewareMixin):

    def process_request(self,request):
        print("Md1請求")     #process_request方法裡面不寫返回值,預設也是返回None,如果你自己寫了return None,也是一樣的效果,不會中斷你的請求,但是如果你return 的一個httpresponse對象,那麼就會在這個方法中斷你的請求,直接返回給用戶,這就成了非正常的流程了        #並且,如果你在這裡return了httpresponse對象,那麼會從你這個中間件類中的process_response方法開始執行返回操作,所以這個類裡面只要有process_response方法,肯定會執行
 
    def process_response(self,request,response):
        print("Md1返回")
        return response

class Md2(MiddlewareMixin):

    def process_request(self,request):
        print("Md2請求")
        #return HttpResponse("Md2中斷")
    def process_response(self,request,response):
        print("Md2返回")
        return response

    結果:

Md1請求
Md2請求
view函數...
Md2返回
Md1返回

    註意:如果當請求到達請求2的時候直接不符合條件返回,即return HttpResponse("Md2中斷"),程式將把請求直接發給中間件2返回,然後依次返回到請求者,結果如下:

    返回Md2中斷的頁面,後臺列印如下:

Md1請求
Md2請求
Md2返回
Md1返回

    流程圖如下:

    img

    之前我們做的cookie認證,都是通過在函數上面加裝飾器搞的,比較麻煩,看看中間件怎麼搞,如果寫的是session認證的,你必須放在django自帶的session中間件的下麵,所以自定義中間之後,你需要註意你的中間件的擺放順序。

class M1(MiddlewareMixin):

    def process_request(self,request):

        #設置路徑白名單,只要訪問的是login登陸路徑,就不做這個cookie認證
        if request.path not in [reverse('login'),]:
            print('我是M1中間件') #客戶端IP地址
            # return HttpResponse('sorry,沒有通過我的M1中間件')
            is_login = request.COOKIES.get('is_login', False)

            if is_login:
                pass
            else:
                # return render(request,'login.html')
                return redirect(reverse('login'))
        else:
            return None #別忘了return None,或者直接寫個pass

    def process_response(self,request,response):

        print('M1響應部分')
        # print(response.__dict__['_container'][0].decode('utf-8'))
        return response
        # return HttpResponse('瞎搞')

      練習:嘗試一下通過中間件來控制用戶的訪問次數,讓用戶在一分鐘之內不能訪問我的網站超過20次。

    後面要學的方法不常用,但是大家最好也要知道。

process_view

      process_view(self, request, view_func, view_args, view_kwargs)

      該方法有四個參數

      request是HttpRequest對象。

      view_func是Django即將使用的視圖函數。 (它是實際的函數對象,而不是函數的名稱作為字元串。)

      view_args是將傳遞給視圖的位置參數的列表.

      view_kwargs是將傳遞給視圖的關鍵字參數的字典。 view_args和view_kwargs都不包含第一個視圖參數(request)。

      Django會在調用視圖函數之前調用process_view方法。

      它應該返回None或一個HttpResponse對象。 如果返回None,Django將繼續處理這個請求,執行任何其他中間件的process_view方法,然後在執行相應的視圖。 如果它返回一個HttpResponse對象,Django不會調用對應的視圖函數。 它將執行中間件的process_response方法並將應用到該HttpResponse並返回結果。

        img

      給MD1和MD2添加process_view方法:

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1裡面的 process_request")

    def process_response(self, request, response):
        print("MD1裡面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__) #就是url映射到的那個視圖函數,也就是說每個中間件的這個process_view已經提前拿到了要執行的那個視圖函數        #ret = view_func(request) #提前執行視圖函數,不用到了上圖的試圖函數的位置再執行,如果你視圖函數有參數的話,可以這麼寫 view_func(request,view_args,view_kwargs) 
        #return ret  #直接就在MD1中間件這裡這個類的process_response給返回了,就不會去找到視圖函數裡面的這個函數去執行了。

class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2裡面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2裡面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

      訪問index視圖函數,看一下輸出結果:

MD2裡面的 process_request
MD1裡面的 process_request
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x000001DE68317488> index
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x000001DE68317488> index
app01 中的 index視圖
MD1裡面的 process_response
MD2裡面的 process_response

      process_view方法是在process_request之後,reprocess_response之前,視圖函數之前執行的,執行順序按照MIDDLEWARE中的註冊順序從前到後順序執行的

        img

process_exception

      process_exception(self, request, exception)

      該方法兩個參數:

      一個HttpRequest對象

      一個exception是視圖函數異常產生的Exception對象。

      這個方法只有在視圖函數中出現異常了才執行,它返回的值可以是一個None也可以是一個HttpResponse對象。如果是HttpResponse對象,Django將調用模板和中間件中的process_response方法,並返回給瀏覽器,否則將預設處理異常。如果返回一個None,則交給下一個中間件的process_exception方法來處理異常。它的執行順序也是按照中間件註冊順序的倒序執行。

        img

      給MD1和MD2添加上這個方法:

from django.utils.deprecation import MiddlewareMixin


class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1裡面的 process_request")

    def process_response(self, request, response):
        print("MD1裡面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 中的process_exception")


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2裡面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2裡面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 中的process_exception")

      如果視圖函數中無異常,process_exception方法不執行。

      想辦法,在視圖函數中拋出一個異常:

def index(request):
    print("app01 中的 index視圖")
    raise ValueError("呵呵")
    return HttpResponse("O98K")

      在MD1的process_exception中返回一個響應對象:

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1裡面的 process_request")

    def process_response(self, request, response):
        print("MD1裡面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 中的process_exception")
        return HttpResponse(str(exception))  # 返回一個響應對象

      看輸出結果:

MD2裡面的 process_request
MD1裡面的 process_request
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x0000022C09727488> index
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x0000022C09727488> index
app01 中的 index視圖
呵呵
MD1 中的process_exception
MD1裡面的 process_response
MD2裡面的 process_response

        img

      註意,這裡並沒有執行MD2的process_exception方法,因為MD1中的process_exception方法直接返回了一個響應對象。

process_template_response(用的比較少)

      process_template_response(self, request, response)

      它的參數,一個HttpRequest對象,response是TemplateResponse對象(由視圖函數或者中間件產生)。

      process_template_response是在視圖函數執行完成後立即執行,但是它有一個前提條件,那就是視圖函數返回的對象有一個render()方法(或者表明該對象是一個TemplateResponse對象或等價方法)。

class MD1(MiddlewareMixin):

    def process_request(self, request):
        print("MD1裡面的 process_request")

    def process_response(self, request, response):
        print("MD1裡面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD1 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD1 中的process_exception")
        return HttpResponse(str(exception))

    def process_template_response(self, request, response):
        print("MD1 中的process_template_response")
        return response


class MD2(MiddlewareMixin):
    def process_request(self, request):
        print("MD2裡面的 process_request")
        pass

    def process_response(self, request, response):
        print("MD2裡面的 process_response")
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        print("-" * 80)
        print("MD2 中的process_view")
        print(view_func, view_func.__name__)

    def process_exception(self, request, exception):
        print(exception)
        print("MD2 中的process_exception")

    def process_template_response(self, request, response):
        print("MD2 中的process_template_response")
        return response

      views.py中:

def index(request):
    print("app01 中的 index視圖")
  #raise ValueError('出錯啦') 
    def render():
        print("in index/render")          #raise ValueError('出錯啦') #至於render函數中報錯了,那麼會先執行process_template_response方法,然後執行process_exception方法,如果是在render方法外面報錯了,那麼就不會執行這個process_template_response方法了。
        return HttpResponse("O98K") #返回的將是這個新的對象
    rep = HttpResponse("OK")
    rep.render = render
    return rep

      訪問index視圖,終端輸出的結果:

MD2裡面的 process_request
MD1裡面的 process_request
--------------------------------------------------------------------------------
MD2 中的process_view
<function index at 0x000001C111B97488> index
--------------------------------------------------------------------------------
MD1 中的process_view
<function index at 0x000001C111B97488> index
app01 中的 index視圖
MD2 中的process_template_response
MD1 中的process_template_response
in index/render
MD1裡面的 process_response
MD2裡面的 process_response

      從結果看出:

      視圖函數執行完之後,立即執行了中間件的process_template_response方法,順序是倒序,先執行MD2的,在執行MD1的,接著執行了視圖函數返回的HttpResponse對象的render方法,返回了一個新的HttpResponse對象,接著執行中間件的process_response方法。

四 中間件執行流程

  上一部分,我們瞭解了中間件中的5個方法,它們的參數、返回值以及什麼時候執行,現在總結一下中間件的執行流程。

  請求到達中間件之後,先按照正序執行每個註冊中間件的process_reques方法,process_request方法返回的值是None,就依次執行,如果返回的值是HttpResponse對象,不再執行後面的process_request方法,而是執行當前對應中間件的process_response方法,將HttpResponse對象返回給瀏覽器。也就是說:如果MIDDLEWARE中註冊了6個中間件,執行過程中,第3個中間件返回了一個HttpResponse對象,那麼第4,5,6中間件的process_request和process_response方法都不執行,順序執行3,2,1中間件的process_response方法。

    img

  process_request方法都執行完後,匹配路由,找到要執行的視圖函數,先不執行視圖函數,先執行中間件中的process_view方法,process_view方法返回None,繼續按順序執行,所有process_view方法執行完後執行視圖函數。加入中間件3 的process_view方法返回了HttpResponse對象,則4,5,6的process_view以及視圖函數都不執行,直接從最後一個中間件,也就是中間件6的process_response方法開始倒序執行。

    img

  process_template_response和process_exception兩個方法的觸發是有條件的,執行順序也是倒序。總結所有的執行流程如下:

    img

    img

五 中間件版登陸認證

中間件版的登錄驗證需要依靠session,所以資料庫中要有django_session表。

    urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^index/$', views.index),
    url(r'^login/$', views.login, name='login'),
]

    views.py

from django.shortcuts import render, HttpResponse, redirect


def index(request):
    return HttpResponse('this is index')


def home(request):
    return HttpResponse('this is home')


def login(request):
    if request.method == "POST":
        user = request.POST.get("user")
        pwd = request.POST.get("pwd")

        if user == "Q1mi" and pwd == "123456":
            # 設置session
            request.session["user"] = user
            # 獲取跳到登陸頁面之前的URL
            next_url = request.GET.get("next")
            # 如果有,就跳轉回登陸之前的URL
            if next_url:
                return redirect(next_url)
            # 否則預設跳轉到index頁面
            else:
                return redirect("/index/")
    return render(request, "login.html")

    login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="x-ua-compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登錄頁面</title>
</head>
<body>
<form action="{% url 'login' %}">
    <p>
        <label for="user">用戶名:</label>
        <input type="text" name="user" id="user">
    </p>
    <p>
        <label for="pwd">密 碼:</label>
        <input type="text" name="pwd" id="pwd">
    </p>
    <input type="submit" value="登錄">
</form>
</body>
</html>

    middlewares.py

class AuthMD(MiddlewareMixin):
    white_list = ['/login/', ]  # 白名單
    balck_list = ['/black/', ]  # 黑名單

    def process_request(self, request):
        from django.shortcuts import redirect, HttpResponse

        next_url = request.path_info
        print(request.path_info, request.get_full_path())

        if next_url in self.white_list or request.session.get("user"):
            return
        elif next_url in self.balck_list:
            return HttpResponse('This is an illegal URL')
        else:
            return redirect("/login/?next={}".format(next_url))

    在settings.py中註冊

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'middlewares.AuthMD',
]

    AuthMD中間件註冊後,所有的請求都要走AuthMD的process_request方法。

    訪問的URL在白名單內或者session中有user用戶名,則不做阻攔走正常流程;

    如果URL在黑名單中,則返回This is an illegal URL的字元串;

    正常的URL但是需要登錄後訪問,讓瀏覽器跳轉到登錄頁面。

    註:AuthMD中間件中需要session,所以AuthMD註冊的位置要在session中間的下方。

    附:Django請求流程圖

      img

六 中間件案例

應用案例

1、做IP訪問頻率限制

      某些IP訪問伺服器的頻率過高,進行攔截,比如限制每分鐘不能超過20次。

2、URL訪問過濾

      如果用戶訪問的是login視圖(放過)

      如果訪問其他視圖,需要檢測是不是有session認證,已經有了放行,沒有返回login,這樣就省得在多個視圖函數上寫裝飾器了!

源碼試讀

    作為延伸擴展內容,有餘力的同學可以嘗試著讀一下以下兩個自帶的中間件:

'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • <!DOCTYPE html><html><style> *{padding: 0;margin: 0} #open{ width: 300px; height: 300px; background-color: brown; position: relative; border-radius:50 ...
  • 字體的分類: serif (襯線字體){在筆畫上面有些特殊的修飾效果} sans-serif (非襯線字體){橫平豎直.橫就是橫,點就是點} monospace ( 等寬字體) cursive ( 草書字體) fantasy (虛幻字體) 以上這些分類都是一些大的分類, 並沒有涉及具體的類型,如果將 ...
  • JS中數據類型轉換彙總 JS中的數據類型分為 【基本數據類型】 數字 number 字元串 string 布爾 boolean 空 null 未定義 undefined 【引用數據類型】 對象 object 普通對象 數組對象 (Array) 正則對象 (RegExp) 日期對象 (Date) 數學 ...
  • 1. 繼承 ES 中只支持實現繼承,而且其實現繼承主要依靠原型鏈來實現的。 2. 原型鏈 ES中 描述了 原型鏈的概念,並將原型鏈作為實現繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法。 回顧一下構造函數、原型和實例的關係 每個構造函數都有一個原型對象,原型對象都包 ...
  • JavaScript 不提供任何內建的列印或顯示函數。 JavaScript 顯示方案 JavaScript 能夠以不同方式“顯示”數據: 使用 window.alert() 寫入警告框 使用 document.write() 寫入 HTML 輸出 使用 innerHTML 寫入 HTML 元素 使 ...
  • 模板模式(Template): 提到模板,可能大多數人想到的是"簡歷模板"、"論文模板"等,比如我們要寫簡歷時,會從網上下載一份漂亮的簡歷模板,其格式是固定的,我們根據自己的情況填充不同的內容。模板模式定義一個操作中的演算法的骨架,而將一些步驟延遲到子類中。模板方法使得子類可以不改變一個演算法的結構即可 ...
  • 事務一致性 首先,我們來回顧一下ACID原則: Atomicity:原子性,改變數據狀態要麼是一起完成,要麼一起失敗 Consistency:一致性,數據的狀態是完整一致的 Isolation:隔離線,即使有併發事務,互相之間也不影響 Durability:持久性, 一旦事務提交,不可撤銷 在單體應 ...
  • 多年後, 再次翻閱設計模式書籍, 將每種模式的要點總結於此, 需要本身有一定設計模式基礎, 再結合要點, 幫助更好理解與運用. ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...