YONGFEIUALL
izheyi.com
2022-02-21T02:22:45.008Z
http://izheyi.com/
唐胡璐
Hexo
Python给Jenkins Job批量修改权限
http://izheyi.com/2022/02/21/Python给Jenkins-Job批量修改权限/
2022-02-21T01:16:09.000Z
2022-02-21T02:22:45.008Z
<p>开发老是不自觉的更新UAT的Job,对测试造成一定的影响。查看了一下,Jenkins上的用户是通过第三方系统引入的,没有更合理的分组,用Role Based Strategy不能够实现对特定Job的权限控制。</p>
<p>采用项目矩阵授权策略来实现:目前的权限不受影响,只有UAT View下的Job只有测试能访问执行。</p>
<ol>
<li><p>全局配置,设置Admin和Authenticated User权限</p>
</li>
<li><p>项目设置,选择不继承权限策略,并对特定用户加进来设置相应权限</p>
</li>
</ol>
<p>这种方式,View下有很多个Job,每来一个人都要一个一个加,可以利用代码方式批量的来添加用户权限:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">s = jenkins.Jenkins(url=URL, username=USERNAME, password=PASSWORD)</span><br><span class="line"></span><br><span class="line">jobs = s.get_jobs(view_name=VIEW_NAME)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> job <span class="keyword">in</span> jobs:</span><br><span class="line"> name = job[<span class="string">'name'</span>]</span><br><span class="line"> config = s.get_job_config(name)</span><br><span class="line"></span><br><span class="line"> content = f<span class="string">'''</permission></span><br><span class="line"> <permission>hudson.model.Item.Build:{USER}</permission></span><br><span class="line"> <permission>hudson.model.Item.Cancel:{USER}</permission></span><br><span class="line"> <permission>hudson.model.Item.Configure:{USER}</permission></span><br><span class="line"> <permission>hudson.model.Item.Delete:{USER}</permission></span><br><span class="line"> <permission>hudson.model.Item.Move:{USER}</permission></span><br><span class="line"> <permission>hudson.model.Item.Read:{USER}</permission></span><br><span class="line"> <permission>hudson.model.Item.Workspace:{USER}</permission></span><br><span class="line"> '''</span></span><br><span class="line"></span><br><span class="line"> new = config.replace(<span class="string">'</permission>'</span>, content, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> s.reconfig_job(name, new)</span><br></pre></td></tr></table></figure>
<p>开发老是不自觉的更新UAT的Job,对测试造成一定的影响。查看了一下,Jenkins上的用户是通过第三方系统引入的,没有更合理的分组,用Role Based Strategy不能够实现对特定Job的权限控制。</p>
<p>采用项目矩阵授权策略来实现:目前的权限不受影响,只
Python给Gogs项目批量打Tag
http://izheyi.com/2021/10/21/Python给Gogs项目批量打Tag/
2021-10-21T03:03:54.000Z
2021-11-01T05:53:28.265Z
<p>Gogs的Rest API不能用,就用下面的方式来实现:</p>
<p>通过csv文件配置要打Tag的项目</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Project Name,Release,Tag Name,Tag Branch,Title,Content t1,Y,v1.1.0,master,,test1 t2,Y,v1.1.0,master,v1.1.0,test2 t3,Y,v1.1.0,master,v1.1.0,test3</span><br></pre></td></tr></table></figure>
<p>实现</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line">session = HTMLSession()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get crsf token</span></span><br><span class="line">r = session.get(url=f<span class="string">'{URL}/user/login'</span>)</span><br><span class="line">csrf = r.html.xpath(<span class="string">'//input[@name="_csrf"]/@value'</span>, first=<span class="keyword">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Login</span></span><br><span class="line">data = {</span><br><span class="line"> <span class="string">'_csrf'</span>: csrf,</span><br><span class="line"> <span class="string">'user_name'</span>: USERNAME,</span><br><span class="line"> <span class="string">'password'</span>: PASSWORD,</span><br><span class="line"> <span class="string">'login_source'</span>: <span class="number">2</span></span><br><span class="line">}</span><br><span class="line">session.post(url=f<span class="string">'{URL}/user/login'</span>, data=data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get login crsf token</span></span><br><span class="line">r = session.get(url=f<span class="string">'{URL}'</span>)</span><br><span class="line">login_csrf = r.html.xpath(<span class="string">'//meta[@name="_csrf"]/@content'</span>, first=<span class="keyword">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Set tag for project from csv file</span></span><br><span class="line">projects = get_test_csv_data(<span class="string">'build.csv'</span>)</span><br><span class="line"><span class="keyword">for</span> project <span class="keyword">in</span> projects:</span><br><span class="line"> <span class="keyword">if</span> project[<span class="number">1</span>] == <span class="string">'Y'</span>:</span><br><span class="line"></span><br><span class="line"> build_data = {</span><br><span class="line"> <span class="string">'tag_name'</span>: project[<span class="number">2</span>],</span><br><span class="line"> <span class="string">'tag_target'</span>: project[<span class="number">3</span>],</span><br><span class="line"> <span class="string">'title'</span>: project[<span class="number">4</span>],</span><br><span class="line"> <span class="string">'content'</span>: project[<span class="number">5</span>],</span><br><span class="line"> <span class="string">'_csrf'</span>: login_csrf</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> print(f<span class="string">'===== set tag {project[2]} for {project[0]} ====='</span>)</span><br><span class="line"> print(build_data)</span><br><span class="line"> r = session.post(url=f<span class="string">'{URL}/{OWNER}/{project[0]}/releases/new'</span>, data=build_data)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># Verify set correctly</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> res = session.get(f<span class="string">'{URL}/{OWNER}/{project[0]}/releases'</span>)</span><br><span class="line"> version = res.html.xpath(<span class="string">'//ul[@id="release-list"]//a'</span>, first=<span class="keyword">True</span>).text</span><br><span class="line"> <span class="keyword">if</span> version == project[<span class="number">2</span>]:</span><br><span class="line"> print(f<span class="string">'===== set tag {project[2]} for {project[0]} successfully ====='</span>)</span><br><span class="line"> <span class="keyword">except</span>:</span><br><span class="line"> print(f<span class="string">'===== set tag {project[2]} for {project[0]} failed ====='</span>)</span><br><span class="line"> print()</span><br></pre></td></tr></table></figure>
<p>结果</p>
<figure class="highlight asciidoc"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="header">===== set tag v1.1.0 for t1 =====</span></span><br><span class="line">{<span class="emphasis">'tag_name'</span>: <span class="emphasis">'v1.1.0'</span>, <span class="emphasis">'tag_target'</span>: <span class="emphasis">'master'</span>, <span class="emphasis">'title'</span>: '<span class="emphasis">', '</span>content': <span class="emphasis">'test1'</span>, <span class="emphasis">'_csrf'</span>: <span class="emphasis">'iE2XUTv0z4jDvjr6HuPYTiFvOrc6MTYzNTUwMDA3OTgyNjgxNTU5Mw=='</span>}</span><br><span class="line"><span class="header">===== set tag v1.1.0 for t1 failed =====</span></span><br><span class="line"></span><br><span class="line"><span class="header">===== set tag v1.1.0 for t2 =====</span></span><br><span class="line">{<span class="emphasis">'tag_name'</span>: <span class="emphasis">'v1.1.0'</span>, <span class="emphasis">'tag_target'</span>: <span class="emphasis">'master'</span>, <span class="emphasis">'title'</span>: <span class="emphasis">'v1.1.0'</span>, <span class="emphasis">'content'</span>: <span class="emphasis">'test2'</span>, <span class="emphasis">'_csrf'</span>: <span class="emphasis">'iE2XUTv0z4jDvjr6HuPYTiFvOrc6MTYzNTUwMDA3OTgyNjgxNTU5Mw=='</span>}</span><br><span class="line"><span class="header">===== set tag v1.1.0 for t2 successfully =====</span></span><br><span class="line"></span><br><span class="line"><span class="header">===== set tag v1.1.0 for t3 =====</span></span><br><span class="line">{<span class="emphasis">'tag_name'</span>: <span class="emphasis">'v1.1.0'</span>, <span class="emphasis">'tag_target'</span>: <span class="emphasis">'master'</span>, <span class="emphasis">'title'</span>: <span class="emphasis">'v1.1.0'</span>, <span class="emphasis">'content'</span>: <span class="emphasis">'test3'</span>, <span class="emphasis">'_csrf'</span>: <span class="emphasis">'iE2XUTv0z4jDvjr6HuPYTiFvOrc6MTYzNTUwMDA3OTgyNjgxNTU5Mw=='</span>}</span><br><span class="line"><span class="header">===== set tag v1.1.0 for t3 successfully =====</span></span><br></pre></td></tr></table></figure>
<p>Gogs的Rest API不能用,就用下面的方式来实现:</p>
<p>通过csv文件配置要打Tag的项目</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line"
Python获得Gogs里项目最新发布版本Version
http://izheyi.com/2021/10/10/Python获得Gogs里项目最新发布版本Version/
2021-10-10T05:59:01.000Z
2021-10-11T06:09:21.198Z
<p>现在每个大版本号对应的各个项目的小版本号是不一致的,要快速的获得每次发布所对应的版本号。写个脚本来实现。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">session = HTMLSession()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get crsf token</span></span><br><span class="line">r = session.get(url=URL)</span><br><span class="line">csrf = r.html.xpath(<span class="string">'//input[@name="_csrf"]'</span>, first=<span class="keyword">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Login</span></span><br><span class="line">data = {</span><br><span class="line"> <span class="string">'_csrf'</span>: csrf.attrs.get(<span class="string">'value'</span>),</span><br><span class="line"> <span class="string">'user_name'</span>: USERNAME,</span><br><span class="line"> <span class="string">'password'</span>: PASSWORD,</span><br><span class="line"> <span class="string">'login_source'</span>: <span class="number">2</span></span><br><span class="line">}</span><br><span class="line">session.post(url=URL, data=data)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get project latest release version</span></span><br><span class="line">release_version = {}</span><br><span class="line"><span class="keyword">for</span> p <span class="keyword">in</span> PROJECT_LIST:</span><br><span class="line"> res = session.get(f<span class="string">'https://{URL}/{p}/releases'</span>)</span><br><span class="line"></span><br><span class="line"> version = res.html.xpath(<span class="string">'//ul[@id="release-list"]//a'</span>, first=<span class="keyword">True</span>).text</span><br><span class="line"></span><br><span class="line"> release_version[p] = version</span><br><span class="line"></span><br><span class="line">print(json.dumps(release_version, indent=<span class="number">4</span>))</span><br></pre></td></tr></table></figure>
<p>并通过API的方式,直接把相关信息填写到Confluence上。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">con = Confluence(url=URL, username=USERNAME, password=PASSWORD)</span><br><span class="line"></span><br><span class="line">body = <span class="string">'<div class="table-wrap"><table class="relative-table wrapped confluenceTable">版本信息</table></div>'</span></span><br><span class="line">con.create_page(space=<span class="string">'BDP'</span>, title=<span class="string">'testtesttest'</span>, parent_id=<span class="number">85295571</span>,</span><br><span class="line"> body=body)</span><br></pre></td></tr></table></figure>
<p>最后,把脚本集成到Jenkins上,每次只要运行一下Job就可以快速完整版本号的整理工作。</p>
<p>现在每个大版本号对应的各个项目的小版本号是不一致的,要快速的获得每次发布所对应的版本号。写个脚本来实现。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class=
FastAPI: JWT登录认证(20)
http://izheyi.com/2021/10/06/FastAPI-登录认证(20)/
2021-10-06T13:30:11.000Z
2021-10-06T08:25:29.385Z
<p>至此,FastAPI的基本使用和整个流程都过完了。接下来看一下登录认证,是一个系统里最基本的功能,有些页面只能登录之后才能够访问。</p>
<p>有Session和Token两种方式实现,目前最流行的下面token方式:</p>
<ol>
<li>用户登录后,返回一个token给前端,后台不存储该token</li>
<li>前端再带着这个token去访问需要登录的页面</li>
<li><p>后端根据token去判断用户,确定当前登录用户</p>
<p>Bearer的简单OAuth2,前面已聊过<a href="2021/10/03/FastAPI-Security(14)/">FastAPI: Security(14)</a>,当时还没有生成token,OAuth2不能生成token,我们一般用<code>JWT</code>来实现</p>
</li>
</ol>
<h4 id="JWT"><a href="#JWT" class="headerlink" title="JWT"></a>JWT</h4><p>JWT全称是<code>Json Web Token</code>,JWT认证目前是前后端分离中非常流行的一种认证方式,生成的token分为三个部分: HEADER.PAYLOAD.SIGNATURE, 这三个部分都是可逆算法base64加密后的字符串, 最后用<code>.</code>拼接</p>
<ol>
<li><p>HEADER</p>
<p>通常是加密算法</p>
</li>
<li><p>PAYLOAD</p>
<p>存储的自定义信息和token的过期时间</p>
</li>
<li><p>SIGNATURE</p>
<p>这个签证信息由三部分组成:</p>
<ul>
<li>header (base64后的)</li>
<li>payload (base64后的)</li>
<li>secret (盐值(salt): 指的是加密时加入的自定义的字符串, 最好是随机或者杂乱的字符串, 这样更能够确定加密后字符串的唯一性, 可以使用settings中的SECRET_KEY)</li>
</ul>
<p>这个部分需要base64加密后的header和base64加密后的payload使用<code>.</code>连接组成的字符串,然后通过header中声明的加密方式进行加盐<code>secret</code>组合加密,然后就构成了jwt的第三部分。</p>
</li>
</ol>
<p> python中有好几多库可以实现JWT认证,这里也用官网使用的<code>python-jose</code>,Python-jose需要一个额外的加密后端。这里我们使用的是推荐的后端:<code>pyca/cryptography</code></p>
<figure class="highlight accesslog"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install python-jose<span class="string">[cryptography]</span></span><br></pre></td></tr></table></figure>
<h6 id="生成token"><a href="#生成token" class="headerlink" title="生成token"></a>生成token</h6> <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> jose <span class="keyword">import</span> jwt</span><br><span class="line"><span class="keyword">from</span> datetime <span class="keyword">import</span> datetime, timedelta</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加密密钥</span></span><br><span class="line">SECRET_KEY = <span class="string">"testjwt"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置过期时间 示例5分钟</span></span><br><span class="line">expire = datetime.utcnow() + timedelta(minutes=<span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># exp 是固定写法必须得传 username和uid是自己存的值</span></span><br><span class="line">to_encode = {<span class="string">"exp"</span>: expire, <span class="string">"username"</span>: <span class="string">"admin"</span>, <span class="string">"uid"</span>: <span class="string">"12345"</span>}</span><br><span class="line"></span><br><span class="line"><span class="comment"># 生成token</span></span><br><span class="line">token = jwt.encode(to_encode, SECRET_KEY, algorithm=<span class="string">"HS256"</span>)</span><br></pre></td></tr></table></figure>
<h6 id="解密token"><a href="#解密token" class="headerlink" title="解密token"></a>解密token</h6> <figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> jose.exceptions <span class="keyword">import</span> ExpiredSignatureError, JWTError</span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line"> payload = jwt.decode(token, SECRET_KEY, algorithms=<span class="string">"HS256"</span> )</span><br><span class="line"> print(payload)</span><br><span class="line"><span class="keyword">except</span> ExpiredSignatureError <span class="keyword">as</span> e:</span><br><span class="line"> print(<span class="string">"token过期"</span>)</span><br><span class="line"><span class="keyword">except</span> JWTError <span class="keyword">as</span> e:</span><br><span class="line"> print(<span class="string">"token验证失败"</span>)</span><br></pre></td></tr></table></figure>
<h4 id="FastAPI实现JWT认证"><a href="#FastAPI实现JWT认证" class="headerlink" title="FastAPI实现JWT认证"></a>FastAPI实现JWT认证</h4><ol>
<li><p>加密解密的公共方法</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">create_jwt_token</span><span class="params">(data: dict, expires_delta: Optional[timedelta] = None)</span>:</span></span><br><span class="line"> to_encode = data.copy()</span><br><span class="line"> <span class="keyword">if</span> expires_delta:</span><br><span class="line"> expire = datetime.utcnow() + expires_delta</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> expire = datetime.utcnow() + timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES)</span><br><span class="line"> to_encode.update({<span class="string">"exp"</span>: expire})</span><br><span class="line"> token = jwt.encode(claims=to_encode, key=config.SECRET_KEY, algorithm=config.ALGORITHM)</span><br><span class="line"> <span class="keyword">return</span> token</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">decode_jwt_token</span><span class="params">(token: str)</span>:</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> payload = jwt.decode(token=token, key=config.SECRET_KEY, algorithms=config.ALGORITHM)</span><br><span class="line"> <span class="keyword">return</span> payload</span><br><span class="line"> <span class="keyword">except</span> (jwt.JWTError, jwt.ExpiredSignatureError, AttributeError) <span class="keyword">as</span> e:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,</span><br><span class="line"> detail=f<span class="string">'access token failed: {e}'</span>,</span><br><span class="line"> <span class="comment"># 根据OAuth2规范, 认证失败需要在响应头中添加如下键值对</span></span><br><span class="line"> headers={<span class="string">'WWW-Authenticate'</span>: <span class="string">"Bearer"</span>}</span><br><span class="line"> )</span><br></pre></td></tr></table></figure>
</li>
</ol>
<ol>
<li><p>登录生成Token</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">oauth2_scheme = OAuth2PasswordBearer(tokenUrl=<span class="string">"/api/login"</span>)</span><br><span class="line"></span><br><span class="line"><span class="decorator">@router.post('/api/login', tags=['users'])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">user_login</span><span class="params">(username: str = Form<span class="params">(...)</span>, password: str = Form<span class="params">(...)</span>, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> db_user = crud.get_user_by_mail(db, username)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> db_user:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">404</span>, detail=<span class="string">"User not found"</span>)</span><br><span class="line"> <span class="keyword">if</span> password != db_user.hash_password:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'passwod incorrect'</span>)</span><br><span class="line"></span><br><span class="line"> data = {<span class="string">'email'</span>: db_user.email, <span class="string">'user_id'</span>: db_user.id}</span><br><span class="line"> token = jwttoken.create_jwt_token(data)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">"token"</span>: token, <span class="string">"token_type"</span>: <span class="string">"bearer"</span>}</span><br><span class="line"></span><br><span class="line"><span class="decorator">@router.get('/current/user', tags=['users'])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_current_user</span><span class="params">(token: Optional[str] = Header<span class="params">(...)</span>, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> payload = jwttoken.decode_jwt_token(token)</span><br><span class="line"> email: str = payload.get(<span class="string">"email"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> email <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=status.HTTP_401_UNAUTHORIZED,</span><br><span class="line"> detail=f<span class="string">'access token failed'</span>,</span><br><span class="line"> <span class="comment"># 根据OAuth2规范, 认证失败需要在响应头中添加如下键值对</span></span><br><span class="line"> headers={<span class="string">'WWW-Authenticate'</span>: <span class="string">"Bearer"</span>}</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> user = crud.get_user_by_mail(db, email=email)</span><br><span class="line"> <span class="keyword">if</span> user <span class="keyword">is</span> <span class="keyword">None</span>:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">404</span>, detail=<span class="string">"User not found"</span>)</span><br><span class="line"> <span class="keyword">return</span> user</span><br></pre></td></tr></table></figure>
</li>
</ol>
<ol>
<li><p>验证Token</p>
<p><img src="/images/fastapi/jwt.jpg" alt=""></p>
</li>
</ol>
<p>至此,FastAPI的基本使用和整个流程都过完了。接下来看一下登录认证,是一个系统里最基本的功能,有些页面只能登录之后才能够访问。</p>
<p>有Session和Token两种方式实现,目前最流行的下面token方式:</p>
<ol>
<li>用户登录后,返回一个tok
FastAPI: Docker部署(19)
http://izheyi.com/2021/10/06/FastAPI-Docker部署(19)/
2021-10-06T09:10:27.000Z
2021-10-04T09:51:53.666Z
<p>用Docker去部署应用已是目前最流行和通用的方式。现在把之前的项目部署到Docker上去。</p>
<h4 id="准备Dockerfile"><a href="#准备Dockerfile" class="headerlink" title="准备Dockerfile"></a>准备Dockerfile</h4><p>项目根目录下创建<code>Dockerfile</code></p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">FROM</span> python:<span class="number">3.7</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">WORKDIR</span> <span class="bash">/fastapi</span><br><span class="line"></span></span><br><span class="line"><span class="built_in">COPY</span> <span class="bash">./requirements.txt /fastapi/requirements.txt</span><br><span class="line"></span></span><br><span class="line"><span class="built_in">RUN</span> <span class="bash">pip install --no-cache-dir --upgrade -r /fastapi/requirements.txt</span><br><span class="line"></span></span><br><span class="line"><span class="built_in">COPY</span> <span class="bash">. /fastapi/</span><br><span class="line"></span></span><br><span class="line"><span class="built_in">CMD</span> <span class="bash">[<span class="string">"uvicorn"</span>, <span class="string">"main:app"</span>, <span class="string">"--host"</span>, <span class="string">"0.0.0.0"</span>, <span class="string">"--port"</span>, <span class="string">"80"</span>]</span></span><br></pre></td></tr></table></figure>
<h4 id="Build-Docker-Image"><a href="#Build-Docker-Image" class="headerlink" title="Build Docker Image"></a>Build Docker Image</h4><p>去到项目根目录下,执行下面命令</p>
<p><code>docker build -t myfastapi:user .</code></p>
<p>验证</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">PS E:\fastapi> docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE myfastapi user 0790ae3493a3 14 seconds ago 972MB</span><br></pre></td></tr></table></figure>
<h4 id="Start-Docker-Container"><a href="#Start-Docker-Container" class="headerlink" title="Start Docker Container"></a>Start Docker Container</h4><p>执行下面命令,部署应用到Container</p>
<p><code>docker run -d --name fastapiservice -p 80:80 myfastapi:user</code></p>
<p>验证</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">PS E:\fastapi> docker container ls</span><br><span class="line">CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES</span><br><span class="line"><span class="number">7</span>b550b5a7ebb myfastapi:user <span class="string">"uvicorn main:app --…"</span> About a minute ago Up About a minute <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span>:<span class="number">80</span>-><span class="number">80</span>/tcp fastapiservice</span><br></pre></td></tr></table></figure>
<h4 id="验证部署成功"><a href="#验证部署成功" class="headerlink" title="验证部署成功"></a>验证部署成功</h4><p><img src="/images/fastapi/docker.jpg" alt=""></p>
<p>用Docker去部署应用已是目前最流行和通用的方式。现在把之前的项目部署到Docker上去。</p>
<h4 id="准备Dockerfile"><a href="#准备Dockerfile" class="headerlink" title="准备Dockerfile">
FastAPI: 项目结构(18)
http://izheyi.com/2021/10/05/FastAPI-项目结构(17)/
2021-10-05T07:16:39.000Z
2021-10-05T03:27:08.570Z
<p>如前言所述,开发一个项目,不可能所所有的文件都放到一个文件里,是需要有结构化的目录。</p>
<p>简单结构如下:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">E:. │ config.py │ dependencies.py │ main.py │ requirements.txt │ ├─models │ │ crud.py │ │ database.py │ │ models.py │ │ schemas.py │ │ __init__.py │ ├─routers │ │ users.py │ │ __init__.py │ ├─static │ fastapi.jpg │ ├─templates │ │ index.html │ ├─utils │ │ __init__.py</span><br></pre></td></tr></table></figure>
<ul>
<li><p>models</p>
<p>对数据库和数据的定义操作</p>
</li>
<li><p>routers</p>
<p>路由控制,更好的按模块化划分系统功能实现</p>
</li>
<li><p>static</p>
<p>引用的静态资源文件,js,css</p>
</li>
<li><p>templates</p>
<p>前面页面的模板文件</p>
</li>
<li><p>dependencies</p>
<p>系统所需的依赖项</p>
</li>
<li><p>main</p>
<p>系统启动入口</p>
</li>
<li><p>config</p>
<p>配置参数,连接数据等</p>
</li>
<li><p>utils</p>
<p>公共的方法,E.g., JWT token的处理</p>
</li>
<li><p>requirements</p>
<p>系统所依赖的外部库</p>
</li>
</ul>
<p>项目请参见:<a href="https://github.com/yongfeiuall/fastapi" target="_blank" rel="external">github-fastapi</a></p>
<p>如前言所述,开发一个项目,不可能所所有的文件都放到一个文件里,是需要有结构化的目录。</p>
<p>简单结构如下:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="l
FastAPI: APIRouter(17)
http://izheyi.com/2021/10/05/FastAPI-APIRouter(17)/
2021-10-05T06:59:41.000Z
2021-10-04T07:55:30.854Z
<p>从前面的例子看,所有的Views操作都放在一个文件里,代码不易维护。为了更好的管理路径和代码,可以通过<code>APIRouter</code>来为该模块创建路径操作,给我们提供了在多个文件中注册路由的功能。</p>
<h4 id="使用Router"><a href="#使用Router" class="headerlink" title="使用Router"></a>使用Router</h4><p>在项目下创建<code>routers -> users.py</code></p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> APIRouter, Depends, HTTPException</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> Session</span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> List</span><br><span class="line"><span class="keyword">from</span> models <span class="keyword">import</span> crud,schemas</span><br><span class="line"><span class="keyword">from</span> dependencies <span class="keyword">import</span> get_db</span><br><span class="line"></span><br><span class="line">router = APIRouter()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 新建用户</span></span><br><span class="line"><span class="decorator">@router.post("/users/", response_model=schemas.User, tags=['users'])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">create_user</span><span class="params">(user: schemas.UserCreate, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> crud.create_user(db, user=user)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 通过id查询用户</span></span><br><span class="line"><span class="decorator">@router.get("/user/{user_id}", response_model=schemas.User, tags=['users'])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">read_user</span><span class="params">(user_id: int, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> db_user = crud.get_user(db, user_id=user_id)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> db_user:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">404</span>, detail=<span class="string">"User not found"</span>)</span><br><span class="line"> <span class="keyword">return</span> db_user</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 所有用户</span></span><br><span class="line"><span class="decorator">@router.get('/users/', response_model=List[schemas.User], tags=['users'])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">read_users</span><span class="params">(start: int=<span class="number">0</span>, limit: int=<span class="number">10</span>, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> crud.get_users(db, start, limit)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Delete user</span></span><br><span class="line"><span class="decorator">@router.delete('/user/{user_id}', response_model=schemas.User, tags=['users'])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete_user</span><span class="params">(user_id: int, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> db_user = crud.delete_user(db, user_id=user_id)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> db_user:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'User not found'</span>)</span><br><span class="line"> <span class="keyword">return</span> db_user</span><br><span class="line"></span><br><span class="line"><span class="comment"># Update User</span></span><br><span class="line"><span class="decorator">@router.put('/user/{user_id}', response_model=schemas.User, tags=['users'])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">update_user</span><span class="params">(user_id: int, update_user: schemas.UserUpdate, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> db_user = crud.update_user(db, user_id=user_id, update_user=update_user)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> db_user:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'User not found'</span>)</span><br><span class="line"> <span class="keyword">return</span> db_user</span><br></pre></td></tr></table></figure>
<h4 id="注册Router"><a href="#注册Router" class="headerlink" title="注册Router"></a>注册Router</h4><p>将我们的 APIRouter 注册到核心对象上去。<code>main.py</code></p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 导入 FastAPI</span></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> FastAPI</span><br><span class="line"><span class="keyword">from</span> routers <span class="keyword">import</span> users</span><br><span class="line"><span class="keyword">import</span> uvicorn</span><br><span class="line"><span class="keyword">import</span> config</span><br><span class="line"></span><br><span class="line"><span class="comment"># 实例化 FastAPI</span></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line">app.include_router(users.router)</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/router.jpg" alt=""></p>
<p>从前面的例子看,所有的Views操作都放在一个文件里,代码不易维护。为了更好的管理路径和代码,可以通过<code>APIRouter</code>来为该模块创建路径操作,给我们提供了在多个文件中注册路由的功能。</p>
<h4 id="使用Router"><a href="
FastAPI: static file & template(16)
http://izheyi.com/2021/10/04/FastAPI-static-template(16)/
2021-10-04T12:32:18.000Z
2021-10-04T08:05:25.210Z
<p>合理的使用静态资源和页面模板,更好的来实现网站前后端分离。</p>
<p>静态资源通过<code>aiofiles</code>来实现。</p>
<p>简单理解,模板就是 <code>web</code> 后端向前端发送的 <code>html</code> 模型,还是<code>jinia2</code>来实现。</p>
<h4 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h4><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">pip <span class="keyword">install</span> aiofiles</span><br><span class="line"></span><br><span class="line">pip <span class="keyword">install</span> jinja2</span><br></pre></td></tr></table></figure>
<h4 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h4><ol>
<li><p>在项目根目录下,创建<code>static</code>和<code>templates</code>文件夹。</p>
</li>
<li><p>在static下添加一个图片<code>fastapi.jpg</code>。</p>
</li>
<li><p>在templates下添加一个页面模板。</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="doctype"><!DOCTYPE html></span></span><br><span class="line"><span class="tag"><<span class="title">html</span>></span></span><br><span class="line"><span class="tag"><<span class="title">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="title">title</span>></span>Welcome<span class="tag"></<span class="title">title</span>></span></span><br><span class="line"> <span class="tag"><<span class="title">link</span> <span class="attribute">href</span>=<span class="value">"{{ url_for('static', path='/fastapi.jpg') }}"</span> <span class="attribute">rel</span>=<span class="value">"stylesheet"</span>></span></span><br><span class="line"><span class="tag"></<span class="title">head</span>></span></span><br><span class="line"><span class="tag"><<span class="title">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="title">h1</span>></span>Welcome: {{ username }}<span class="tag"></<span class="title">h1</span>></span></span><br><span class="line"><span class="tag"></<span class="title">body</span>></span></span><br><span class="line"><span class="tag"></<span class="title">html</span>></span></span><br></pre></td></tr></table></figure>
</li>
<li><p>接口返回html页面。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> fastapi.responses <span class="keyword">import</span> HTMLResponse</span><br><span class="line"><span class="keyword">from</span> fastapi.staticfiles <span class="keyword">import</span> StaticFiles</span><br><span class="line"><span class="keyword">from</span> fastapi.templating <span class="keyword">import</span> Jinja2Templates</span><br><span class="line"></span><br><span class="line">app.mount(<span class="string">"/static"</span>, StaticFiles(directory=<span class="string">"static"</span>), name=<span class="string">"static"</span>)</span><br><span class="line"></span><br><span class="line">templates = Jinja2Templates(directory=<span class="string">"templates"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Return html page</span></span><br><span class="line"><span class="decorator">@app.get("/user/{user_id}", response_class=HTMLResponse)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">read_user</span><span class="params">(request: Request, user_id: int, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> db_user = crud.get_user(db, user_id=user_id)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> db_user:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">404</span>, detail=<span class="string">"User not found"</span>)</span><br><span class="line"> <span class="keyword">return</span> templates.TemplateResponse(<span class="string">'index.html'</span>, {<span class="string">'request'</span>: request, <span class="string">'username'</span>: db_user.email})</span><br></pre></td></tr></table></figure>
</li>
<li><p>结果</p>
<p><img src="/images/fastapi/response_html.jpg" alt=""></p>
</li>
</ol>
<p>合理的使用静态资源和页面模板,更好的来实现网站前后端分离。</p>
<p>静态资源通过<code>aiofiles</code>来实现。</p>
<p>简单理解,模板就是 <code>web</code> 后端向前端发送的 <code>html</code> 模型,还是<c
FastAPI: Mysql数据库操作(15)
http://izheyi.com/2021/10/04/FastAPI-数据库操作(15)/
2021-10-04T09:24:50.000Z
2021-10-04T07:55:58.576Z
<p>FastAPI可以通过SQLAlchemy将其轻松的适应于任何的数据库。SQLAlchemy是一个ORM(object-relational mapping)的框架。在ORM中,你创建一个类就会通过SQLAlchemy将其自动转成一张表,在类中的每一个属性就会将其转成表中的字段。</p>
<p>需要安装</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install sqlalchemy pip install pymysql</span><br></pre></td></tr></table></figure>
<p>统一来管理数据库相关:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">├─db</span><br><span class="line">│ crud.py</span><br><span class="line">│ database.py</span><br><span class="line">│ models.py</span><br><span class="line">│ schemas.py</span><br><span class="line">│ __init__.py</span><br></pre></td></tr></table></figure>
<h4 id="database-py-数据库配置相关"><a href="#database-py-数据库配置相关" class="headerlink" title="database.py 数据库配置相关"></a>database.py 数据库配置相关</h4><p>在数据库相关的配置文件中,首先创建一个SQLAlchemy的”engine”,然后创建SessionLocal实例进行会话,最后创建模型类的基类。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> create_engine</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.ext.declarative <span class="keyword">import</span> declarative_base</span><br><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> sessionmaker</span><br><span class="line"></span><br><span class="line">SQLALCHEMY_DATABASE_URL = <span class="string">"mysql+pymysql://root:123456@127.0.0.1:3306/test"</span></span><br><span class="line"></span><br><span class="line">engine = create_engine(</span><br><span class="line"> SQLALCHEMY_DATABASE_URL, encoding=<span class="string">'utf8'</span>, echo=<span class="keyword">True</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># SQLAlchemy中,CRUD是通过会话进行管理的,所以需要先创建会话,</span></span><br><span class="line">SessionLocal = sessionmaker(autocommit=<span class="keyword">False</span>, autoflush=<span class="keyword">False</span>, bind=engine)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建基本映射类</span></span><br><span class="line">Base = declarative_base()</span><br></pre></td></tr></table></figure>
<h4 id="models-py-数据库模型表"><a href="#models-py-数据库模型表" class="headerlink" title="models.py 数据库模型表"></a>models.py 数据库模型表</h4><p>通过数据库配置文件中的基类来创建模型类。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy <span class="keyword">import</span> Boolean, Column, Integer, String</span><br><span class="line"><span class="keyword">from</span> database <span class="keyword">import</span> Base</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span><span class="params">(Base)</span>:</span></span><br><span class="line"> __tablename__ = <span class="string">"users"</span></span><br><span class="line"> id = Column(Integer, primary_key=<span class="keyword">True</span>, index=<span class="keyword">True</span>)</span><br><span class="line"> email = Column(String(<span class="number">32</span>), unique=<span class="keyword">True</span>, index=<span class="keyword">True</span>)</span><br><span class="line"> hash_password = Column(String(<span class="number">32</span>))</span><br><span class="line"> is_active = Column(Boolean, default=<span class="keyword">True</span>)</span><br></pre></td></tr></table></figure>
<h4 id="schemas-py-模型验证"><a href="#schemas-py-模型验证" class="headerlink" title="schemas.py 模型验证"></a>schemas.py 模型验证</h4><p>定义请求参数模型验证与响应模型验证的Pydantic模型,其中响应模型中设置orm_mode=True参数,表示与ORM模型兼容,因为后续中返回的数据库查询是orm模型,通过设置这个参数可以将orm模型通过pydantic模型进行验证。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> pydantic <span class="keyword">import</span> BaseModel</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserBase</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> email: str</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserCreate</span><span class="params">(UserBase)</span>:</span></span><br><span class="line"> <span class="string">"""</span><br><span class="line"> 请求模型验证:</span><br><span class="line"> email:</span><br><span class="line"> password:</span><br><span class="line"> """</span></span><br><span class="line"> password: str</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserUpdate</span><span class="params">(UserBase)</span>:</span></span><br><span class="line"> is_active: bool</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span><span class="params">(UserBase)</span>:</span></span><br><span class="line"> <span class="string">"""</span><br><span class="line"> 响应模型:</span><br><span class="line"> id:</span><br><span class="line"> email:</span><br><span class="line"> is_active</span><br><span class="line"> 并且设置orm_mode与之兼容</span><br><span class="line"> """</span></span><br><span class="line"> id: int</span><br><span class="line"> is_active: bool</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Config</span>:</span></span><br><span class="line"> orm_mode = <span class="keyword">True</span></span><br></pre></td></tr></table></figure>
<h4 id="crud-py-数据库操作相关"><a href="#crud-py-数据库操作相关" class="headerlink" title="crud.py 数据库操作相关"></a>crud.py 数据库操作相关</h4><p>通过传入数据库连接以及参数等进行数据库操作,包括创建用户、查询用户等,返回的是orm模型对象。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> sqlalchemy.orm <span class="keyword">import</span> Session</span><br><span class="line"><span class="keyword">import</span> models, schemas</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Get user by id</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_user</span><span class="params">(db: Session, user_id: int)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> db.query(models.User).filter(models.User.id == user_id).first()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Get all users</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_users</span><span class="params">(db: Session, start: int=<span class="number">0</span>, limit: int=<span class="number">10</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> db.query(models.User).offset(start).limit(limit).all()</span><br><span class="line"></span><br><span class="line"><span class="comment"># creae user</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">create_user</span><span class="params">(db: Session, user: schemas.UserCreate)</span>:</span></span><br><span class="line"> fake_hashed_password = user.password + <span class="string">"notreallyhashed"</span></span><br><span class="line"> user = models.User(email=user.email, hash_password=fake_hashed_password)</span><br><span class="line"> db.add(user)</span><br><span class="line"> db.commit() <span class="comment"># 提交保存到数据库中</span></span><br><span class="line"> db.refresh(user) <span class="comment"># 刷新</span></span><br><span class="line"> <span class="keyword">return</span> user</span><br><span class="line"></span><br><span class="line"><span class="comment"># Delete user</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete_user</span><span class="params">(db: Session, user_id: int)</span>:</span></span><br><span class="line"> user = db.query(models.User).filter(models.User.id == user_id).first()</span><br><span class="line"> <span class="keyword">if</span> user:</span><br><span class="line"> db.delete(user)</span><br><span class="line"> db.commit()</span><br><span class="line"> db.flush()</span><br><span class="line"> <span class="keyword">return</span> user</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># Update user</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">update_user</span><span class="params">(db: Session, user_id: int, update_user: schemas.UserUpdate)</span>:</span></span><br><span class="line"> user = db.query(models.User).filter(models.User.id == user_id).first()</span><br><span class="line"> <span class="keyword">if</span> user:</span><br><span class="line"> update_user = update_user.dict(exclude_unset=<span class="keyword">True</span>)</span><br><span class="line"> <span class="keyword">for</span> k, v <span class="keyword">in</span> update_user.items():</span><br><span class="line"> setattr(user, k, v)</span><br><span class="line"> db.commit()</span><br><span class="line"> db.flush()</span><br><span class="line"> db.refresh(user)</span><br><span class="line"> <span class="keyword">return</span> user</span><br></pre></td></tr></table></figure>
<h4 id="调用验证"><a href="#调用验证" class="headerlink" title="调用验证"></a>调用验证</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line">Base.metadata.create_all(bind=engine) <span class="comment">#数据库初始化,如果没有库或者表,会自动创建</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 实例化 FastAPI</span></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line"><span class="comment"># Dependency</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_db</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="string">"""</span><br><span class="line"> 每一个请求处理完毕后会关闭当前连接,不同的请求使用不同的连接</span><br><span class="line"> :return:</span><br><span class="line"> """</span></span><br><span class="line"> db = SessionLocal()</span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">yield</span> db</span><br><span class="line"> <span class="keyword">finally</span>:</span><br><span class="line"> db.close()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 新建用户</span></span><br><span class="line"><span class="decorator">@app.post("/users/", response_model=schemas.User)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">create_user</span><span class="params">(user: schemas.UserCreate, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> crud.db_create_user(db=db, user=user)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 通过id查询用户</span></span><br><span class="line"><span class="decorator">@app.get("/user/{user_id}", response_model=schemas.User)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">read_user</span><span class="params">(user_id: int, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> db_user = crud.get_user(db, user_id=user_id)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> db_user:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">404</span>, detail=<span class="string">"User not found"</span>)</span><br><span class="line"> <span class="keyword">return</span> db_user</span><br><span class="line"></span><br><span class="line"><span class="comment"># 所有用户</span></span><br><span class="line"><span class="decorator">@app.get('/users/', response_model=List[schemas.User])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">read_users</span><span class="params">(start: int=<span class="number">0</span>, limit: int=<span class="number">10</span>, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> crud.get_users(db, start, limit)</span><br><span class="line"></span><br><span class="line"><span class="comment"># Delete user</span></span><br><span class="line"><span class="decorator">@app.delete('/user/{user_id}', response_model=schemas.User)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">delete_user</span><span class="params">(user_id: int, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> db_user = crud.delete_user(db, user_id=user_id)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> db_user:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'User not found'</span>)</span><br><span class="line"> <span class="keyword">return</span> db_user</span><br><span class="line"></span><br><span class="line"><span class="comment"># Update User</span></span><br><span class="line"><span class="decorator">@app.put('/user/{user_id}', response_model=schemas.User)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">update_user</span><span class="params">(user_id: int, update_user: schemas.UserUpdate, db: Session = Depends<span class="params">(get_db)</span>)</span>:</span></span><br><span class="line"> db_user = crud.update_user(db, user_id=user_id, update_user=update_user)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> db_user:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'User not found'</span>)</span><br><span class="line"> <span class="keyword">return</span> db_user</span><br></pre></td></tr></table></figure>
<p>DB:</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mysql> desc users;</span><br><span class="line">+---------------+-------------+------+-----+---------+----------------+</span><br><span class="line">| Field | Type | Null | Key | Default | Extra |</span><br><span class="line">+---------------+-------------+------+-----+---------+----------------+</span><br><span class="line">| id | int(11) | NO | PRI | NULL | auto_increment |</span><br><span class="line">| email | varchar(32) | YES | UNI | NULL | |</span><br><span class="line">| hash_password | varchar(32) | YES | | NULL | |</span><br><span class="line">| is_active | tinyint(1) | YES | | NULL | |</span><br><span class="line">+---------------+-------------+------+-----+---------+----------------+</span><br><span class="line">4 rows in set (0.00 sec)</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/db_add.jpg" alt=""></p>
<p><img src="/images/fastapi/db_search.jpg" alt=""></p>
<p><img src="/images/fastapi/db_update.jpg" alt=""></p>
<p><img src="/images/fastapi/db_delete.jpg" alt=""></p>
<p>FastAPI可以通过SQLAlchemy将其轻松的适应于任何的数据库。SQLAlchemy是一个ORM(object-relational mapping)的框架。在ORM中,你创建一个类就会通过SQLAlchemy将其自动转成一张表,在类中的每一个属性就会将其转成表中的
FastAPI: Security(14)
http://izheyi.com/2021/10/03/FastAPI-Security(14)/
2021-10-03T01:58:29.000Z
2021-10-05T01:47:22.879Z
<p>安全,权限对于任何一个系统是不可避免的。</p>
<p>FastAPI提供了多种工具,可帮助你以标准的方式轻松、快速地处理安全性,而无需研究和学习所有的安全规范。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">oauth2_scheme = OAuth2PasswordBearer(tokenUrl=<span class="string">"token"</span>)</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/users/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">users</span><span class="params">(token: str = Depends<span class="params">(oauth2_scheme)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'token'</span>: token}</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/security_np.jpg" alt=""></p>
<h5 id="简单-Bear-OAuth2实现完整流程"><a href="#简单-Bear-OAuth2实现完整流程" class="headerlink" title="简单 Bear OAuth2实现完整流程"></a>简单 Bear OAuth2实现完整流程</h5><p>在FastAPI中, 提供了多种认证解决方案工具, 其中也包括了OAuth2, 可以使用<code>OAuth2PasswordBearer</code>类来实现OAuth2的功能, 使用的是OAuth2中的一种认证方案, 通过<code>bearer token</code>来携带<code>token</code>, 具体做法就是:</p>
<p>在请求头中添加参数<code>Authorization</code>, 其值为<code>Bearer</code>和<code>token</code>中间使用<code>空格</code>连接形成的字符串, 如<code>Bearer your_token_string</code>, 注意, <code>Authorization</code>和<code>Bearer</code>都是规范中固定的写法, 不可修改</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line">fake_user = {</span><br><span class="line"> <span class="string">'admin'</span>:{</span><br><span class="line"> <span class="string">'username'</span>: <span class="string">'admin'</span>,</span><br><span class="line"> <span class="string">'hash_password'</span>: <span class="string">'fakehashadmin'</span>,</span><br><span class="line"> <span class="string">'enabled'</span>: <span class="keyword">True</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">oauth2_scheme = OAuth2PasswordBearer(tokenUrl=<span class="string">"token"</span>)<span class="comment"># 这个参数的作用是当输入完用户密码后, 点击Authorize按钮后, 会请求127.0.0.1:8000/token这个网址, 把用户密码通过Form表单的形式传递给这个请求, 即这个/token就是我们网站的后台登录接口.</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">fake_hash_password</span><span class="params">(password: str)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"fakehash"</span> + password</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> username: str</span><br><span class="line"> enbaled: Optional[bool] = <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserPwd</span><span class="params">(User)</span>:</span></span><br><span class="line"> hash_password: str</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.post('/login')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">login</span><span class="params">(form: OAuth2PasswordRequestForm = Depends<span class="params">()</span>)</span>:</span></span><br><span class="line"> user = fake_user.get(form.username)</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> user:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'user incorrect'</span>)</span><br><span class="line"> u = UserPwd(**user)</span><br><span class="line"> hashpwd = fake_hash_password(form.password)</span><br><span class="line"> <span class="keyword">if</span> hashpwd != u.hash_password:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'passwod incorrect'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">"access_token"</span>: u.username, <span class="string">"token_type"</span>: <span class="string">"bearer"</span>}</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/security_username.jpg" alt=""></p>
<p><img src="/images/fastapi/security_password.jpg" alt=""></p>
<p><img src="/images/fastapi/security_correct.jpg" alt=""></p>
<p>安全,权限对于任何一个系统是不可避免的。</p>
<p>FastAPI提供了多种工具,可帮助你以标准的方式轻松、快速地处理安全性,而无需研究和学习所有的安全规范。</p>
<figure class="highlight python"><table><tr><td cla
FastAPI: Dependencies(13)
http://izheyi.com/2021/10/03/FastAPI-Dependencies(13)/
2021-10-03T00:35:43.000Z
2021-10-04T07:56:52.990Z
<p>FastAPI 提供了简单易用,但功能强大的<strong>依赖注入</strong>系统,可以让开发人员轻松地把组件集成至 <strong>FastAPI</strong>。</p>
<p>编程中的<strong>「依赖注入」</strong>是声明代码(本文中为<em>路径操作函数</em> )运行所需的,或要使用的「依赖」的一种方式。</p>
<h4 id="基本示例"><a href="#基本示例" class="headerlink" title="基本示例"></a>基本示例</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">result</span><span class="params">(start: int = <span class="number">0</span>, limit: int = <span class="number">10</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'start'</span>: start, <span class="string">'limit'</span>: limit}</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/users/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">users</span><span class="params">(res: dict = Depends<span class="params">(result)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/books/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">books</span><span class="params">(res: dict = Depends<span class="params">(result)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> res</span><br></pre></td></tr></table></figure>
<p>这里只能传给 Depends 一个参数。</p>
<p>接收到新的请求时,<strong>FastAPI</strong> 执行如下操作:</p>
<ul>
<li>用正确的参数调用依赖项函数(「可依赖项」)</li>
<li>获取函数返回的结果</li>
<li>把函数返回的结果赋值给<em>路径操作函数</em>的参数</li>
</ul>
<h4 id="Class-依赖注入"><a href="#Class-依赖注入" class="headerlink" title="Class 依赖注入"></a>Class 依赖注入</h4><p>也可以用Python Class来实现依赖注入。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Result</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, start: int = <span class="number">0</span>, limit: int = <span class="number">10</span>)</span>:</span></span><br><span class="line"> self.start = start</span><br><span class="line"> self.limit = limit</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/users/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">users</span><span class="params">(res: Result = Depends<span class="params">(Result)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> res</span><br></pre></td></tr></table></figure>
<p>引用时,也可以用下边方式:</p>
<p><code>res: Result = Depends()</code></p>
<h4 id="子依赖注入"><a href="#子依赖注入" class="headerlink" title="子依赖注入"></a>子依赖注入</h4><p>FastAPI 支持创建含任意深度的子依赖项。类似于函数调用:fun1 -> fun2 -> fun3</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">result1</span><span class="params">(start: int = <span class="number">0</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> start</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">result2</span><span class="params">(start: int = Depends<span class="params">(result1)</span>, limit: int = <span class="number">0</span>)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> limit:</span><br><span class="line"> <span class="keyword">return</span> limit</span><br><span class="line"> <span class="keyword">return</span> start</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/users/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">users</span><span class="params">(res: dict = Depends<span class="params">(result2)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> res</span><br></pre></td></tr></table></figure>
<p>结果</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://127.0.0.1:8000/users/?start=2 2 http://127.0.0.1:8000/users/?limit=5 5</span><br></pre></td></tr></table></figure>
<h4 id="多次使用同一个依赖项"><a href="#多次使用同一个依赖项" class="headerlink" title="多次使用同一个依赖项"></a>多次使用同一个依赖项</h4><p>如果在同一个<em>路径操作</em> 多次声明了同一个依赖项,例如,多个依赖项共用一个子依赖项,FastAPI 在处理同一请求时,只调用一次该子依赖项。</p>
<p>FastAPI 不会为同一个请求多次调用同一个依赖项,而是把依赖项的返回值进行「缓存」,并把它传递给同一请求中所有需要使用该返回值的「依赖项」。</p>
<p>在高级使用场景中,如果不想使用「缓存」值,而是为需要在同一请求的每一步操作(多次)中都实际调用依赖项,可以把 <code>Depends</code> 的参数 <code>use_cache</code> 的值设置为 <code>False</code></p>
<h4 id="路径依赖"><a href="#路径依赖" class="headerlink" title="路径依赖"></a>路径依赖</h4><p>以在<em>路径操作装饰器</em>中添加一个由 <code>dependencies</code> 组成的 <code>list</code>来注入多个引用。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">result1</span><span class="params">(start: int)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> start != <span class="number">2</span>:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'start not == 2'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">result2</span><span class="params">(limit: int)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> limit != <span class="number">5</span>:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'limit not == 5'</span>)</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/users/', dependencies=[Depends(result1), Depends(result2)])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">users</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'status'</span>: <span class="string">'ok'</span>}</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/injection_path_1.jpg" alt=""></p>
<p><img src="/images/fastapi/injection_path_2.jpg" alt=""></p>
<p><img src="/images/fastapi/injection_path_3.jpg" alt=""></p>
<h4 id="全局依赖"><a href="#全局依赖" class="headerlink" title="全局依赖"></a>全局依赖</h4><p>有时,我们要为整个应用添加依赖项。</p>
<p>可以把依赖项添加至整个 <code>FastAPI</code> 应用。后续就可以为所有<em>路径操作</em>应用该依赖项。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">result1</span><span class="params">(start: int)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> start != <span class="number">2</span>:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'start not == 2'</span>)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">result2</span><span class="params">(limit: int)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> limit != <span class="number">5</span>:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(status_code=<span class="number">400</span>, detail=<span class="string">'limit not == 5'</span>)</span><br><span class="line"></span><br><span class="line">app = FastAPI(dependencies=[Depends(result1), Depends(result2)])</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/users/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">users</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'status'</span>: <span class="string">'ok'</span>}</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/books/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">users</span><span class="params">()</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'status'</span>: <span class="string">'ok'</span>}</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/injection_path_3.jpg" alt=""></p>
<p><img src="/images/fastapi/injection_path_4.jpg" alt=""></p>
<p>FastAPI 提供了简单易用,但功能强大的<strong>依赖注入</strong>系统,可以让开发人员轻松地把组件集成至 <strong>FastAPI</strong>。</p>
<p>编程中的<strong>「依赖注入」</strong>是声明代码(本文中为<em>
FastAPI: 响应模型(12)
http://izheyi.com/2021/10/02/FastAPI-响应模型(12)/
2021-10-02T01:27:24.000Z
2021-10-04T07:57:44.180Z
<p>在前面的操作中也看到,请求响应返回的就是请求体的内容,比如,要是登记注册就是返回用户名和密码,这种行为是不行的。</p>
<p>可以使用 <code>response_model</code> 参数来声明用于响应的模型。</p>
<ul>
<li>将输出数据转换为其声明的类型。</li>
<li>校验数据。</li>
<li>会将输出数据限制在该模型定义内。</li>
<li>在 OpenAPI 的<em>路径操作</em>中为响应添加一个 JSON Schema。</li>
<li>并在自动生成文档系统中使用。</li>
</ul>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> username: str</span><br><span class="line"> password: str</span><br><span class="line"> email: EmailStr</span><br><span class="line"> adress: Optional[str] = <span class="string">'beijing'</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserRes</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> username: str</span><br><span class="line"> email: EmailStr</span><br><span class="line"> address: Optional[str] = <span class="string">'beijing'</span></span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.post('/user/register/', response_model=UserRes)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">register</span><span class="params">(user: User)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> user</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/response_model.jpg" alt=""></p>
<p>上面的例子中,可以看到响应里会自动的返回请求体里默认的字段。如果请求体中不传,就不要返回带默认值的字段,可以用<code>response_model_exclude_unset</code>来实现</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> username: str</span><br><span class="line"> password: str</span><br><span class="line"> email: EmailStr</span><br><span class="line"> adress: Optional[str] = <span class="string">'beijing'</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserRes</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> username: str</span><br><span class="line"> email: EmailStr</span><br><span class="line"> address: Optional[str] = <span class="string">'beijing'</span></span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.post('/user/register/', response_model=UserRes, response_model_exclude_unset=True)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">register</span><span class="params">(user: User)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> user</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/response_model_default.jpg" alt=""></p>
<p>在前面的操作中也看到,请求响应返回的就是请求体的内容,比如,要是登记注册就是返回用户名和密码,这种行为是不行的。</p>
<p>可以使用 <code>response_model</code> 参数来声明用于响应的模型。</p>
<ul>
<li>将输出数据转换为其声明的类
FastAPI: 响应状态码(11)
http://izheyi.com/2021/10/02/FastAPI-响应状态码(11)/
2021-10-02T01:27:09.000Z
2021-10-04T08:00:14.471Z
<p>可以使用 <code>status_code</code> 参数来声明用于响应的 HTTP 状态码。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="decorator">@app.post('/uploads/', status_code=201)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(files: List[UploadFile] = File<span class="params">(...)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'filename'</span>: [file.filename <span class="keyword">for</span> file <span class="keyword">in</span> files]}</span><br></pre></td></tr></table></figure>
<p>默认返回的状态码是200,修改后返回201。</p>
<p>HTTP的状态码很多,也不容易 记住所有状态码的意思,FastAPI提供了更方便的方式。</p>
<p><img src="/images/fastapi/response_code.jpg" alt=""></p>
<p>可以使用 <code>status_code</code> 参数来声明用于响应的 HTTP 状态码。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="l
FastAPI: 请求Form&File(10)
http://izheyi.com/2021/10/02/FastAPI-请求Form-File(10)/
2021-10-02T00:29:35.000Z
2021-10-04T08:00:58.245Z
<p>Form表单和File Upload是避免不了的操作。</p>
<p>要使用表单,需预先安装 python-multipart</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install python-multipart</span><br></pre></td></tr></table></figure>
<h4 id="Form"><a href="#Form" class="headerlink" title="Form"></a>Form</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="decorator">@app.post('/login/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(username : str = Form<span class="params">(...)</span>, password: str = Form<span class="params">(...)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'username'</span>: username, <span class="string">'password'</span>: password}</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_form.jpg" alt=""></p>
<h4 id="File"><a href="#File" class="headerlink" title="File"></a>File</h4><p>文件上传也是以Form形式发送。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="decorator">@app.post('/upload/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(file: UploadFile = File<span class="params">(...)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'filename'</span>: file.filename}</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_file.jpg" alt=""></p>
<p>也支持多个文件上传</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="decorator">@app.post('/uploads/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(files: List[UploadFile] = File<span class="params">(...)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'filename'</span>: [file.filename <span class="keyword">for</span> file <span class="keyword">in</span> files]}</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_files.jpg" alt=""></p>
<p>Form表单和File Upload是避免不了的操作。</p>
<p>要使用表单,需预先安装 python-multipart</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span cl
FastAPI: 请求Header&Cookie(9)
http://izheyi.com/2021/10/02/FastAPI-请求Header-Cookie(9)/
2021-10-01T23:26:40.000Z
2021-10-04T08:05:55.476Z
<h4 id="Header"><a href="#Header" class="headerlink" title="Header"></a>Header</h4><p>请求自定义Header。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="decorator">@app.get('/users/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(user_agent: Optional[str] = Header<span class="params">(None)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'User-Agent'</span>: user_agent}</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_header.jpg" alt=""></p>
<h4 id="Cookie"><a href="#Cookie" class="headerlink" title="Cookie"></a>Cookie</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="decorator">@app.get('/users/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(user_agent: Optional[str] = Header<span class="params">(None)</span>,</span><br><span class="line"> sid: Optional[str] = Cookie<span class="params">(None)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> {<span class="string">'User-Agent'</span>: user_agent}, {<span class="string">'sid'</span>: sid}</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_cookie.jpg" alt=""></p>
<h4 id="Header"><a href="#Header" class="headerlink" title="Header"></a>Header</h4><p>请求自定义Header。</p>
<figure class="highlight python"><tab
FastAPI: 请求体嵌套模型(8)
http://izheyi.com/2021/10/01/FastAPI-请求体嵌套模型(8)/
2021-10-01T06:53:17.000Z
2021-10-04T08:01:43.129Z
<p>可以将一个模型属性定义为有多子元素的类型。<code>List</code>来实现</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> name: str = Field(..., min_length=<span class="number">2</span>, max_length=<span class="number">10</span>)</span><br><span class="line"> price: float</span><br><span class="line"> description: Optional[str] = <span class="keyword">None</span></span><br><span class="line"> tag: List[str] = <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.post('/books/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(book: Book = Body<span class="params">(..., embed=True)</span>,</span><br><span class="line"> query: Optional[str] = None)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> book, query</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_list.jpg" alt=""></p>
<p>还可以在模型里嵌套模型,可以做到任意深度嵌套,来实现更复杂的结构。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Author</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> name: str</span><br><span class="line"> address: Optional[str] = <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> name: str</span><br><span class="line"> price: float</span><br><span class="line"> description: Optional[str] = <span class="keyword">None</span></span><br><span class="line"> author: Author</span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.post('/authors/{id}')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(*,</span><br><span class="line"> id: int = Path<span class="params">(..., gt=<span class="number">12</span>)</span>,</span><br><span class="line"> book: Book)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> id, book</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_model.jpg" alt=""></p>
<p>可以将一个模型属性定义为有多子元素的类型。<code>List</code>来实现</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</s
FastAPI: 请求体Query-Path-Body-Field(7)
http://izheyi.com/2021/10/01/FastAPI-请求体Query-Path-Body-Field(7)/
2021-10-01T03:22:52.000Z
2021-10-04T08:01:47.717Z
<h4 id="多参数混用Path和Query"><a href="#多参数混用Path和Query" class="headerlink" title="多参数混用Path和Query"></a>多参数混用Path和Query</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> name: str</span><br><span class="line"> price: float</span><br><span class="line"> description: Optional[str] = <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Author</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> name: str</span><br><span class="line"> address: Optional[str] = <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.post('/authors/{id}')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(*,</span><br><span class="line"> id: int = Path<span class="params">(..., gt=<span class="number">12</span>)</span>,</span><br><span class="line"> author: Author,</span><br><span class="line"> book: Book,</span><br><span class="line"> query: Optional[str] = Query<span class="params">(None)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> id, author, book, query</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_path_query.jpg" alt=""></p>
<h4 id="Body实现请求体中单一值"><a href="#Body实现请求体中单一值" class="headerlink" title="Body实现请求体中单一值"></a>Body实现请求体中单一值</h4><p>在设定好的模型之外,想在发送请求时另外加一个字段进去,如果单独定义就会把它当成一个查询参数,可以利用<code>Body</code>来实现。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> name: str</span><br><span class="line"> price: float</span><br><span class="line"> description: Optional[str] = <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Author</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> name: str</span><br><span class="line"> address: Optional[str] = <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.post('/authors/{id}')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(*,</span><br><span class="line"> id: int = Path<span class="params">(..., gt=<span class="number">12</span>)</span>,</span><br><span class="line"> author: Author,</span><br><span class="line"> book: Book,</span><br><span class="line"> publisher: str = Body<span class="params">(...)</span>,</span><br><span class="line"> query: Optional[str] = Query<span class="params">(None)</span>)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> id, author, book, publisher, query</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_body.jpg" alt=""></p>
<h4 id="嵌入请求体参数"><a href="#嵌入请求体参数" class="headerlink" title="嵌入请求体参数"></a>嵌入请求体参数</h4><p>如果想把请求体外边再加一个字段来标识,也可以通过<code>Body</code>来实现</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> name: str</span><br><span class="line"> price: float</span><br><span class="line"> description: Optional[str] = <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.post('/books/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(book: Book = Body<span class="params">(..., embed=True)</span>,</span><br><span class="line"> query: Optional[str] = None)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> book, query</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_embed.jpg" alt=""></p>
<h4 id="Field校验模型里字段"><a href="#Field校验模型里字段" class="headerlink" title="Field校验模型里字段"></a>Field校验模型里字段</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> name: str = Field(..., min_length=<span class="number">2</span>, max_length=<span class="number">10</span>)</span><br><span class="line"> price: float</span><br><span class="line"> description: Optional[str] = <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.post('/books/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(book: Book = Body<span class="params">(..., embed=True)</span>,</span><br><span class="line"> query: Optional[str] = None)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> book, query</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_field.jpg" alt=""></p>
<h4 id="多参数混用Path和Query"><a href="#多参数混用Path和Query" class="headerlink" title="多参数混用Path和Query"></a>多参数混用Path和Query</h4><figure class="highli
FastAPI: 请求体基础(6)
http://izheyi.com/2021/10/01/FastAPI-请求体基础(6)/
2021-10-01T03:22:10.000Z
2021-10-04T08:02:16.354Z
<p>这个不多说,就是客户端发送给 API 的数据。通过Pydantic来声明请求体。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Book</span><span class="params">(BaseModel)</span>:</span></span><br><span class="line"> name: str</span><br><span class="line"> price: float</span><br><span class="line"> description: Optional[str] = <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.post('/books/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(book: Book)</span>:</span></span><br><span class="line"> <span class="keyword">return</span> book</span><br></pre></td></tr></table></figure>
<p><img src="/images/fastapi/request_basic.jpg" alt=""></p>
<p>这个不多说,就是客户端发送给 API 的数据。通过Pydantic来声明请求体。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</sp
FastAPI: 请求路径参数的校验(5)
http://izheyi.com/2021/09/29/FastAPI-请求路径参数的校验(5)/
2021-09-29T01:48:23.000Z
2021-10-04T08:03:06.101Z
<p>可以通过 <code>Path</code> 对路径参数做校验。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">books = [{<span class="string">'name'</span>: <span class="string">'python'</span>}, {<span class="string">'name'</span>: <span class="string">'fastapi'</span>}, {<span class="string">'name'</span>: <span class="string">'java'</span>}, ]</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/users/{id}')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(id: int = Path<span class="params">(..., gt=<span class="number">12</span>)</span>,</span><br><span class="line"> start: int = <span class="number">0</span>,</span><br><span class="line"> limit: Optional[int] = None)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> limit:</span><br><span class="line"> <span class="keyword">return</span> {id: books[start : limit]}</span><br><span class="line"> <span class="keyword">return</span> {id: books[start: ]}</span><br></pre></td></tr></table></figure>
<ul>
<li>gt:大于(greater than)</li>
<li>ge:大于等于(greater than or equal)</li>
<li>lt:小于(less than)</li>
<li>le:小于等于(less than or equal)</li>
</ul>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">http://<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">8000</span>/users/<span class="number">11</span></span><br><span class="line"></span><br><span class="line">{<span class="string">"detail"</span>:[{<span class="string">"loc"</span>:[<span class="string">"path"</span>,<span class="string">"id"</span>],<span class="string">"msg"</span>:<span class="string">"ensure this value is greater than 12"</span>,<span class="string">"type"</span>:<span class="string">"value_error.number.not_gt"</span>,<span class="string">"ctx"</span>:{<span class="string">"limit_value"</span>:<span class="number">12</span>}}]}</span><br></pre></td></tr></table></figure>
<h4 id="参数排序"><a href="#参数排序" class="headerlink" title="参数排序"></a>参数排序</h4><p>有默认值参数和必填参数值都存在时,有默认值参数必须放到必填参数后边。</p>
<p>传递 <code>*</code> 作为函数的第一个参数。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="decorator">@app.get('/users/{id}')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(*, id: int = Path<span class="params">(..., gt=<span class="number">12</span>)</span>,</span><br><span class="line"> start: int = <span class="number">0</span>,</span><br><span class="line"> limit: Optional[int] = None)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> limit:</span><br><span class="line"> <span class="keyword">return</span> {id: books[start : limit]}</span><br><span class="line"> <span class="keyword">return</span> {id: books[start: ]}</span><br></pre></td></tr></table></figure>
<p>Python 不会对该 <code>*</code> 做任何事情,它将之后的所有参数都作为关键字参数来调用。</p>
<p>可以通过 <code>Path</code> 对路径参数做校验。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><
FastAPI: 请求查询参数的校验(4)
http://izheyi.com/2021/09/28/FastAPI-请求查询参数的校验(4)/
2021-09-28T01:48:10.000Z
2021-10-04T08:02:48.077Z
<p>可以通过Query来实现对字符串查询参数的校验。</p>
<h4 id="常规校验"><a href="#常规校验" class="headerlink" title="常规校验"></a>常规校验</h4><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">books = [{<span class="string">'name'</span>: <span class="string">'python'</span>}, {<span class="string">'name'</span>: <span class="string">'fastapi'</span>}, {<span class="string">'name'</span>: <span class="string">'java'</span>}]</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/books/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(book: str = Query<span class="params">(<span class="string">'Java Script'</span>, min_length=<span class="number">2</span>)</span>)</span>:</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> book:</span><br><span class="line"> books.append({<span class="string">'name'</span>: book})</span><br><span class="line"> <span class="keyword">return</span> books</span><br></pre></td></tr></table></figure>
<p>参数的默认值是Java Script,最小长度是2。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">http://127.0.0.1:8000/books/</span><br><span class="line">[{"name":"python"},{"name":"fastapi"},{"name":"java"},{"name":"Java Script"}]</span><br><span class="line"></span><br><span class="line">http://127.0.0.1:8000/books/?book=c</span><br><span class="line">{"detail":[{"loc":["query","book"],"msg":"ensure this value has at least 2 characters","type":"value_error.any_str.min_length","ctx":{"limit_value":2}}]}</span><br></pre></td></tr></table></figure>
<p>甚至可以直接用正则表达式来做检验,一般应该也用不到。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="decorator">@app.get('/books/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(book: Optional[str] = Query<span class="params">(<span class="string">'Java Script'</span>, min_length=<span class="number">2</span>, regex=<span class="string">'^J'</span>)</span>)</span>:</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> book:</span><br><span class="line"> books.append({<span class="string">'name'</span>: book})</span><br><span class="line"> <span class="keyword">return</span> books</span><br></pre></td></tr></table></figure>
<h4 id="必填参数"><a href="#必填参数" class="headerlink" title="必填参数"></a>必填参数</h4><p>可以将 <code>...</code> 用作第一个参数值,在声明必填参数。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="decorator">@app.get('/books/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(book: str = Query<span class="params">(..., min_length=<span class="number">2</span>, regex=<span class="string">'^J'</span>)</span>)</span>:</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> book:</span><br><span class="line"> books.append({<span class="string">'name'</span>: book})</span><br><span class="line"> <span class="keyword">return</span> books</span><br></pre></td></tr></table></figure>
<h4 id="参数列表多个值"><a href="#参数列表多个值" class="headerlink" title="参数列表多个值"></a>参数列表多个值</h4><p>查询参数可以声明时接受多个值</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">books = [<span class="string">'python'</span>, <span class="string">'fastapi'</span>]</span><br><span class="line"></span><br><span class="line"><span class="decorator">@app.get('/books/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">root</span><span class="params">(book: Optional[list] = Query<span class="params">(None)</span>)</span>:</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> book:</span><br><span class="line"> books.extend(book)</span><br><span class="line"> <span class="keyword">return</span> books</span><br></pre></td></tr></table></figure>
<p>结果</p>
<figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">http</span>:<span class="comment">//127.0.0.1:8000/books/</span></span><br><span class="line">[<span class="string">"python"</span>,<span class="string">"fastapi"</span>]</span><br><span class="line"></span><br><span class="line"><span class="attribute">http</span>:<span class="comment">//127.0.0.1:8000/books/?book=java&book=c</span></span><br><span class="line">[<span class="string">"python"</span>,<span class="string">"fastapi"</span>,<span class="string">"java"</span>,<span class="string">"c"</span>]</span><br></pre></td></tr></table></figure>
<p>还有下面的校验,用到再说</p>
<ul>
<li>alia</li>
<li>title</li>
<li>description</li>
<li>deprecated</li>
</ul>
<p>可以通过Query来实现对字符串查询参数的校验。</p>
<h4 id="常规校验"><a href="#常规校验" class="headerlink" title="常规校验"></a>常规校验</h4><figure class="highlight python">