由于項目需要,需要在基于 asp.net mvc 的 Web 項目框架中做權限的控制,于是才有了這個權限控制組件,最初只是支持 netframework,后來 dotnetcore 2.0 發(fā)布了之后添加了對 asp.net core 的支持,在 dotnetcore 3.0 發(fā)布之后也增加了對 asp.net core 3.0 的支持(1.9.0及之后版本),目前對于 asp.net core 支持的更多一些,asp.net core 可以使用 TagHelper 來控制頁面上元素的權限訪問,也可以通過 Policy 來控制權限訪問,同時支持通過中間件也可以實現(xiàn)對靜態(tài)資源的訪問。
安裝 nuget 包 WeihanLi.AspNetMvc.AccessControlHelper
Copydotnet add package WeihanLi.AspNetMvc.AccessControlHelper
以下代碼定義了一個簡單的訪問策略,需要登錄且擁有 Admin 角色,可以根據(jù)自己需要調(diào)整優(yōu)化
Copypublic class AdminPermissionRequireStrategy : IResourceAccessStrategy
{
private readonly IHttpContextAccessor _accessor;
public AdminPermissionRequireStrategy(IHttpContextAccessor accessor)
{
_accessor=accessor;
}
public bool IsCanAccess(string accessKey)
{
var user=_accessor.HttpContext.User;
return user.Identity.IsAuthenticated && user.IsInRole("Admin");
}
public IActionResult DisallowedCommonResult=> new ContentResult
{
Content="No Permission",
ContentType="text/plain",
StatusCode=403
};
public IActionResult DisallowedAjaxResult=> new JsonResult(new JsonResultModel
{
ErrorMsg="No Permission",
Status=JsonResultStatus.NoPermission
});
}
定義頁面元素/控件訪問策略:
Copypublic class AdminOnlyControlAccessStrategy : IControlAccessStrategy
{
private readonly IHttpContextAccessor _accessor;
public AdminOnlyControlAccessStrategy(IHttpContextAccessor httpContextAccessor)=> _accessor=httpContextAccessor;
public bool IsControlCanAccess(string accessKey)
{
if ("Never".Equals(accessKey, System.StringComparison.OrdinalIgnoreCase))
{
return false;
}
var user=_accessor.HttpContext.User;
return user.Identity.IsAuthenticated && user.IsInRole("Admin");
}
}
在 Startup 里注冊服務:
Copyservices.AddAccessControlHelper()
.AddResourceAccessStrategy<AdminPermissionRequireStrategy>()
.AddControlAccessStrategy<AdminOnlyControlAccessStrategy>()
;
如果你只是 web api ,不涉及到頁面元素的權限控制可以只注冊 ResourceAccessStrategy
Copyservices.AddAccessControlHelper()
.AddResourceAccessStrategy<AdminPermissionRequireStrategy>();
默認訪問策略的生命周期是單例的,如果需要注冊為Scoped,可以指定默認的生命周期
Copyservices.AddAccessControlHelper()
.AddResourceAccessStrategy<AdminPermissionRequireStrategy>(ServiceLifetime.Scoped);
對于 asp.net core 應用推薦使用 Policy 來控制權限的訪問,可以在需要權限控制的 Action 或者 Controller 上設置 [Authorize("AccessControl")] 或者 [Authorize(AccessControlHelperConstants.PolicyName)]
Copy[Authorize(AccessControlHelperConstants.PolicyName)]
public class SystemSettingsController : AdminBaseController
{
// ...
}
Copy[Authorize(AccessControlHelperConstants.PolicyName)]
public ActionResult UserList()
{
return View();
}
在 Views 目錄下的 _ViewImports.cshtml 文件中導入 AccessControlHelper 的 TagHelper
Copy@using ActivityReservation
@using WeihanLi.AspNetMvc.AccessControlHelper
@using WeihanLi.AspNetMvc.MvcSimplePager
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, WeihanLi.AspNetMvc.AccessControlHelper
在需要權限控制的元素上增加 asp-access 的 attribute 就可以了,如果需要 access-key 通過 asp-access-key 來配置
Copy<ul class="list-group" asp-access asp-access-key="AdminOnly">
<li role="separator" class="list-unstyled">
<br />
</li>
<li class="list-group-item">@Html.ActionLink("用戶管理", "UserList", "Account")</li>
<li class="list-group-item">@Html.ActionLink("操作日志查看", "Index", "OperationLog")</li>
<li class="list-group-item">@Html.ActionLink("系統(tǒng)設置管理", "Index", "SystemSettings")</li>
<li class="list-group-item">
@Html.ActionLink("微信設置管理", "Index", new {
controller="Config",
area="Wechat"
})
</li>
</ul>
這樣就可以了,有權限訪問的時候才會正常渲染,沒有權限訪問的時候,這一段 ul 并不會渲染輸出,在客戶端瀏覽器查看源代碼也不會看到對應的代碼
FormMaking表單設計器是一款與行業(yè)無關的通用表單設計器前端解決方案,表單設計器包含設計器(MakingForm)和生成器(GenerateForm)兩個模塊,設計器主要負責表單的動態(tài)設計,可以通過拖拽的形式設計生成表單頁面,生成器則負責對表單頁面的渲染和數(shù)據(jù)的回寫。
如果你對FormMaking不太了解可能會把FormMaking當成是單純頁面設計器,只為了減少重復的表單,稍微有點經(jīng)驗的前端開發(fā),通過簡單的Copy Paste就能很快做出一個固定的表單頁面。而FormMaking做的是一整套與表單相關的前端方案,包含了表單的動態(tài)設計,表單渲染,數(shù)據(jù)回寫,數(shù)據(jù)校驗,事件支持,css自定義,動作面板等豐富的功能,而這些所有的功能只需要一個import 命令將MakingForm和GenerateForm引入到項目中,你的項目就可以擁有這些功能。
FormMaking基礎功能可以看之前都介紹。
本文我們將介紹如何使用 FormMaking 來引入自己的高級組件,并可以通過設計器進行配置,處理事件等操作
封裝分頁數(shù)據(jù)表格組件
我們將封裝 一個分頁數(shù)據(jù)表格組件,組件采用 ElementPlus TableV2
<template>
<div>
<div style="height: 400px">
<el-auto-resizer>
<template #default="{ height, width }">
<el-table-v2
:columns="columns"
:data="data"
:width="width"
:height="height"
fixed
/>
</template>
</el-auto-resizer>
</div>
<el-pagination
background
layout="prev, pager, next"
:total="1000"
v-model:current-page="currentPage"
@current-change="loadPageData"
/>
</div>
</template>
<script setup>
import { onMounted, ref, watch } from 'vue'
const props=defineProps({
modelValue: {
type: Array,
default: ()=> []
},
columns: {
type: Array,
default: ()=> []
}
})
const emit=defineEmits(['on-load'])
const data=ref(props.modelValue)
const currentPage=ref(1)
const loadPageData=(index)=> {
// 通過 emit,可以將事件拋出到設計器中進行配置
emit('on-load', index)
}
onMounted(()=> {
emit('on-load', currentPage.value)
})
watch(()=> props.modelValue, (val)=> {
data.value=val
})
</script>
引入到設計器
注冊組件
首先應該在自己項目中進行組件的注冊。
import CustomPaginationTable from 'PaginationTable.vue'
app.use(FormMakingV3, {
components: [{
name: 'custom-pagination-table',
component: CustomPaginationTable // 自定義組件
}]
})
也可以使用Vue.component 進行組件的注冊
app.component('custom-pagination-table', CustomPaginationTable)
設計器配置
<template>
<fm-making-form
:custom-fields="customFields"
>
</fm-making-form>
</template>
<script>
export default {
data() {
return {
customFields: [
{
name: '分頁數(shù)據(jù)列表',
el: 'custom-pagination-table',
options: {
defaultValue: [],
labelWidth: 0,
isLabelWidth: false,
hidden: false,
dataBind: true,
validator: '',
extendProps: {
columns: [] // 用于配置表格的列
}
},
events: {
onLoad: '' // 定義設計器可以配置的事件,處理組件 emit 的事件
}
}
]
}
}
}
</script>
然后在自定義字段中就展示出來了
設計器界面
配置組件表格
表格列配置
在字段屬性中對擴展屬性配置 進行設置
數(shù)據(jù)加載
數(shù)據(jù)加載的 on-load事件,我們在自定義組件中通過emit拋出到設計器中進行配置,在字段屬性-動作設置中添加onLoad事件配置如下:
效果展示
我們來查看下最后的效果
效果圖
更多關于FormMaking詳情可移步:
可視化低代碼表單設計器 - FormMaking
寶頁面比較復雜,含有各種請求參數(shù)和加密參數(shù),如果直接請求或者分析Ajax將會非常繁瑣。Selenium是一個自動化測試工具,可以驅(qū)動瀏覽器去完成各種工作,比如模擬點擊、輸入和下拉等多種功能,這樣我們只需關心操作,不需要關心后臺發(fā)生了怎么樣的請求下面對具體操作步驟進行詳述
實現(xiàn)流程
1.搜索關鍵字:利用Selenium驅(qū)動瀏覽器搜索關鍵字,得到查詢后的商品列表
2.分析頁碼并翻頁:得到商品頁碼數(shù),模擬翻頁,得到后續(xù)頁面的商品列表
3.分析提取商品內(nèi)容:利用PyQuery分析源碼,解析得到商品列表
4.存儲至MongoDB:將商品列表信息存儲到數(shù)據(jù)庫MongoDB
具體實現(xiàn)
1.首先需要聲明一個browser用來操作,我的是chrome。這里的wait是在后面的判斷元素是否出現(xiàn)時使用,第二個參數(shù)為等待最長時間,超過該值則拋出異常
#創(chuàng)建一個瀏覽器對象 browser=webdriver.Chrome() #十秒內(nèi)尋找元素失敗,將報出TimeoutException異常 wait=WebDriverWait(browser, 10)
2.聲明好之后就需要進行打開網(wǎng)頁、進行搜索的操作
def search(): """ 作用:通過關鍵字搜索得到搜索內(nèi)容第一頁產(chǎn)品信息,并返回搜索內(nèi)容共計頁碼 """ print('正在搜索') try: browser.get('https://www.taobao.com') print(browser.page_source) # 獲取input輸入框?qū)ο? input=wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, "#q")) )# # 獲取button提交對象 submit=wait.until( EC.element_to_be_clickable( (By.CSS_SELECTOR, "#J_TSearchForm > div.search-button > button") ) ) # KEYWORD是config.py文件中定義的搜索關鍵字,可修改 input.send_keys(KEYWORD) # 點擊button提交按鈕 submit.click() # 通過CSS_SELECTOR選擇器獲取total頁碼 total_page=wait.until( EC.presence_of_element_located( (By.CSS_SELECTOR, "#mainsrp-pager > div > div > div > div.total") ) ) # 解析頁面內(nèi)容,并將數(shù)據(jù)保存至MongoDB中 get_products() return total_page.text except TimeoutException: # 出現(xiàn)超時訪問就重新調(diào)用一次search return search()
3.第一個頁面操作之后,我們需要進行翻頁操作,如下:
def next_page(page_number): """ 作用:作人為翻頁功能,并得到該頁產(chǎn)品信息 page_number:循環(huán)的頁碼,將頁碼寫入input框中 """ print('正在翻頁', page_number) try: # 獲取input輸入框?qū)ο? input=wait.until( EC.presence_of_element_located( (By.CSS_SELECTOR, "#mainsrp-pager > div > div > div > div.form > input") ) ) # 獲取button提交對象 submit=wait.until( EC.element_to_be_clickable( (By.CSS_SELECTOR, "div > div > div > div.form > span.btn.J_Submit") ) ) # 將input框清空 input.clear() # 向input框中輸入頁碼 input.send_keys(page_number) # 點擊button提交按鈕 submit.click() wait.until( # 驗證輸入框內(nèi)的頁碼是否為當前高亮頁碼,如果是,則繼續(xù)執(zhí)行 # text參數(shù)是否在該span元素內(nèi),此處需要注意text的參數(shù)位置 EC.text_to_be_present_in_element( (By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > ul > li.item.active > span'), str(page_number) ) ) # 獲取產(chǎn)品信息并保存至MongoDB get_products() except TimeoutException: # 超時異常,則繼續(xù)解析該頁 next_page(page_number)
4.寫完搜索操作和翻頁操作后,我們需要完成對每個頁面的商品信息獲取功能
def get_products(): """ 作用:通過頁面源代碼,將產(chǎn)品信息保存至MongoDB中 """ # 通過CSS_SELECTOR選擇器,驗證頁面中是否存在產(chǎn)品對象 wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, "#mainsrp-itemlist .items .item")) ) # 拿到頁面源碼 html=browser.page_source # 通過pyquery找到產(chǎn)品對象 doc=pq(html) items=doc('#mainsrp-itemlist .items .item').items() # 遍歷每個產(chǎn)品對象,組建數(shù)據(jù)內(nèi)容 for item in items: product={ 'image': item.find('.pic .img').attr('src'), 'price': item.find('.price').text(), 'deal': item.find('.deal-cnt').text()[:-3], 'title': item.find('.title').text(), 'shop': item.find('.shop').text(), 'location': item.find('.location').text() } # print(product) # 將組建好的每個產(chǎn)品信息保存至MongoDB save_to_mongo(product)
5.獲取信息之后則需要對信息進行存儲,將數(shù)據(jù)庫關鍵參數(shù)定義在config.py文件中
#!/usr/bin/env python # coding=utf-8 # 本地的數(shù)據(jù)庫 MONGO_URL='localhost' # 數(shù)據(jù)庫的名稱 MONGO_DB='taobao' # 數(shù)據(jù)庫的表名 MONGO_TABLE='products'
6.將組建好的每個產(chǎn)品信息保存至MongoDB
# 創(chuàng)建一個MongoDB對象 client=pymongo.MongoClient(MONGO_URL) # 創(chuàng)建一個數(shù)據(jù)庫,注意是[]而不是() db=client[MONGO_DB] def save_to_mongo(result): """ 作用:將信息保存至MongoDB中 """ try: if db[MONGO_TABLE].insert(result): print('Successfully save to MongoDB', result) except Exception: print('Failed save to MongoDB', result)
7.爬取調(diào)度器
def TbMeishi_Spider(): """ 作用:淘寶美食爬取調(diào)度器 """ try: total_page=search() # 通過正則將search返回的total頁碼內(nèi)容解析成數(shù)字形式 pattern=re.compile('(\d+)') total_page=int(pattern.search(total_page).group(1)) # print(total_page) # 遍歷頁碼,傳入next_page函數(shù)中,做整站爬取 for i in range(2, total_page + 1): next_page(i) except Exception: print('有錯誤產(chǎn)生...') finally: browser.close()
8.代碼完成到這里已經(jīng)可以結束了,但是使用Selenium模擬會發(fā)現(xiàn)每次運行程序都會打開一個瀏覽器,然后自動操作,可以最直接地觀察到代碼的運行,但是假如不想彈出瀏覽器的話,可以使用PhantomJS來實現(xiàn)一個無界面的webkit瀏覽器。雖然沒有界面,但dom渲染、js運行、網(wǎng)絡訪問、canvas/svg繪制等功能都很完備,在頁面抓取、頁面輸出、自動化測試等方面有廣泛的應用。下載PhantomJS地址:http://phantomjs.org/download.html,下載完成后解壓縮,將PhantomJS添加到環(huán)境變量中即可
在config.py文件中加上
# 關閉load-images的功能,加快運行速度,默認開啟,開啟disk-cache緩存,默認關閉 SERVICE_ARGS=['--load-images=false', '--disk-cache=true'] # 無界面瀏覽器,將browser改為 browser=webdriver.PhantomJS(service_args=SERVICE_ARGS) # 設置PlantomJS的窗口大小,可能會影響內(nèi)容下載 browser.set_window_size(1400, 900)
總結思考
1.目標網(wǎng)址不匹配
一開始使用顯示等待訪問,通過CSS_SELECTOR的id選擇器找到網(wǎng)頁元素,可最后程序都執(zhí)行異常,每次都是TimeoutExceptions的異常錯誤。經(jīng)過反復排查,才發(fā)現(xiàn)淘寶網(wǎng)有很多個站點
我在程序中使用的是:http://www.taobao.com
但是在進行網(wǎng)頁分析的時候隨意用百度搜了一個淘寶站點,它的站點是:https://uland.taobao.com/sem/tbsearch
兩個站點地址不一樣,網(wǎng)頁元素也不一樣,自己竟然會犯這種錯誤,應牢記,正確的選擇元素應該是下面這幾行
from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC input=WebDriverWait(browser, 10).until( EC.presence_of_element_located((By.ID, 'q')) ) button=WebDriverWait(browser, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '.btn-search')) )
2.text參數(shù)是否在該span元素內(nèi),此處需要注意text的參數(shù)位置
# 驗證text參數(shù)是否與選擇器選擇的元素文本內(nèi)容一致 text_to_be_present_in_element(locator, text_)
3.完整源碼地址:https://github.com/XiaoFei-97/TbMeishi_Spider\
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。