質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

1045閲覧

HTTP通信でjson形式のデータをサーバーAPIにPOSTしたい

cosmori-man

総合スコア2

HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

2グッド

0クリップ

投稿2023/11/16 06:14

編集2023/11/22 07:56

質問

〇Content-Type: application/jsonの場合にエンコーディングの指定は必要か。
〇根本的にjson形式のデータをPOSTする方法が間違っているか。
以下の現象に関して、解決策や問題点が思い当たる方がいらっしゃれば、ぜひ教えていただけますでしょうか。
よろしくお願いいたします。

実現したいこと

C#

1// POST: api/PushMsg 2 [HttpPost] 3 public bool PushMessage([FromBody] string value) 4 { 5 try 6 { 7 Logger.log(Logger.INFO, "Apnsサーバーに送信処理開始(クライアントから受信直後)"); 8 Logger.log(Logger.INFO, value, true); 9 PushMessage msg = JsonConvert.DeserializeObject<PushMessage>(value); 10 var ret = SendMessage(msg); 11 Logger.log(Logger.INFO, "Apnsサーバーに送信正常終了", true); 12 return ret.Result; 13 } 14 catch(Exception ex) 15 { 16 Logger.log(Logger.ERROR, "送信異常終了 => " + ex.Message, true); 17 return false; 18 } 19 }

URL:http://IPアドレス:ポート番号/サーバー名/API/PushMsg
Header:Content-Type: application/json\r\n
HTTPBody:以下の文字列(CString)をバッファ(char*)の格納して ”POST”
{"userNo":4,"member":[12,16],"category":"comment","detail":{"commentId":10282,"talkId":36},"title":"新規コメント","message":"新規コメントがあります。","appId":"製品識別用ID","sound":"default"}

で上記APIにjson形式の文字列をPOSTしたい

発生している問題・エラーメッセージ

System.Text.DecoderFallbackException: Unable to translate bytes [82][DC] at index 116 from specified code page to Unicode.……

Header: application/json; charset=UNICODE
とすると、API/PushMsgにたどり着いて文字列を渡すことができるが、
引数で受け取る文字列が文字化けしてしまった。
例)≻獵牥潎㨢㜳∬敭扭牥㨢ㅛ崲∬灡䥰≤∺獡牴獯慴敧䄮瑳潲慔歬Ⱒ琢瑩敬㨢丢睥敍獳条≥∬敭獳条≥∺潙⁵慨敶愠丠睥䴠獥慳敧∮∬慣整潧祲㨢挢浯敭瑮Ⱒ搢瑥楡≬笺挢浯敭瑮摉㨢㤱㠶ⰰ琢污䥫≤㈺紱∬潳湵≤∺敤慦汵≴}

試したこと

検証用にPOSTされた文字列を受け取って、そのまま返すだけのAPIを用意して検証してみた。↓

C#

1[HttpPost("TestFunc")] 2 public string TestFunc([FromBody] string value) 3 { 4 var postData = ""; 5 postData = value; 6 Logger.log(Logger.INFO, " postData :" + postData, true); 7 8 return postData != null ? postData : "no post body"; 9 }

URL:http://IPアドレス:ポート番号/サーバー名/API/PushMsg/TestFunc
Header:Content-Type: text/plain\r\n
HTTPBody:同様

これを実行してデバックすると、
postData :{"userNo":37,"member":[12],"appId":"アプリ識別用ID","title":"新規メッセージ","message":"新規のメッセージがあります。","category":"comment","detail":{"commentId":19802,"talkId":21},"sound":"default"}
となり文字化け等起こらず、成功しました。
サーバーログでも確認済みです。

MFC側POST処理

C++

