0

XML ファイルのコンテンツをハンドヘルド デバイス (Compact Framework/Windows CE) からサーバー アプリの Web API メソッドに次のように送信しようとしています (クライアント コード):

public static string SendXMLFile(string xmlFilepath, string uri, int timeout)
{
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);

    request.KeepAlive = false;
    request.ProtocolVersion = HttpVersion.Version10;

    request.Method = "POST";

    StringBuilder sb = new StringBuilder();
    using (StreamReader sr = new StreamReader(xmlFilepath))
    {
        String line;
        while ((line = sr.ReadLine()) != null)
        {
            // test to see if it's finding any lines
            //MessageBox.Show(line); <= works fine
            sb.AppendLine(line);
        }
        byte[] postBytes = Encoding.UTF8.GetBytes(sb.ToString());

        if (timeout < 0)
        {
            request.ReadWriteTimeout = timeout;
            request.Timeout = timeout;
        }

        request.ContentLength = postBytes.Length;
        request.KeepAlive = false;

        request.ContentType = "application/xml";

        try
        {
            Stream requestStream = request.GetRequestStream();

            requestStream.Write(postBytes, 0, postBytes.Length);
            requestStream.Close();

            using (var response = (HttpWebResponse)request.GetResponse())
            {
                return response.ToString();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("SendXMLFile exception " + ex.Message);
            request.Abort();
            return string.Empty;
        }
    }
}

コメントアウトされたコード (「<= は正常に動作します」) でわかるように、テストしたところ、必要なデータが StringBuilder に追加されています。スローされる例外はありません (「SendXMLFile 例外」は表示されません)。

ただし、対応するサーバー コードが呼び出されると、次のようになります。

