Appium+python自動化(四十一)-Appium自動化測試框架綜合實踐 – 即將落下帷幕(超詳解)

1.簡介

  今天我們緊接着上一篇繼續分享Appium自動化測試框架綜合實踐 – 代碼實現。到今天為止,大功即將告成;框架所需要的代碼實現都基本完成。

2.data數據封裝

2.1使用背景

在實際項目過程中,我們的數據可能是存儲在一個數據文件中,如txt,excel、csv文件類型。我們可以封裝一些方法來讀取文件中的數據來實現數據驅動。

2.2案例

將測試賬號存儲在account.csv文件,內容如下:

account.csv

hg2018

hg2018

hg2019

zxw2019

666

222

參考代碼

2.3enumerate()簡介

enumerate()是python的內置函數

  • enumerate在字典上是枚舉、列舉的意思
  • 對於一個可迭代的(iterable)/可遍歷的對象(如列表、字符串),enumerate將其組成一個索引序列,利用它可以同時獲得索引和值
  • enumerate多用於在for循環中得到計數。

2.4enumerate()使用

如果對一個列表,既要遍歷索引又要遍曆元素時,首先可以這樣寫:

參考代碼
list = ["這", "是", "一個", "測試","數據"]

for i in range(len(list)):

    print(i,list[i])

上述方法有些累贅,利用enumerate()會更加直接和優美:

參考代碼
list1 = ["這", "是", "一個", "測試","數據"]

for index, item in enumerate(list1):

        print(index,item)

3.數據讀取方法封裝

  數據讀取方法也屬於公共方法,這裏我們首先實現一下,然後將其封裝到裡邊即可。

3.1數據讀取方法實現的參考代碼

import csv


     def get_csv_data(csv_file,line):

        with open(csv_file, 'r', encoding='utf-8-sig') as file:

            reader=csv.reader(file)

            for index, row in enumerate(reader,1):

                if index == line:

                    return row

 

    csv_file='../data/account.csv'

    data=get_csv_data(csv_file,3)

    print(data)

3.2封裝

將其封裝在公共方法中,在其他地方用到的時候,直接導入調用即可。

4.utf-8與utf-8-sig兩種編碼格式的區別

UTF-8以字節為編碼單元,它的字節順序在所有系統中都是一樣的,沒有字節序的問題,也因此它實際上並不需要BOM(“ByteOrder Mark”)。但是UTF-8 with BOM即utf-8-sig需要提供BOM。

5.config文件配置

各種配置文件都放在這個目錄下。

5.1日誌文件配置 

主要是一些日誌信息的配置。

log.config

 參考代碼
[loggers]
keys=root,infoLogger

[logger_root]
level=DEBUG
handlers=consoleHandler,fileHandler

[logger_infoLogger]
handlers=consoleHandler,fileHandler
qualname=infoLogger
propagate=0

[handlers]
keys=consoleHandler,fileHandler

[handler_consoleHandler]
class=StreamHandler
level=INFO
formatter=form02
args=(sys.stdout,)

[handler_fileHandler]
class=FileHandler
level=INFO
formatter=form01
args=('../logs/runlog.log', 'a')

[formatters]
keys=form01,form02

[formatter_form01]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

[formatter_form02]
format=%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s

6.測試用例封裝

這裏宏哥舉例給小夥伴們演示:封裝註冊和登錄兩個測試用例。

6.1測試用例執行開始結束操作封裝

測試用例執行開始和結束的封裝,其他模塊用到直接導入,調用即可。

myunit.py

參考代碼
# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.註釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2019-11-20
@author: 北京-宏哥   QQ交流群:707699217
Project:Appium自動化測試框架綜合實踐 - 代碼實現
'''
# 3.導入模塊
import unittest
from kyb_testProject.common.desired_caps import appium_desired
import logging
from time import sleep

class StartEnd(unittest.TestCase):
    def setUp(self):
        logging.info('=====setUp====')
        self.driver=appium_desired()

    def tearDown(self):
        logging.info('====tearDown====')
        sleep(5)
        self.driver.close_app()

6.2註冊用例

開始註冊用例代碼邏輯的實現。

test_register.py

參考代碼
# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.註釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2019-11-20
@author: 北京-宏哥   QQ交流群:707699217
Project:Appium自動化測試框架綜合實踐 - 代碼實現
'''
# 3.導入模塊
from kyb_testProject.common.myunit import StartEnd
from kyb_testProject.businessView.registerView import RegisterView
import logging,random,unittest