1BOOL NotifyiOS( CString strConnectString, CString strBody ) 2{ 3 // jsonファイル読み取り 4 CString strLocalTextFullPath = _T( "jsonファイルのパス" ); 5 6 // 文字コード指定してファイルから読み取り 7 strBody = _T( "" ); 8 FILE *pFile; 9 fopen_s( &pFile, CT2A( strLocalTextFullPath ), "r, ccs=UTF-8" ); 10 //fopen_s( &pFile, CT2A( strLocalTextFullPath ), "r, ccs=UTF-16LE" ); 11 if ( NULL != pFile ) { 12 CStdioFile file( pFile ); 13 CString strJsonText; 14 while ( file.ReadString( strJsonText ) ) { 15 TRACE( L"%s\n", strJsonText ); 16 strBody = strJsonText; 17 } 18 file.Close( ); 19 } 20 fclose( pFile ); 21 22 // バッファ確保、データコピー 23 DWORD nBuff = strBody.GetLength() * sizeof( TCHAR ); 24 char *pBuff = NULL; 25 pBuff = (char *)malloc( nBuff ); 26 if ( NULL == pBuff ) { 27 return FALSE; 28 } 29 strcpy_s( pBuff, nBuff, CT2A( strBody, CP_UTF8 ) ); 30 strcpy_s( pBuff, nBuff, CT2A( strBody ) ); // こうするとTestFuncで文字化け 31 32 CString strURL = strConnectString; 33 34 CString strHttpRes; 35 int nResult = -1; 36 nResult = HttpiOSNotify( strHttpRes, CT2A( strURL ), pBuff, nBuff, 300000 ); // POST 37 38 if ( 0 > nResult ) { 39 free( pBuff ); 40 return FALSE; 41 } 42 43 free( pBuff ); 44 return TRUE; 45} 46 47int HttpiOSNotify( CString &csRet, const char *szURL, void *pData, int nData, DWORD Timeout, const char *szProxyServer, const char *szProxyUser, const char *szProxyPassword ) 48{ 49 int nRet; 50 CActiveBuffer Buff; 51 52 if ( FALSE == Buff.Init( 32768, 32768 ) ) return 0; 53 54 nRet = HttpiOSNotify( Buff, szURL, pData, nData, Timeout, szProxyServer, szProxyUser, szProxyPassword ); 55 if ( 1 > nRet ) { 56 Buff.Close( ); 57 return nRet; 58 } 59 60 Buff.Add( "\0", 1 ); 61 62 csRet = (char *)Buff.DataP(); 63 Buff.Close( ); 64 65 return nRet; 66} 67 68int HttpiOSNotify( CActiveBuffer &Buff, const char *szURL, void *pData, int nData, DWORD Timeout, const char *szProxyServer, const char *szProxyUser, const char *szProxyPassword ) 69{ 70 char szWork[ 512 ]; 71 DWORD Timeout2; 72 CWinInetHttpThread *thHttpGet; 73 CString strHeader; 74 CActiveBuffer Body; 75 76 if ( FALSE == Body.Init( 131072, 131072 ) ) return -1; 77 78 Timeout2 = Timeout / 2; 79 if ( 30000 > Timeout2 ) Timeout2 = 30000; 80 81 //strHeader = _T( "" ); // ヘッダー指定なし 82 strHeader = _T( "Content-Type: application/json\n" ); // × 83 //strHeader = _T( "Content-Type: application/json; charset=UTF-8\r\n" ); // × 84 //strHeader = _T( "Content-Type: application/json; charset=UNICODE\r\n" ); // 〇 85 //strHeader = _T( "Content-Type: application/json; charset=UTF-16\r\n" ); // 〇 86 //strHeader = _T( "Content-Type: text/plain\r\n" ); // TestFunc用 87 88 Body.Add( pData, (DWORD)nData ); 89 90 thHttpGet = new CWinInetHttpThread( CString( szURL ), Timeout2, Timeout2, Timeout2, strHeader, Body.DataP( ), Body.GetLength( ), HttpReceiveToBuffer, &Buff, NULL, 0, CString( szProxyServer ), CString( szProxyUser ), CString( szProxyPassword ) ); 91 92 if ( WAIT_OBJECT_0 != thHttpGet->WaitForThread( Timeout ) ) { 93 thHttpGet->SetCancel( ); 94 thHttpGet->WaitForEndThread( ); 95 delete thHttpGet; 96 Body.Close( ); 97 return -12002; 98 } 99 100 thHttpGet->WaitForEndThread( ); 101 102 if ( true == thHttpGet->GetErrInfo( )->IsError( ) ) { 103 int nErr = thHttpGet->GetErrInfo( )->ErrCode( ); 104 delete thHttpGet; 105 Body.Close( ); 106 return -nErr; 107 } 108 109 delete thHttpGet; 110 return (int)Buff.GetLength( ); 111} 112 113その他一般的なHTTPセッション処理

【現状の原因調査結果】

TestFuncでは文字化けせず、PushMessageでは文字化けしてしまう理由がよくわかりません。

ちなみにコマンドプロンプトから同じjson文字列のデータをポストすると文字化けもなく、成功します。
例)curl -X POST -H "Content-Type: application/json" -d @iOSNotifyInfo.json "http://192.168.1.175:80/ApnsPushServerDebug/API/PushMsg" -v