[Route("api/DeliveryItems/PostArgsAndXMLFileAsStr")]
public async void PostArgsAndXMLFileAsStr([FromBody] string stringifiedXML, string serialNum, string siteNum)
{
    string beginningInvoiceNum = string.Empty;
    string endingInvoiceNum = string.Empty;

    XDocument doc = XDocument.Parse(stringifiedXML);

...「serialNum」および「siteNum」引数は期待どおり (有効な期待値を含む) ですが、本体 (文字列化された XML) は null です。なんで?

アップデート

これもクライアントに追加しました:

request.ContentLength = postBytes.Length;
// Did the sb get into the byte array?
MessageBox.Show(request.ContentLength.ToString());

...そして、「112」と表示されているように、バイト配列にはデータがあります(XMLファイルは非常に小さいです)。

更新 2

ここで、さらに別のデバッグ メッセージを追加しました。

try
{
    Stream requestStream = request.GetRequestStream();
    // now test this:
    MessageBox.Show(string.Format("requestStream length is {0}", requestStream.Length.ToString()));
    requestStream.Write(postBytes, 0, postBytes.Length);
    requestStream.Close();

    using (var response = (HttpWebResponse)request.GetResponse())
    {
        return response.ToString();
    }
}
catch (Exception ex)
{
    MessageBox.Show("SendXMLFile exception " + ex.Message);
    request.Abort();
    return string.Empty;
}

...そして、「requestStream length is」というメッセージすら表示されません。代わりに、「SendXMLFileException NotSupportedException」が表示されます...???

更新 3

これは、ホーソン効果または類似の例だと思います。debug (MessageBox.Show()) ステートメントをコメントアウトしたら、サーバー アプリに戻す作業に戻りますが、[FromBody] val は null です。

次に、クライアントに「トランスポート接続からデータを読み取れません」というメッセージが表示されます

更新 4

stringifiedXML はここではまだ null です:

public async void PostArgsAndXMLFileAsStr([FromBody] string stringifiedXML, string serialNum, string siteNum)
{
    string beginningInvoiceNum = string.Empty;
    string endingInvoiceNum = string.Empty;

    XDocument doc = XDocument.Parse(stringifiedXML);

...この質問への回答に続いて、クライアントのコードを次のように変更した後でも:

public static string SendXMLFile(string xmlFilepath, string uri)
{
    MessageBox.Show(string.Format("In SendXMLFile() - xmlFilepath == {0}, uri == {1}", xmlFilepath, uri));
    string strData = GetDataFromXMLFile();
    HttpWebRequest request = CreateRequest(uri, HttpMethods.POST, strData, "application/xml");

    request.KeepAlive = false;
    request.ProtocolVersion = HttpVersion.Version10;

    try
    {
        using (var response = (HttpWebResponse)request.GetResponse())
        {
            return response.GetResponseStream().ToString();
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("SendXMLFile exception " + ex.Message);
        request.Abort();
        return string.Empty;
    }
}

private static string GetDataFromXMLFile()
{
    // test data - if it works, get the (same, for now) data from the file
    return @"<?xml version=1.0?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>";  //had to remove "s from version num
}

// Based on code from Andy Wiggly (the owner of Wiggly Field in Chicago and the Wiggly chewing gum company?)
public static HttpWebRequest CreateRequest(string uri, HttpMethods method, string data, string contentType)
{
    WebRequest request = HttpWebRequest.Create(uri);
    request.Method = Enum.ToObject(typeof(HttpMethods), method).ToString();
    request.ContentType = contentType;
    ((HttpWebRequest)request).Accept = contentType;
    if (method != HttpMethods.GET && method != HttpMethods.DELETE)
    {
        Encoding encoding = Encoding.UTF8;
        request.ContentLength = encoding.GetByteCount(data);
        request.ContentType = contentType;
        request.GetRequestStream().Write(
          encoding.GetBytes(data), 0, (int)request.ContentLength);
        request.GetRequestStream().Close();
    }
    else
    {
        // If we're doing a GET or DELETE don't bother with this 
        request.ContentLength = 0;
    }
    // Finally, return the newly created request to the caller. 
    return request as HttpWebRequest;
}

注:これがサーバーをシャットダウンしたことによる単なる誤解を招く副作用かどうかはわかりませんが、その後、クライアント/ハンドヘルド アプリで次のエラー メッセージが表示されました。

"System.Net.ProtocolVi..." "この操作は、要求が送信された後に実行できません。"

更新 5

スタック トレースが必要な場合は、&c:

serNum と siteNum は、次のように uri に連結される単純な値です。

string uri = string.Format("http://192.168.125.50:28642/api/FileTransfer/GetHHSetupUpdate?serialNum={0}&clientVersion={1}", serNum, clientVer);

次のようにスタック トレースを取得しようとしました。

catch (Exception ex)
{
    MessageBox.Show(string.Format("Msg = {0}; StackTrace = {1)", ex.Message, ex.StackTrace));
    request.Abort();
    return string.Empty;
}

...しかし、今は「この操作は、リクエストが送信された後は実行できません」と表示されるだけです。

更新 6

メソッドのシグネチャを次のように変更しました。

public static HttpWebResponse SendXMLFile(string xmlFilepath, string uri)

...そしてこれに対応するコード:

try
{
    using (var response = (HttpWebResponse)request.GetResponse())
    {
        return response;
    }
}
catch (Exception ex)
{
    MessageBox.Show(string.Format("Msg = {0}; StackTrace = {1)", ex.Message, ex.StackTrace));
    request.Abort();
    return null;
}

...しかし、違いはありませんでした(「StackTrave =」メッセージが表示されないため、最初に失敗したに違いありません)

更新 7

2 つのデバッグ文字列を入れます。

0)

public static HttpWebRequest CreateRequestNoCredentials(string uri, HttpMethods method, string data, string contentType)
{
    //test:
    MessageBox.Show(string.Format("In CreateRequestNoCredentials(); data passed in = {0}", data));

1) SendXMLFile() で:

//test:
MessageBox.Show(string.Format("After calling CreateRequestNoCredentials(), request contentLen = {0}, headers = {1}, requestUri = {2}", 
    request.ContentLength, request.Headers, request.RequestUri));

...そして私はこれを見ます:

ここに画像の説明を入力

...しかし、2番目のサーバーが悲惨な詳細を表示する機会を得る前に、サーバーはnullボディ値を受け取り、クラッシュし、クライアントは同じ古い「この操作は後で実行できません」と泣き言を言いますリクエストが送信されました」という苦情。

更新 8

「 CreateRequest 呼び出しの後に KeepAlive と ProtocolVersion の設定を削除すると、例外がなくなるのではないかと思います。」という提案に応えて、コードを次のように変更しました。

    HttpWebRequest request = CreateRequestNoCredentials(uri, HttpMethods.POST, strData, "application/xml");

    //test:
    MessageBox.Show(string.Format("After calling CreateRequestNoCredentials(), request contentLen = {0}, headers = {1}, requestUri = {2}", 
        request.ContentLength, request.Headers, request.RequestUri));

    request.KeepAlive = false;
    request.ProtocolVersion = HttpVersion.Version10;


public static HttpWebRequest CreateRequestNoCredentials(string uri, HttpMethods method, string data, string contentType)
{
    //test:
    MessageBox.Show(string.Format("In CreateRequestNoCredentials(); data passed in = {0}", data));

    WebRequest request = HttpWebRequest.Create(uri);
    request.Method = Enum.ToObject(typeof(HttpMethods), method).ToString();
    request.ContentType = contentType;
    ((HttpWebRequest)request).Accept = contentType;

    if (method != HttpMethods.GET && method != HttpMethods.DELETE)
    {
        Encoding encoding = Encoding.UTF8;
        request.ContentLength = encoding.GetByteCount(data);
        request.ContentType = contentType;
        request.GetRequestStream().Write(
          encoding.GetBytes(data), 0, (int)request.ContentLength);
        request.GetRequestStream().Close();
    }
    else
    {
        // If we're doing a GET or DELETE don't bother with this 
        request.ContentLength = 0;
    }
    // Finally, return the newly created request to the caller. 
    return request as HttpWebRequest;
}

...これに:

    HttpWebRequest request = CreateRequestNoCredentials(uri, HttpMethods.POST, strData, "application/xml");

    //test:
    MessageBox.Show(string.Format("After calling CreateRequestNoCredentials(), request contentLen = {0}, headers = {1}, requestUri = {2}", 
        request.ContentLength, request.Headers, request.RequestUri));

public static HttpWebRequest CreateRequestNoCredentials(string uri, HttpMethods method, string data, string contentType)
{
    //test:
    MessageBox.Show(string.Format("In CreateRequestNoCredentials(); data passed in = {0}", data));

    WebRequest request = HttpWebRequest.Create(uri);
    request.Method = Enum.ToObject(typeof(HttpMethods), method).ToString();
    request.ContentType = contentType;
    ((HttpWebRequest)request).Accept = contentType;
    // moved from elsewhere to here:
    ((HttpWebRequest)request).KeepAlive = false;
    ((HttpWebRequest)request).ProtocolVersion = HttpVersion.Version10;

    if (method != HttpMethods.GET && method != HttpMethods.DELETE)
    {
        Encoding encoding = Encoding.UTF8;
        request.ContentLength = encoding.GetByteCount(data);
        request.ContentType = contentType;
        request.GetRequestStream().Write(
          encoding.GetBytes(data), 0, (int)request.ContentLength);
        request.GetRequestStream().Close();
    }
    else
    {
        // If we're doing a GET or DELETE don't bother with this 
        request.ContentLength = 0;
    }
    // Finally, return the newly created request to the caller. 
    return request as HttpWebRequest;
}

...それでも、同じエラーメッセージ (「この操作は、リクエストが送信された後は実行できません」) が表示され、stringifiedXML はサーバーにヒットしたときにまだ null です。

更新 9

Fiddler 2 を介して、私が理解しているものを送信すると、次のようになります (画像を右クリックし、視覚的なスーパーパワーがない場合は、新しいタブで開きます)。

ここに画像の説明を入力

...しかし、私が何を見ているのかわかりません...うまくいきましたか? 失敗しましたか?「body == 0」列は一時停止/失敗したと思わせますが、「204」は「サーバーはリクエストを正常に処理しましたが、コンテンツを返していません」という意味のようです...

更新 10

これは、uri を修正した後の Fiddler の叫び声です。サーバー アプリのブレークポイントに到達し、適切なデータが送信されます。

ここに画像の説明を入力

更新 11

このコードを変更すると:

string strData = sb.ToString();
HttpWebRequest request = CreateRequestNoCredentials(uri, HttpMethods.POST, strData, "application/xml");

...これに:

string strData = @sb.ToString(); // GetDataFromXMLFile();
string body = String.Format("\"{0}\"", strData);
HttpWebRequest request = CreateRequestNoCredentials(uri, HttpMethods.POST, body, "application/json"); 

...これを stringifiedXML で取得しています: "

...そして、次のようになりました

どう見ても改善ですね…

更新 12

Fiddle で "Request Body" として渡された文字列の正確な構成/フォーマットに応じて、結果は根本的に異なります。

これをリクエスト本文として:

<?xml version="1.0"?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>

...文字列化されたXMLはnullです

これをリクエスト本文として:

"<?xml version=1.0?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>"

...stringifiedXML はまったく同じです ("")

...しかし、例外があります:

System.Xml.XmlException はユーザー コードによって処理されませんでした HResult=-2146232000 Message='1.0' は予期しないトークンです。予想されるトークンは '"' または ''' です。1 行目、15 番目の位置。Source=System.Xml LineNumber=1 LinePosition=15 SourceUri="" System.Xml.XmlTextReaderImpl.ThrowUnexpectedToken(String expectedToken1, String expectedToken2) の Xml.XmlTextReaderImpl.Throw(String res, String[] args) ) System.Xml.Linq.XDocument.Load (XmlReader リーダー、LoadOptions オプション) で System.Xml.Linq.XDocument.Parse (文字列テキスト、LoadOptions オプション) で System.Xml.Linq.XDocument で。

これをリクエスト本文として:

"<?xml version="1.0"?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>"

...stringifiedXML は "

最後から 2 番目に、これをリクエスト ボディとして使用します。

"<?xml version=\"1.0\"?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>"

...stringifiedXML はまったく同じものです ("")

...しかし、私はこの例外を受け取ります:

System.InvalidOperationException はユーザー コードによって処理されませんでした。HResult=-2146233079 メッセージ = シーケンスには要素が含まれていません ソース = System.Core .MoveNext() in c:\HandheldServer\HandheldServer\Controllers\DeliveryItemsController.cs:109 行目 InnerException:

そして最後に、これを渡すと、(偽物ではありますが) vals が角度変数内に含まれます。

"<?xml version=\"1.0\"?><LocateAndLaunch><Tasks>Some Task</Tasks><Locations>Some Location</Locations></LocateAndLaunch>"

...私はまだ「シーケンスに要素が含まれていません」と表示されます

この方法は、レイチェル・カニングよりもうるさいです! それは何を望んでいる - ビールの卵?!?

更新 13

このコードで:

public async void PostArgsAndXMLFileAsStr([FromBody] string stringifiedXML, string serialNum, string siteNum)
{
    XDocument doc = XDocument.Parse(await Request.Content.ReadAsStringAsync()); 

...またはこれ:

. . .XDocument doc = XDocument.Load(await Request.Content.ReadAsStreamAsync());

...そして、これは着信 stringifiedXML として:

"一部の TaskSome の場所"

...例外が発生します: 「System.Xml.XmlException was unhandled by user code HResult=-2146232000 Message=Root element is missing.」

このコード (同じ stringifiedXML) を使用すると、次のようになります。

XDocument doc = XDocument.Parse(stringifiedXML);

... 私は、「System.InvalidOperationException はユーザー コードによって処理されませんでした。HResult=-2146233079 メッセージ = シーケンスに要素が含まれていません。 HandheldServer.Controllers.DeliveryItemsController.d__2.MoveNext() in c:\HandheldServer\HandheldServer \Controllers\DeliveryItemsController.cs:line 109 InnerException: "

IOW、着信文字列の解析方法に応じて、「ルート要素が見つかりません」または「シーケンスに要素が含まれていません」というメッセージが表示されます

デュース・マカリスター・マクリーン・ヴァージニア・ウィーパーとは?!? 「<LocateAndLaunch>」はルート要素ではありませんか?「何らかのタスク」と「何らかの場所」の要素ではないでしょうか。

4

1 に答える 1

1

このようなアクションメソッドの場合

public async void PostArgsAndXMLFileAsStr([FromBody] string stringifiedXML,
                                              string serialNum, string siteNum)
{}

要求メッセージは次のようにする必要があります。ここでは JSON を使用します。

POST http://localhost:port/api/values/PostArgsAndXMLFileAsStr?serialNum=1&siteNum=2 HTTP/1.1
Content-Type: application/json
Host: localhost:port
Content-Length: 94

"<?xml version=1.0?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>"

リクエストの本文には、二重引用符を含める必要があります。これで、バインディングは正しく機能するはずです。

したがって、コンテンツ タイプapplication/jsonを指定してメッセージを投稿し、本文を次のようにフォーマットします。

string content = @"<?xml version=1.0?><LocateAndLaunch><Tasks></Tasks><Locations></Locations></LocateAndLaunch>";
string body = String.Format("\"{0}\"", content);

クライアント側のコードを変更する前に、Fiddler を使用して上記のような POST を送信し、Web API 側で機能することを確認します。その後、クライアント側を変更して、Fiddler で動作中の要求だけを出力するようにします。

于 2014-03-13T02:07:41.107 に答える