class RegisterTest(StartEnd):
    def test_user_register(self):
        logging.info('======test_user_register======')
        r=RegisterView(self.driver)

        username = 'bjhg2019' + 'fly' + str(random.randint(1000, 9000))
        password = 'bjhg2020' + str(random.randint(1000, 9000))
        email = 'bjhg' + str(random.randint(1000, 9000)) + '@163.com'

        self.assertTrue(r.register_action(username,password,email))

if __name__ == '__main__':
    unittest.main()

6.3登錄用例

開始登錄用例代碼邏輯的實現。

test_login.py

參考代碼
# coding=utf-8
# 1.先設置編碼,utf-8可支持中英文,如上,一般放在第一行

# 2.註釋:包括記錄創建時間,創建人,項目名稱。
'''
Created on 2019-11-13
@author: 北京-宏哥   QQ交流群:707699217
Project:Appium自動化測試框架綜合實踐 - 代碼實現
'''
# 3.導入模塊
from kyb_testProject.common.myunit import StartEnd
from kyb_testProject.businessView.loginView import LoginView
import unittest
import logging

class TestLogin(StartEnd):
    csv_file='../data/account.csv'

    @unittest.skip('test_login_zxw2018')
    def test_login_zxw2018(self):
        logging.info('======test_login_zxw2018=====')
        l=LoginView(self.driver)
        data=l.get_csv_data(self.csv_file,2)

        l.login_action(data[0],data[1])
        self.assertTrue(l.check_loginStatus())

    # @unittest.skip('skip test_login_zxw2017')
    def test_login_zxw2017(self):
        logging.info('======test_login_zxw2017=====')
        l=LoginView(self.driver)
        data = l.get_csv_data(self.csv_file, 1)

        l.login_action(data[0], data[1])
        self.assertTrue(l.check_loginStatus())

    @unittest.skip('test_login_error')
    def test_login_error(self):
        logging.info('======test_login_error=====')
        l = LoginView(self.driver)
        data = l.get_csv_data(self.csv_file, 3)

        l.login_action(data[0], data[1])
        self.assertTrue(l.check_loginStatus(),msg='login fail!')

if __name__ == '__main__':
    unittest.main()

7.小結

到此,Appium自動化測試框架就差下一篇就全部完成了,聰明的你都懂了嗎???嘿嘿!慢慢地來吧。

下節預告

下一篇,講解執行測試用例,生成測試報告,以及自動化平台,請關注宏哥,敬請期待!!!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※台灣海運大陸貨務運送流程

※兩岸物流進出口一站式服務

買電動車送補貼金?傳德國財政部提12億歐元獎勵方案

德國電動車銷售積弱不振,據傳財政部已提出總額12億歐元(相當於13.5億美元)的補貼法案,希望為電動車市重新注入活力,而所需費用將由政府及車商聯手買單。

華爾街日報25日引述未具名消息人士報導,德國政府高層和汽車產業代表即將在26日與德國總理梅克爾會面,討論刺激電動車銷售量的獎勵措施,另外也會考慮直接由政府出資、在全國建設充電站。

根據財政部提出的建議,在2016年至2018年夏季期間,購買電動車的民眾可獲得政府與車商補貼5,000歐元,購買油電混合動力車的民眾則可獲得3,000歐元的補助,在此之後補貼金額會分別下降至3,000歐元(電動車)、2,000歐元(油電混合動力車)。

消息顯示,售價高於60,000歐元的電動車不在補助範圍之內,以免獎勵措施淪為補貼富人買車的工具。另外,這個方案將遵循先買先補貼的原則,12億歐元的補助款用罄之後將不再補款。財政部也提議提供3億歐元的額外補貼,為電動車設立充電站。

Thomson Reuters報導,梅克爾甫於4月12日呼籲汽車製造商開出願望清單。梅克爾表示,官方將盡快清除法律障礙、讓他們可以在德國測試無人駕駛車。她透露,德國執政聯盟將在5月底開會討論自駕車法令規範。德國財長Wolfgang Schaeuble上個月說,官方將設法協助電動車開發、但可能無法讓所有車商感到滿意。車界領袖呼籲,德國若想維持全球領導地位、政府就得想辦法提振電動車買氣。

