[UiPath] 認証ありプロキシの配下にあるクライアントから、URIを指定して直接ダウンロードさせる！

この記事は、[UiPath (produced with UiPath Friends) Advent Calendar 2022](https://qiita.com/advent-calendar/2022/uipath) の9日目の投稿です。
8日目は下記の投稿でした。

- @Davoi さん: [【UiPath】キャンセルスコープ (Cancellation Scope) の使い方
](https://qiita.com/Davoi/items/6d8c649f5c505174d5a6)


# TL;DR
- HTTP Request アクティビティでは実現できない
- RestSharpを利用してプロキシの情報を明示的に与えることで実現できる

<details><summary>実装内容の要約</summary>

```cs
// クライアントの作成
RestClient client       = new RestSharp.RestClient()
client.BaseUrl          = new Uri(uri)
client.Proxy            = new System.Net.WebProxy(proxyHost, proxyPort)
client.Proxy.Credential = new System.Net.NetworkCredential(proxyUsername, proxyPassword)

// リクエストの作成
RestRequest request     = new RestRequest()
request.Method          = RestSharp.Method.GET

// クライアントを用いてリクエストを実行（送信）し、返送されたデータを保存
IRestResponse response  = client.Execute(request)

// 返送されたデータのバイト列を、ファイルとして書き込み
// （返り値がないため、【Invoke Method】アクティビティを使用する）
System.IO.File.WriteAllBytes(filePath, response.RawBytes)

```
</details>



# はじめに
UiPath には 【HTTP Request】アクティビティ（日本語では【HTTP 要求】）というものがあります。
一般的にこのアクティビティは API を操作するときなどに使用されますが、それだけではなく、URIによって示されたリソースを取得するために使用することもできます。この使用方法は、[UiPathのナレッジベース](https://www.uipath.com/ja/resources/knowledge-base/download-files-from-website)でも紹介されています。
この方法を使うことで、ブラウザの起動や操作を行うことなく、リソースを取得することが出来るようになるため、プロセスの安定性の向上が見込まれます。

ところで、企業活動においてインターネットアクセスを行うとき、セキュリティ対策などの理由によって導入されたプロキシを通過することが求められるケースがあります。またそのプロキシにおいて、ユーザー認証を求めるケースもあります。
この「ユーザー認証を求めるプロキシ」（以下、「認証ありプロキシ」）の配下で実行されるクライアントにおいて上記を実現できないか試していたところ、一筋縄ではいかず、アクティビティをそのまま使用することはできませんでした。
その代わりにUiPath上で同様の操作を実現する方法を調べ、実際に実現することができましたので、その方法を紹介したいと思います。

# 実現のための手順

## 1. RestSharp を導入する

まずは、外部との通信を行うために必要なライブラリを導入します。
UiPathがその基礎として使用する VB.Net では、RestSharpと呼ばれるライブラリが存在しますので、これを使用します。

実は【HTTP Request】アクティビティも、内部では RestSharp を使用しています。
このアクティビティを含むパッケージ（UiPath.WebAPI.Activities）をダウンロード・インストールするとき、RestSharpも同時にダウンロードされる仕組みになっていますので、まずは上記アクティビティパッケージをダウンロード・インストールしましょう。

## 2. ワークフロー上で実装する

それでは実際に、RestSharpを用いて実装を進めていきます。
ある程度、UiPathによる開発を経験している方は、下記折りたたみを参照ください。

ここから先は、要素ごとに順を追って説明していきます。

### クライアントの作成

まず、「クライアント」を作成します。これは接続する先やその設定を登録するための要素です。
変数パネルで `client` という変数を作成し、その型を `RestSharp.RestClient` に設定します。
この型は「型の参照」を使用しないと出てこないと思いますので、まずはそれを選択しましょう。
そのあとに表示されるウィンドウで、上記の型を検索してみてください。

作成が終わったら、【Assign】（【代入】）アクティビティをデザイナーパネルに配置し、以下のように設定しましょう。

```:クライアントの作成
左辺（To）   : client
右辺（Value）: new RestSharp.RestClient()
```

### クライアントへの設定

作成したクライアントに対して、設定を追加していきます。
ここでは、3つの設定を追加していきます。

- BaseUrl
    - 接続先（今回は、取得したいリソースが示されたURI）を指定します
    - ここでは、 `uri` という文字列の変数にURIが含まれていると仮定します
- Proxy
    - 通過させたいプロキシのサーバー情報を指定します
    - サーバーのFQDNと、接続に使用するポート番号が必要です
    - ここでは、`proxyHost` という文字列の変数にプロキシサーバーのFQDNが、 `proxyPort` というInt32型の変数にプロキシサーバーのポートが含まれている仮定します
- Proxy.Credential
    - プロキシに対して送信する認証情報を指定します
    - 認証情報として、IDとパスワードを準備しておきます
    - ここでは、`proxyUsername` という文字列の変数にユーザー名が、 `proxyPassword` という文字列の変数にパスワードが含まれていると仮定します
        - 認証情報をアセットなどから取得する場合は、`proxyPassword` は `System.Security.SecureString` 型でもOKです

こちらも、【Assign】アクティビティを用いて設定しましょう。


```: BaseUrl の設定
左辺（To）   : client.BaseUrl
右辺（Value）: new Uri(uri)
```

```: Proxy の設定
左辺（To）   : client.Proxy
右辺（Value）: new System.Net.WebProxy(proxyHost, proxyPort)
```

```: Proxy.Credential の設定
左辺（To）   : client.Proxy.Credential
右辺（Value）: new System.Net.NetworkCredential(proxyUsername, proxyPassword)
```


### リクエストの作成

次に、「リクエスト」を作成します。これは、クライアントが接続を行うときにどのようにデータを要求するのかを制御する要素です。
変数パネルで `request` という変数を作成し、その型を `RestSharp.RestRequest` に設定します。
こちらも、「型の参照」を使用して検索をしていただき、型を設定してあげましょう。

作成が終わったら、【Assign】アクティビティをデザイナーパネルに配置し、以下のように設定しましょう。

```: リクエストの作成
左辺（To）   : request
右辺（Value）: new RestSharp.RestRequest()
```

### リクエストへの設定

作成したリクエストに対しても、設定を追加していきます。
ここでは、1つの設定を追加していきます。

- Method
    - リクエストを送る際に、データを「送信する」のか「取得する」のか、を選択します
    - 今回のケースでは「取得」に該当するので、その内容を設定します
        - RestSharpに `RestSharp.Method.GET` という定数が準備されているので、それを使用します

例によって、【Assign】アクティビティを使用します。

```: リクエストへの設定
左辺（To）   : request.Method
右辺（Value）: RestSharp.Method.GET
```

### クライアントを使用してリクエストを実行

ここまでで、実際に接続をしてダウンロードする準備ができました。
いざ、接続です。

まず、接続をした結果を保存しておくための変数を準備します。
変数パネルで `response` という変数を作成し、その型を `RestSharp.IRestResponse` に設定します。
例によって、「型の参照」を使って型を設定しましょう。

作成が終わったら、こちらも例によって【Assign】アクティビティを使います。

```: リクエストを実行
左辺（To）   : response
右辺（Value）: client.Execute(request)
```

あとでこの部分を実行するわけですが、この処理の部分は少し時間が掛かります。
このタイミングで実際に通信をしているためですが、どのくらい時間が掛かるかは、取得したいリソースの大きさや通信環境によります。

### ファイルへの書き込み

接続した結果を変数に保存しましたが、私たちが欲しいのはファイルです。
ですので、接続した結果をファイルに変換（書き出し）してあげる処理を追加しましょう。

書き込みのために、以下の2つを準備します。

- 出力先
    - ファイルとして出力したときの、ファイルのパスです
    - 今回は、 `saveAs` という文字列の変数にパスが含まれていると仮定します
- 出力する内容
    - ファイルに対して書き込む内容です
    - 書き込む内容はデータの「バイト列」になります
    - 今回は、 `response` からバイト列を抽出する方法（`response.RawBytes` プロパティ）を使用します

実装する内容をコードで表現すると、下記のようになります。

```
System.IO.File.WriteAllBytes(saveAs, response.RawBytes)
```

もしかすると違和感を覚える方がいらっしゃるかもしれません。「あれ、そういえば変数作ってないじゃん」と。

実はこの処理は、プログラム上では処理の結果を出力しません。要は「実行するだけ」です。
そしてそのような処理は、UiPathのなかでは【Assign】アクティビティを使って表現することができません。
【Assign】アクティビティは、何かしらの結果や値を代入するものですが、その「代入するもの」がないためです。

「ではどうするのか」というと、【Invoke Method】（【メソッドを呼び出し】）アクティビティを使用します。
このアクティビティは上記でお話ししたような、プログラム上で処理の結果を出力しないものに対して、その「実行を指示する」アクティビティになります。

それでは、このアクティビティを実際に使ってみましょう。
ここではアクティビティの画像をもとにご説明を進めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36038/7f285cd2-521b-2e50-061d-0b0d0c0da615.png)

- TargetType
    - 実行したい処理が .NET のどの型に含まれるものなのかを指定します
    - 今回は、`System.IO.File` にある処理ですので、この内容を指定します
- MethodName
    - 実行したい処理の名称を指定します
    - 今回は、`WriteAllBytes` という処理ですので、この内容を指定します

これで呼び出したい処理については表現できました。
次にこの処理に対して渡すデータ、つまり「出力先」と「出力する内容」を指定しましょう。
アクティビティを選択した状態で、プロパティパネルにある「Parameter」（「パラメーター」）を探し、「…」ボタンをクリックします。表示されたウィンドウの内容を、下記画像に揃えましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36038/fec4d49b-ff1a-d825-a4a0-5247c1be1df9.png)

:::note info
なぜか2行目の値欄でエラーが表示されます。「responseというオブジェクトには RawBytes というプロパティが定義されていない」とあるのですが、正常に動作しますのでご安心ください。
:::

ここまで設定出来ましたら、すべての処理が実装できました。完成です！

## 3. 実行する

はい、あとは実行しましょう。動画にしようかと思いましたが、見た目にほとんど変化がない処理なので（笑）やめることにしました。実際の動きは、お試しいただくみなさまのお手元で体験してください。

# まとめ

正直なところ、ここまで実装する必要があるケースは多くないかもしれません。そもそも画面操作で十分に安定するのであれば、それでも問題ないからです。
一方でこれから先、UiPathによる自動化のレベルをさらに高めたいと考えている方々も多くいると思います（いてほしいです）。そのような方々に対して、この記事が助けになれば幸いです。