補足情報(FW/ツールのバージョンなど)

【開発環境】
(アプリ)
フレームワーク:MFC
言語:C++
プロジェクトの文字セット:UNICODE

(サーバ)
フレームワーク:.Net Core 3.1
言語:C#

chiyochiyo, TYY👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

can110

2023/11/16 06:42

MFC側のコード(POSTデータ作成~送信部分)を提示ください。
cosmori-man

2023/11/16 08:08

MFC側のHTTPのPOST処理を追記させていただきました。 省略が多くわかりずらくなってしまっていますが、何卒宜しくお願い致します。
jimbe

2023/11/16 08:58 編集

java のタグが付いていますけども、どこら辺に java が関係するのでしょうか。 json の間違い?
cosmori-man

2023/11/16 08:59

jimbeさん すみません間違いです。 訂正いたします。
jimbe

2023/11/16 10:15

編集ありがとうございます。
yominet

2023/11/17 02:53

c++の26行目のstrcpy_sとCT2Aの作業意図は何でしょうか?
cosmori-man

2023/11/17 04:30

yominetさん strcpy_sではCString型の変数の値(json形式の文字列)をchar *型にコピーしています。 この部分、文字コードや符号化の観点でおかしいでしょうか?(自分でも少し怪しんでます)
cosmori-man

2023/11/17 04:38

CT2Aに関しては、strcpy_sが引数にマルチバイト文字列を取るため、CStringをマルチバイトに変換するために使用しています。
yominet

2023/11/17 18:17 編集

それでは、送信する内容がマルチバイトに変換されているのだと思います。 直の回答になるかどうかわからないのでコメントで申し訳ありませんが、たぶん21~26行目は送信用のバッファを作成し、そこに文字コードに関係なくバイナリデータとして入れておくって作業ではないかと思います。 なので、22行目のchar*は、BYTE*に置き換えて考えてみてはいかがでしょうか あと、個人的経験ですが今時マルチバイト・UNICODEを切り替える場面もそうはないので Tマクロ系は極力使わず、CString=CStringWと割り切ってしまったほうが楽かもしれません。
cosmori-man

2023/11/20 07:59

コメントありがとうございます。 BYTE*も試しましたが、文字列を正しく渡せていないようで ”文字列中に無効な文字があります”と表示されて、HTTPレスポンスが「サーバーで予期しない状態が発生し、要求を満たすことができませんでした。」(500)となってしまいます。 サーバー側の呼び出す関数の引数が、SYSTEM::Stringという型なのですが、こちらを調べるとUTF-16と記載されておりました。 HTTPヘッダーにてapplication/json → UTF-8エンコード となるのにPOSTBodyを受け取る型がUTF-16 となるのでもう無理では。。となっています。 年越せません。うわ~~~
yominet

2023/11/20 14:54