蘋果在德國的電動車開發案似乎正進行的如火如荼。AppleInsider 4月18日引述法蘭克福廣訊報(Frankfurter Allgemeine Zeitung)報導,蘋果已在柏林設立秘密開發實驗室,目前在當地擁有15-20名工程、軟體、硬體、行銷背景的德國汽車業頂尖人才。報導指出,蘋果進軍汽車業的第一款產品將是電動車、但初期不具備自駕功能。

(本文內容由授權提供)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※台灣海運大陸貨務運送流程

※兩岸物流進出口一站式服務

ASP.NET Core 3.0 gRPC 身份認證和授權

一.開頭聊騷

本文算是對於 ASP.NET Core 3.0 gRPC 研究性學習的最後一篇了,以後在實際使用中,可能會發一些經驗之文。本文主要講 ASP.NET Core 本身的認證授權和gRPC接入,認證方式採用目前主流的 JWT 結合 IdentityServer4。

二.服務端配置

我們首先需要在服務端配置認證和授權。gRPC基於此文的Demo來開始: ,IdentityServer 基於此文Demo: 。

配置

1.首先啟動 IdentityServer4 地址為:

2.為gRPC項目安裝Jwt組件:Install-Package Microsoft.AspNetCore.Authentication.JwtBearer -Version 3.0.0

3.為gRPC項目配置認證和授權服務

在 Startup 類的 ConfigureServices 方法中,配置如下代碼

services.AddAuthorization(options =>
{
    options.AddPolicy(JwtBearerDefaults.AuthenticationScheme, policy =>
    {
        policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);
        policy.RequireClaim("sub");
    });
});
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;
        options.Audience = "grpc1";
    });

4.啟用認證授權中間件

在 Startup 類的 Configure 方法中,配置如下代碼

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

請務必注意中間件順序

5.為gRPC服務啟用授權

我們在 LuCatService 的 SuckingCat 方法上,加上 [Authorize]特性,就和在MVC中一樣。

測試

運行客戶端調用服務端來進行測試,發現服務端返回了授權失敗,客戶端同樣獲得了錯誤。這證明我們的服務端配置是沒有問題的

三.客戶端配置

配置

客戶端首先需要從 IdentityServer 申請 Token,然後在調用 gRPC 服務時傳遞過去,這和 HTTP Api 調用一樣。

1.客戶端項目安裝組件 IdentityModel 獲得基於 HttpClient 的和 IdentityServer 的交互的封裝。

2.獲取Token

// discover endpoints from metadata
var client = new HttpClient();

var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
if (disco.IsError)
{
    Console.WriteLine(disco.Error);
    return;
}

// request token
var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
    Address = disco.TokenEndpoint,
    ClientId = "ro.client",
    ClientSecret = "secret",

    UserName = "alice",
    Password = "password",
    Scope = "grpc1"
});

if (tokenResponse.IsError)
{
    Console.WriteLine(tokenResponse.Error);
    return;
}

Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");

3.為 gRPC 客戶端請求設置 Token

和 HTTP Api 調用一樣,gRPC也是放在頭部的

var headers = new Metadata {{"Authorization", $"Bearer {tokenResponse.Json["access_token"]}"}};

var catClient = new LuCat.LuCatClient(channel);
var catReply = await catClient.SuckingCatAsync(new Empty(), headers);

主要就是在調用 SuckingCatAsync方法時,傳入了header。

測試

可以看到成功的進行了調用。

四.結束

本文所用代碼地址:

gRPC in Asp.Net Core :

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※台灣海運大陸貨務運送流程

※兩岸物流進出口一站式服務

Flutter 構建的 Mac 桌面應用上無法發出網絡?

在上一篇文章中我們分享了,如何開發桌面應用。在本章文章中,來解決一下為何在 Mac 中無法發出網絡情況的原因。

起因

事情​起因是這樣的:我總覺得寫一個 Demo 不足以體現我們開發同學的能力。直到最近,我發現了一個可以改善的小點,可幫助我們的測試同學提高測試效率。

