概要
C#でのgaroon SOAP APIのリクエストについて
当初の実装でちょっとハマったのでメモ
公式ドキュメント
エラー内容
GRN_UTIL_API_65001
SOAPリクエストが不正です。
"Header" がSOAPリクエストに存在しません。
サイボウズオフィシャルパートナー、または販売元にお問い合わせください。
[VisualStudio2017でガルーンAPIを使ってメッセージを送ってみる]
(https://qiita.com/HAGITAKO/items/b684f4e31552e5a5db69)
こちらの記事を参考にメッセージ送信はできたけど、
他のAPI使おうとしたらエラーになる!ギエエエエ
原因
Reference.csの修正箇所が足りませんでした。
ちゃんと理解しないまま進めると痛い目見る好例。
Reference.csでは下記の構造になっている
各機能(全般/スケジュール/掲示板など):Bindingクラス
各API(ユーザ情報取得など):Bindingクラス内の各メソッド
参考にした記事はメッセージ送信なのでMessageBindingクラスのMessageCreateThreadsメソッドにSOAPヘッダーを追加している。
他のAPIにはヘッダー付いてないので、そのまま使おうもんならそりゃあヘッダーないですエラーでるよね〜
Garoon SOAP APIを使用するには、ガルーンに登録したユーザーで認証する必要があります。認証には、Cookieを使用した方式と、WebServicesSecurity(WS-Security)を使用した方式があります。
WS-Securityを用いた認証方式
ログイン名とパスワードを SOAP ヘッダに含めることによって認証を行います。 この認証方式では、リクエストごとに認証を行う必要があります。
Cookieを使用しない場合はWS-Securityを用いた認証方式が必要。
(参考記事の実装もこっち)
認証用ログイン名、パスワードがSOAPヘッダに必要だけど、
それが存在しないから認証エラーになってるっぽい
対応内容
例としてログイン名からユーザーを取得するAPIを使用。
- 使いたいAPIが含まれるBindingクラスにSOAPヘッダメンバを追加。
public partial class BaseBinding : System.Web.Services.Protocols.SoapHttpClientProtocol {
/* ここから */
public ActionElement action;
public SecurityElement security;
public TimestampElement timeStamp;
/* ここまで */
private System.Threading.SendOrPostCallback BaseGetUserVersionsOperationCompleted;
- 使用するサービスメソッドにSOAPヘッダ属性追加。
/// <remarks/>
/* ここから */
[System.Web.Services.Protocols.SoapHeaderAttribute("action", Direction = SoapHeaderDirection.InOut)]
[System.Web.Services.Protocols.SoapHeaderAttribute("security", Direction = SoapHeaderDirection.InOut)]
[System.Web.Services.Protocols.SoapHeaderAttribute("timeStamp", Direction = SoapHeaderDirection.InOut)]
/* ここまで */
[System.Web.Services.Protocols.SoapRpcMethodAttribute("BaseGetUsersByLoginName", RequestNamespace="http://wsdl.cybozu.co.jp/base/2008", ResponseNamespace="http://wsdl.cybozu.co.jp/base/2008", Use=System.Web.Services.Description.SoapBindingUse.Literal)]
[return: System.Xml.Serialization.XmlArrayAttribute("returns")]
[return: System.Xml.Serialization.XmlArrayItemAttribute("user", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=false)]
public UserType[] BaseGetUsersByLoginName([System.Xml.Serialization.XmlArrayItemAttribute("login_name", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=false)] string[] parameters) {
- 呼び出し箇所でヘッダー情報を追加
// base区分のAPIクライアント
BaseBinding baseAPI = new BaseBinding();
/* ここから */
//--------------------------------------------------------
// ヘッダ情報追加
//--------------------------------------------------------
// Garoon共通のSOAPヘッダー
ActionElement actionElement = new ActionElement();
UsernameTokenElement userNameTokenElement = new UsernameTokenElement();
SecurityElement securityElement = new SecurityElement();
TimestampElement timeStampElement = new TimestampElement();
// 呼び出すAPI名
actionElement.actionValue = "BaseGetUsersByLoginName";
// アクセス者
userNameTokenElement.Username = "xxxxx"; // GaroonのログインID
userNameTokenElement.Password = "xxxxx"; // ログインパスワード
securityElement.usernameToken = userNameTokenElement;
// タイムスタンプ
timeStampElement.Created = DateTime.UtcNow;
timeStampElement.Expires = timeStampElement.Created.AddDays(8);
// ヘッダー設定
baseAPI.action = actionElement;
baseAPI.security = securityElement;
baseAPI.timeStamp = timeStampElement;
/* ここまで */
//--------------------------------------------------------
// リクエスト(Body)作成
//--------------------------------------------------------
// このAPIだとログインID配列のみ
string[] param = { "yyyyy" };
try
{
//--------------------------------------------------------
// SOAPリクエスト
//--------------------------------------------------------
UserType[] userTypes = baseAPI.BaseGetUsersByLoginName(param);
Console.WriteLine("成功");
}
catch (System.Web.Services.Protocols.SoapException ex)
{
// ユーザーIDに一致するユーザーがいない場合もここに来る
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
結果
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<BaseGetUsersByLoginName xmlns="http://wsdl.cybozu.co.jp/base/2008">
<parameters xmlns="">
<login_name>yyyyy</login_name>
</parameters>
</BaseGetUsersByLoginName>
</soap:Body>
</soap:Envelope>
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>
<Action xmlns="http://wsdl.cybozu.co.jp/base/2008">BaseGetUsersByLoginName</Action>
<Security xmlns="http://wsdl.cybozu.co.jp/base/2008">
<UsernameToken>
<Username>xxxxx</Username>
<Password>xxxxx</Password>
</UsernameToken>
</Security>
<Timestamp xmlns="http://wsdl.cybozu.co.jp/base/2008">
<Created>2018-08-27T00:43:07.6447372Z</Created>
<Expires>2018-09-04T00:43:07.6447372Z</Expires>
</Timestamp>
</soap:Header>
<soap:Body>
<BaseGetUsersByLoginName xmlns="http://wsdl.cybozu.co.jp/base/2008">
<parameters xmlns="">
<login_name>yyyyy</login_name>
</parameters>
</BaseGetUsersByLoginName>
</soap:Body>
</soap:Envelope>
ちゃんとヘッダ設定したらエラー解消。ハレルヤ。
まとめ
使いたいAPI各メソッドにヘッダ追加処理が必要。
うーむめんどくさい。
どうにかうまいやり方ないかなぁ…
というかReference.csの自動生成時にヘッダ入ってれば楽なのに、入れてないのは何か理由があるんだろうか。