どんな、修正したのか追記しましょう
guest

回答2

0

ベストアンサー

MIMEタイプの application/json については、https://datatracker.ietf.org/doc/html/rfc8259 に記述があります。
charsetパラメーターは定義されてないと書いてあります。Charsetは送信者と受信者双方で合意して変更しない限り、UTF-8固定です。もちろん、送信者と受信者双方で合意すれば何でも良いですが。
また、charset=xxxと書いても未定義の属性を書いただけなので、それをどう解釈するかは受け手次第です。常識的に考えると、無視するか、エラーにするか、自分の知っているCharsetの時だけ解釈するか、そのあたりでしょうか。
お書きの現象から見ると「自分の知っているCharsetの時だけ解釈してそれ以外エラー」ですかね。

Unable to translate bytes [82][DC] at index 116 from specified code page to Unicode

というエラーメッセージを見る限り、データがSJIS/CP932で「ま」という文字をUnicodeに変換できないということですね。
MFCライブラリは知らないのでプログラムの中味は見てませんが、
JSON文字列(のつもりの文字列)をファイルに書き出して終了し、ファイルの中味を見てエンコーディングがどうなっているか調べ、ちゃんとUTF-8になるようにプログラムを書き直せば良いかと思います。POSTするのはその後です。

投稿2023/11/16 15:00

編集2023/11/16 15:04
otn

総合スコア84574

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cosmori-man

2023/12/05 07:54

返信遅くなり申し訳ございません。 解決いたしました。 文字コードの変換等の問題は初期に改善しており、通信に失敗する(実際には接続、サーバーAPIの呼び出しともに成功している)と認識していた現象は、サーバーからAPIによって操作される端末側の設定の問題でした。 皆様お手数をおかけしました。 ありがとうございました。
guest

0

ちゃんと流れを追えていませんが、CT2A( strBody )だと意図に反してエンコーディングはCP932になっていると思います。
代わりにCT2A( strBody, CP_UTF8)と明示的にUTF-8'を指定すると正しくエンコードできると思います。
参考:文字コードの変換

追記

HttpSendRequest呼出前のnContentTypeLenのコードの意図が分からないのですが、-1を指定すればNUL終端文字列として内部で計算してくれます。
あるいは代わりにHttpAddRequestHeadersWを利用することを検討してみてください。
具体的なコード例としてはSetting Multiple Headers for HttpSendRequestが参考になると思います。

あと単なる感想ですが、URLにCT2Aを適用していたり、途中でconst char*があったり、クラス名がCMyTimerだったり、HTTP_QUERY_STATUS_CODEの戻り値として2や5を期待していたりと、全体的に危ういコードだな、と思いました。

投稿2023/11/16 08:26

編集2023/11/17 04:24
can110

総合スコア38266

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cosmori-man

2023/11/16 08:38

回答ありがとうございます。 CT2A(strBody, CP_UTF8);としてみましたが、ダメでした。 Content-Type: application/json だとAPIに到達せず、 Content-Type: application/json だとAPIに到達できる要因等は何かわかりませんでしょうか。
can110

2023/11/16 08:54

何がどうダメだったのか不明ですが、とりあえずサーバ/クライアント少なくともいずれかは正しく動くものを用意して、単純な通信内容で原因を追っていくしかないと思います。
cosmori-man

2023/11/16 09:03

CT2A(strBody,CP_UTF8) としても、Content-Type: application/json だとAPIに到達せず、 Content-Type: application/json だとAPIに到達できても文字化けは解消されておりませんでした。 >正しく動くものを用意して、単純な通信内容で原因を追っていくしかないと思います。 やはりそうですね。 引き続き検証してみます。
can110

2023/11/16 09:11

「APIに到達できない」というのが具体的にどのような現象なのか不明ですが Fidllerなどのツールを利用して、低レベルの通信データも確認したほうがよいと思います。
cosmori-man

2023/11/17 00:14

ツールの情報ありがとうございます。 使ってみます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問