大體情況就是在某天晚上,客戶端的一個小問題,需要進行驗證。但是呢,測試同學要做回歸測試,把所有的數據都重新創建了一遍。但是此過程用了很久,理論來說,應該十來分鐘搞定的事情。我就靜靜的在她身旁看着,內心捉急萬分,畢竟我想早點回家(睡覺,打遊戲,看電視)。最後晚上 11 點多打車回家,一路上我都在想,這個問題應該很快就能驗證好吧,為何要折騰這麼就呢?也許是流程真這麼長吧,那我們無法改變流程,那就做出能提升效率的工具吧。

次日,我把我的想法告訴了她,但是她說我們有自己的網頁工具,可以解決啊,只是現在沒時間創建腳本。隨後我擱置這個想法,但是這種提升效率的想法一直縈繞在我的心頭。

再然後,某天突然意識到,同樣是工具,大家都會使用順手好用的,幹嘛使用哪種傳統老土的工具呢?基於這一點,我開啟了新的探索。

我不僅要做客戶端的,還要做網頁的,更要做桌面應用(測試同學最關注)。我要體驗更好,不僅方便我自己測試,還要給所有的測試同學提供便利,減少加班是我的最終目的,哈哈哈。

想法有了,那就開始實施吧。之前遇到一個問題,一直沒解決,但是此刻必須解決了。那就是之前 Flutter 構建的 Mac 應用后,無法發出網絡請求。

在桌面應用無法發出請求?

添加 Dio 以後,會遇到這種情況:



提示,當前系統不支持該操作。

為何會有如此問題,在 Dio 上支持 Other,也就是支持 Mac ,Windows, Linux, 為何我們直接運行有錯誤呢?



具體原因

實際是因為 Mac 應用有沙箱限制,需要在對應文件中開啟即可。

<key>com.apple.security.network.client</key>
<true/>



添加以後,效果如下:


具體參考: https://github.com/google/flutter-desktop-embedding/issues/546

代碼示例:

import 'dart:io';

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

// Sets a platform override for desktop to avoid exceptions. See
// https://flutter.dev/desktop#target-platform-override for more info.
void _enablePlatformOverrideForDesktop() {
  if (!kIsWeb && (Platform.isMacOS || Platform.isWindows || Platform.isLinux)) {
    debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
  }
}

