LoginSignup
2
5

More than 5 years have passed since last update.

C#でガルーンAPI SOAPリクエストが不正です。

Last updated at Posted at 2018-08-27

概要

C#でのgaroon SOAP APIのリクエストについて
当初の実装でちょっとハマったのでメモ

公式ドキュメント

Garoon(ガルーン) SOAP API の概要

エラー内容

GRN_UTIL_API_65001
SOAPリクエストが不正です。
"Header" がSOAPリクエストに存在しません。
サイボウズオフィシャルパートナー、または販売元にお問い合わせください。

VisualStudio2017でガルーンAPIを使ってメッセージを送ってみる
こちらの記事を参考にメッセージ送信はできたけど、
他のAPI使おうとしたらエラーになる!ギエエエエ

原因

Reference.csの修正箇所が足りませんでした。
ちゃんと理解しないまま進めると痛い目見る好例。

Reference.csでは下記の構造になっている
各機能(全般/スケジュール/掲示板など):Bindingクラス
各API(ユーザ情報取得など):Bindingクラス内の各メソッド

参考にした記事はメッセージ送信なのでMessageBindingクラスのMessageCreateThreadsメソッドにSOAPヘッダーを追加している。
他のAPIにはヘッダー付いてないので、そのまま使おうもんならそりゃあヘッダーないですエラーでるよね〜


Garoon SOAP APIの共通仕様

Garoon SOAP APIを使用するには、ガルーンに登録したユーザーで認証する必要があります。認証には、Cookieを使用した方式と、WebServicesSecurity(WS-Security)を使用した方式があります。

WS-Securityを用いた認証方式
ログイン名とパスワードを SOAP ヘッダに含めることによって認証を行います。 この認証方式では、リクエストごとに認証を行う必要があります。

Cookieを使用しない場合はWS-Securityを用いた認証方式が必要。
(参考記事の実装もこっち)
認証用ログイン名、パスワードがSOAPヘッダに必要だけど、
それが存在しないから認証エラーになってるっぽい

対応内容

例としてログイン名からユーザーを取得するAPIを使用。

  • 使いたいAPIが含まれるBindingクラスにSOAPヘッダメンバを追加。
Reference.cs
public partial class BaseBinding : System.Web.Services.Protocols.SoapHttpClientProtocol {

    /* ここから */
    public ActionElement action;
    public SecurityElement security;
    public TimestampElement timeStamp;
    /* ここまで */

    private System.Threading.SendOrPostCallback BaseGetUserVersionsOperationCompleted;
  • 使用するサービスメソッドにSOAPヘッダ属性追加。
Reference.cs
/// <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) {
  • 呼び出し箇所でヘッダー情報を追加
呼び出し.cs

// 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の自動生成時にヘッダ入ってれば楽なのに、入れてないのは何か理由があるんだろうか。

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5