void main() {
  _enablePlatformOverrideForDesktop();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  String requestData = "";

  void _incrementCounter() {
    setState(() {
      _counter++;
      getHttp();
    });
  }

  void getHttp() async {
    try {
      Response response = await Dio().get("http://www.gdky005.com");
      requestData = response.toString();
      print(requestData);
    } catch (e) {
      print(e);
      requestData = e.toString();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
            Container(
              height: 400,
              child: ListView(
                children: <Widget>[
                  Text(
                    '$requestData',
                  )
                ],
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

※不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置,台北網頁設計,各種案例分享

※小三通物流營運型態?

※快速運回,大陸空運推薦?

奧地利一名果農因違法噴灑殺蟲劑,隸屬鄰近2個養蜂人超過50個蜂群因此死亡

奧地利一名果農因違法噴灑殺蟲劑,隸屬鄰近2個養蜂人超過50個蜂群因此死亡。26日奧地利克拉根福法院(Klagenfurt)以「蓄意危害環境」,判處果農1年有期徒刑,至少需服刑4個月才可假釋,以及賠償超過2萬歐元(2萬3500美元)。

這名47歲果農針對他位於奧地利卡林西亞省(Carinthia)拉萬特地區(Lavanttal)的果樹噴灑藥效強大的殺蟲劑陶斯松(chlorpyrifos),當時果樹的花仍會吸引蜜蜂前去。法院指出,以他的經驗和訓練他人的角色,足以證明他知道自身行為會帶來何種後果。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※台灣海運大陸貨務運送流程

※兩岸物流進出口一站式服務

球系統科學家Surendra Adhikari表示,研究團隊設計出一套地球自旋物理學電腦模型

球系統科學家Surendra Adhikari表示,研究團隊設計出一套地球自旋物理學電腦模型,加入20世紀大陸冰川(冰蓋)與海水平衡變化的所有數據,終於找出影響地軸飄移至關重要的第2個原因:格陵蘭島冰蓋大量融化。

目前地球上僅存的大陸冰川只剩下南極洲與格陵蘭島,南極冰蓋覆蓋範圍約1,400萬平方公里,隨著20世紀氣溫上升加劇,格陵蘭冰蓋至今已有約7,500億噸的冰融化進入海洋,這導致地球質量分佈發生變化,進而使地軸飄移。一旦南極冰蓋融化,全球海平面將上升61.1公尺;格陵蘭冰蓋則佔據格陵蘭島82%陸地面積,如果格陵蘭冰蓋融化,將導致海平面上升7.2公尺。這項研究明確顯示了人類活動正在改變地球的基本性質及運作方式。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

※不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置,台北網頁設計,各種案例分享

※小三通物流營運型態?

※快速運回,大陸空運推薦?

特斯拉 Model 3 大賣,電動車朝向榮景仍逢瓶頸?

電動車大廠特斯拉(Tesla)新款房車 Model 3 的預購量超出預期,市場上開始有許多分析師發言,有的說電動車即將取代傳統的汽車;有人甚至認為應該忘掉正面臨創新瓶頸的 Apple 執行長 Tim Cook,將目光移向 Tesla 創辦人 Elon Musk,稱他是偉大夢想及實踐家,其能力傲視全球的企業家,然而事實真是如此嗎?   電動車大廠特斯拉(Tesla)新款房車 Model 3 於 2016 年 3 月展開全球預售,售價 3.5 萬美元(約新台幣 110 萬元),買主只需要預付 1,000 美元(約新台幣 3 萬元)就可以訂車,此舉立刻引起市場震撼,吸引大批車迷排隊預購,光是在一天內便湧入 11 萬筆訂單,首周預購就超過 32 萬筆。  
暗藏玄機的預購書   仔細看看 Tesla 的預購合約書(其實這算不上合約書只是預購同意書而已),就可以看出到底為什麼大家會一窩蜂的搶訂。Tesla 的預購條件很簡單,任何人想要訂購 Model 3,只需要支付 1,000 美元的訂金,而且任何時間如果後悔都可以全部退還,換句話說,對消費者而言沒有任何風險;同樣的,Tesla 在文件裡也沒有寫明交車時間,檯面上說 2017 年底是「預定」交車時間,但如果交不出來或延後到 2018 甚至 2020 年,也沒有任何罰則。   放眼望去,哪家車廠會訂出這種購車合約?這樣的操作 Tesla 的目的為何?   Tesla 成立於 2003 年,到現在已經超過 12 年了,它是第一家把電動車成功推到全球市場的公司,讓電動車與高級車畫上等號,吸引到高收入客戶。Tesla 帶給人們對於未來汽車的夢想,進而推升了它的高股價,甚至比大部分賺錢的公司還要高出十幾、二十倍。美國汽車在 2015 年銷售創下歷來最佳紀錄,但福特(Ford)和通用(GM)等汽車業者股票卻大跌,股價本益比僅 5~7 倍,而 Tesla 卻能在持續虧損下,過去 3 年漲了將近 400%,比標普 500 指數與其他傳統車商要好得多。   2013 年 Tesla 進軍中國,希望拿下繼美國之後第二個龐大的電動車市場,甚至 Musk 曾揚言「中國市場很快就超越美國成為 Tesla 電動車的第一大市場」。但是 Tesla 一直無法突破瓶頸,2015 第三季結束的交車總量只有 3,025 輛,不到全球交付量 50,580 輛的十分之一。目前看起來 Tesla 要搶下中國市場,還有一段很漫長的路要走,除了中國政策的問題,無補貼、高關稅、被排除在國家標準之外,各國車廠也紛紛摩拳擦掌,準備搶食中國電動車的市場大餅。   長期的虧損也讓華爾街的分析師對 Musk 的耐心慢慢消磨殆盡,除了進軍中國沒有交出漂亮的成績單,Tesla 還是延遲交貨的慣犯,從首款電動車 Roadster、次代 Model S 到電動休旅車 Model X,從未準時交貨,Model X 甚至還延遲超過一年半。  
先求資金到位以利發展   雖然 Model 3 被定位成平價車款,它的設計沒有其他現有車款 Model S 與 Model X 複雜,但是能不能準時交貨並不是 Tesla 說了算,還是得仰賴後端生產零件的供應商是否如期交貨。Tesla 也預見可能發生的狀況,已先幫自己找替代方案,在加州費利蒙〈Fremont〉的生產工廠自行製造所需零件。   另外電池材料來源與價格,也是必須考量的因素。電動車使用鋰電池做為動力來源,除了電池本身必須非常嚴謹安全,當需求量大增時也會推升鋰材料的價格,因此未必能夠因為數量大幅成長而使價格大幅下降。2015 年第四季開始,就發生鋰電池正極材料所需的碳酸鋰缺貨,導致鋰電池廠商無法擴產,碳酸鋰每噸的市場價格也從 2013 年 9 月的 5 萬人民幣上漲到 12 萬人民幣。Model 3 都還沒做出來碳酸鋰價格就開始上漲了!   大量的電動車充電需求也代表電力基礎建設必須更新,這也不是短時間內可以做到的事,而 Tesla 垂涎欲滴的中國市場,雖然計劃擴建充電站,卻只支援中國國內的電動車品牌。超級充電站不相容、充電難的問題將進一步加劇 Tesla 的困境。   Tesla 可以在一周內拿到超過 30 萬的消費者預購同意書,並不代表真的能做到這麼多生意,連 Musk 都對 2017 年是否能順利上市不太有信心,他在發表會上說「Model 3 不太可能如期交車」。而當 2017 年時間一到,如果交不出令人滿意的 Model 3,或是首購的客戶有了不好的評價,到時候退訂的浪潮也會很兇猛的。   因此 Tesla「不訂出逾期的賠償條款」,對消費者和他們而言可謂雙贏策略。對消費者來說沒有任何風險,還可以過過 Tesla 車主的癮,於是消費者瘋狂訂購;對 Tesla 來說,可以向華爾街的分析師與市場上的投資人證明 Tesla 的潛在客戶非常龐大,有利於向資本市場募資吸金,等於是先拿到了訂單,再回頭過來向市場募資,一箭雙鵰。  
交車不順恐不利後續發展   而 2017 年是否真的能交車呢?是否真的能賣到 3.5 萬美元這麼低的價格?那是以後的事了,只要資金到位,未來要怎麼做都好說;缺少了資金和市場信心,別談以後了,Tesla 可能就要面臨後勢不振的壓力了。   Musk 在資本市場上為投資人,也為新創公司建立一個遠大的夢想並沒有什麼不對,而且手法高明了許多,但 Musk 真的能把 Cook 比下嗎?蘋果一直以來都是腳踏實地在創新,東西真的做得好賣得好,而賺大錢富可敵國才讓股票大漲的,當然做得再好的產品也會遭遇瓶頸,但是這畢竟靠的是腳踏實地的「本益比」,而不是虛幻不實的「本夢比」。   在商場上投機的確是會成功的,有時候成功的機率還不低,但是如果我們過度推崇投機,把投機說成是偉大夢想及實踐能力傲視全球,那對腳踏實地在創新的 Steven Jobs 和 Cook 也太不公平了!   (首圖來源: CC BY 2.0)   (本文授權轉載自《》─〈〉)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※台灣海運大陸貨務運送流程

※兩岸物流進出口一站式服務

Tesla年產50萬電動車牛皮吹太大,供應商坦承沒把握

特斯拉(Tesla)計畫在2018年大規模量產平價電動車Model 3,時程較原先計畫提早兩年,雖然展現特斯拉執行長穆斯克(Elon Musk)強大企圖心,但也飽受專家質疑,甚至特斯拉旗下供應商對於是否能達成目標也沒把握。   Model 3已開放預定,特斯拉目前累計收到37.3萬輛的訂單、預計2017年底開始交車。但外界所不知道的是,供應商是在三個月之前才被告知增產計畫提前,相關準備作業是否先完成令人質疑。據路透社引述知情人事消息報導指出,特斯拉最新計畫把2017年Model 3產出規模提高兩倍至10萬輛,2018年更將達成40萬輛的產出水平。   穆斯克4月28日曾在財報法說會上對分析師誇下海口,2018年整體產出規模(含Model S、Model X、Model 3)將來到50萬輛,是去年的十倍,也較預設時程快了兩年。不過Model 3設計目前還要等到6月才定案,距初次量產僅剩13個月。   電動車零組件何其多,且缺一不可,特斯拉如何在短時間內搞定供應商、備齊所有零組件,雖然並非完全不可能,但挑戰難度絕對相當高,這也考驗穆斯克的管理協調能力。值得一提的是,北美目前僅有少數汽車產線有年產50萬輛的能力,且背後都有幾十年經驗的車廠在運籌帷幄。   (本文內容由授權使用;首圖來源: CC BY 2.0)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※想知道網站建置、網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計及後台網頁設計

※不管是台北網頁設計公司、台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置,台北網頁設計,各種案例分享

※小三通物流營運型態?

※快速運回,大陸空運推薦?

除了獲取 MAC 地址還能幹啥

        以前寫過一篇《》的文章,文章的地址是:https://www.cnblogs.com/tosser/p/9022187.html,我當時使用 OCX 來實現,可是 OCX 只支持 IE 瀏覽器,後來在往上找了一個 Chrome 的擴展,也解決了該問題。不過,總覺得無論使用 OCX 還是使用 Chrome 的擴展,都是瀏覽器相關的,並不通用。後來,使用 Socket 寫了一個簡單的 DEMO,用來模擬 HTTP 服務器,然後使用 Ajax 進行通信,問題解決了。也不再是瀏覽器相關了。

 

Web 頁面獲取 MAC 地址的設計思路

        Web 頁面獲取 MAC 地址的設計思路是比較簡單的,只需要在本地模擬一個 HTTP 服務器,然後讓 Web 頁面通過 Ajax 來請求 HTTP 服務器,HTTP 服務器直接返回本機的 MAC 地址就可以了。

        具體流程如下圖:

 

        流程圖非常的清楚,主要就是 HostServer 和 Ajax 的通信,這樣就可以得到 MAC 地址,然後通過 DOM 操作,即可把 MAC 地址寫入到 input 框中。這樣,就可以和用戶名、密碼一起提交給服務器進行驗證了。

 

 

 

除了獲取 MAC 地址還能幹啥

 

       之前做過一個物流提貨的項目,涉及到一些硬件設備,包括:小票打印機、刷卡器(身份證、銀聯卡)、進幣器、密碼数字鍵盤等。這些設備、電腦主機和显示器在一個類似 ATM 機那樣的機櫃中(其實就是 ATM 機的設備,本身這套東西就是銀行提供的)。

 

       操作這些硬件的接口廠家提供了一個 OCX,而整個項目是 B/S 架構的。那麼,在客戶端想要操作這些硬件,就要調用 OCX,而 OCX 只能在 IE 瀏覽器下使用(Chrome、FireFox 是不支持 OCX 的)。眾所周知,IE 對 Web 並不友好,但是如何又能在不使用 IE 的情況下,又去調用 OCX 來完成操作硬件的功能呢?那麼就是我們上面的方法了。

 

       簡單的描述一下,就不貼圖了。

 

       在終端上放一個 HostServer 用來接受頁面中 Ajax 的請求,並根據請求去調用 OCX 中相應的功能,把 OCX 的返回信息,再以 Json 的格式返回給 Ajax 即可。

 

       這樣,把 瀏覽器 和 OCX 文件進行了分離,中間加入了一個 HostServer,頁面 和 OCX 的通信通過了 HostServer,那麼以後如果接口是 DLL 文件,也可以通過 HostServer 來進行完成,當然,還可以完成更多的功能。

 

 

 

總結

        其實整個獲取 MAC 地址的功能,對於登錄頁面而言是一個服務端,它在本地是一個可執行的程序,那麼它和普通的 EXE 文件是沒有區別的,那麼它能完成的功能其實遠遠不是獲取一個 MAC 地址的功能,對於上面的例子來說,把服務的提供者和使用者進行了分離,而且針對於本機的擴展也十分的方便了。當然,如果你願意的話,可以讓 HostServer 充當客戶端直接和後端的服務器進行通信而不通過瀏覽器,這樣是不是還能做一些讓用戶沒有感知的事情?

 

 

我的微信公眾號:“碼農UP2U”

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※台灣海運大陸貨務運送流程

※兩岸物流進出口一站式服務

asp.net core 自定義 Policy 替換 AllowAnonymous 的行為

asp.net core 自定義 Policy 替換 AllowAnonymous 的行為

Intro

最近對我們的服務進行了改造,原本內部服務在內部可以匿名調用,現在增加了限制,通過 identity server 來管理 api 和 client,網關和需要訪問api的客戶端或api服務相互調用通過 client_credencial 的方式來調用,這樣一來我們可以清晰知道哪些 api 服務會被哪些 api/client 所調用,而且安全性來說更好。
為了保持後端服務的代碼更好的兼容性,希望能夠實現相同的代碼通過在 Startup 里不同的配置實現不同的 Authorization 邏輯,原來我們的服務的 Authorize 都是以 Authorize("policyName") 的形式來寫的,這樣一來我們只需要修改這個 Policy 的授權配置就可以了。對於 AllowAnonymous 就希望可以通過一種類似的方式來實現,通過自定義一個 Policy 來實現自己的邏輯

實現方式

將 action 上的 AllowAnonymous 替換為 Authorize("policyName"),在沒有設置 Authorize 的 controller 上增加 Authorize("policyName")

public class AllowAnonymousPolicyTransformer : IApplicationModelConvention
{
    private readonly string _policyName;

    public AllowAnonymousPolicyTransformer() : this("anonymous")
    {
    }

    public AllowAnonymousPolicyTransformer(string policyName) => _policyName = policyName;

    public void Apply(ApplicationModel application)
    {
        foreach (var controllerModel in application.Controllers)
        {
            if (controllerModel.Filters.Any(_ => _.GetType() == typeof(AuthorizeFilter)))
            {
                foreach (var actionModel in controllerModel.Actions)
                {
                    if (actionModel.Filters.Any(_ => _.GetType() == typeof(AllowAnonymousFilter)))
                    {
                        var allowAnonymousFilter = actionModel.Filters.First(_ => _.GetType() == typeof(AllowAnonymousFilter));
                        actionModel.Filters.Remove(allowAnonymousFilter);
                        actionModel.Filters.Add(new AuthorizeFilter(_policyName));
                    }
                }
            }
            else
            {
                if (controllerModel.Filters.Any(_ => _.GetType() == typeof(AllowAnonymousFilter)))
                {
                    var allowAnonymousFilter = controllerModel.Filters.First(_ => _.GetType() == typeof(AllowAnonymousFilter));
                    controllerModel.Filters.Remove(allowAnonymousFilter);
                }
                controllerModel.Filters.Add(new AuthorizeFilter(_policyName));
            }
        }
    }
}

public static class MvcBuilderExtensions
{
    public static IMvcBuilder AddAnonymousPolicyTransformer(this IMvcBuilder builder)
    {
        builder.Services.Configure<MvcOptions>(options =>
        {
            options.Conventions.Insert(0, new AllowAnonymousPolicyTransformer());
        });
        return builder;
    }

    public static IMvcBuilder AddAnonymousPolicyTransformer(this IMvcBuilder builder, string policyName)
    {
        builder.Services.Configure<MvcOptions>(options =>
        {
            options.Conventions.Insert(0, new AllowAnonymousPolicyTransformer(policyName));
        });
        return builder;
    }
}

controller 中的代碼:

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly ILogger _logger;

    public ValuesController(ILogger<ValuesController> logger)
    {
        _logger = logger;
    }

    // GET api/values
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        var msg = $"IsAuthenticated: {User.Identity.IsAuthenticated} ,UserName: {User.Identity.Name}";
        _logger.LogInformation(msg);
        return new string[] { msg };
    }

    // GET api/values/5
    [Authorize]
    [HttpGet("{id:int}")]
    public ActionResult<string> Get(int id)
    {
        return "value";
    }
    // ...
}

Startup 中 ConfigureServices 配置:

var anonymousPolicyName = "anonymous";

services.AddAuthorization(options =>
{
    options.AddPolicy(anonymousPolicyName, builder => builder.RequireAssertion(context => context.User.Identity.IsAuthenticated));

    options.DefaultPolicy = new AuthorizationPolicyBuilder(HeaderAuthenticationDefaults.AuthenticationSchema)
        .RequireAuthenticatedUser()
        .RequireAssertion(context => context.User.GetUserId<int>() > 0)
        .Build();
});

services.AddMvc(options =>
    {
        options.Conventions.Add(new ApiControllerVersionConvention());
    })
    .AddAnonymousPolicyTransformer(anonymousPolicyName)
    ;

實現效果

訪問原來的匿名接口

userId 為0訪問原來的匿名接口

userId 大於0訪問原來的匿名接口

userId 為0訪問需要登錄的接口

userId 大於0訪問需要登錄的接口

More

注:按照上面的做法已經可以做到自定義 policy 代替 AllowAnonymous 的行為,但是原來返回的401,現在可能返回到就是 403 了

Reference

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計、台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

※台灣海運大陸貨務運送流程

※兩岸物流進出口一站式服務