くりーむわーかー

プログラムとか。作ってて ・試しててハマった事など。誰かのお役に立てば幸いかと。 その他、いろいろエトセトラ。。。

JSON

C# HttpClientでApacheにPOST送ったら502

すごいはまった。

こんな感じで、JSONデータをPOSTするリクエスト。

string url = @"http://localhost/****";
using (var client = new HttpClient())
{
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
    request.Content = new StringContent(@"{***}", Encoding.UTF8, "application/json");
    var response = await client.SendAsync(request);
    var restext = await response.Content.ReadAsStringAsync();
    Console.WriteLine(restext);
}

そしたら、結果が↓になる。サーバはApache。

502 Proxy Error

リバースプロキシでバックエンドに飛ばしてるんですが、

Apacheのログをdebugレベルにしてログを見ると、

# access.log

"POST /hogeurl HTTP/1.1" 502 341 "- - - - - 202 Keep-Alive"

# error.log

[proxy_http:error] [pid 14412:tid 1408] (20014)Internal error (specific information not available): [client ::1:50107] AH01102: error reading status line from remote server 127.0.0.1:3002
[proxy_http:debug] [pid 14412:tid 1408] mod_proxy_http.c(1311): [client ::1:50107] AH01105: NOT Closing connection to client although reading from backend server 127.0.0.1:3002 failed.
[proxy:error] [pid 14412:tid 1408] [client ::1:50107] AH00898: Error reading from remote server returned by /hogeurl

みたいな感じ。何もわからん。

アプリ側のログには何も書かれてないのでそもそも、アプリまで到達してないっぽい。

普通のGetはちゃんと行くし、PostManで同じPOST投げてもちゃんと上手くいく。

という事は、きっと変なヘッダがついてるんだろうと。

で、C#って発行するリクエストの最終的なHeaderってどうやって見ればいいんですかね?

Apache側でログの指定ってヘッダのキー指定で一つ一つ見るしかなさそうなので、

変なヘッダ入ってないか確認するのが出来ないのですが・・・。

良いやり方ないもんか。。。

まーいいや。結論から言うと、C#のHTTPClientで発行するPOSTにはデフォで↓がつくっぽい。

Expect: 100-Continue

拡張ヘッダという事みたいですが、これが悪さしてるっぽい。というかApacheと相性悪い?

これを送らないようにするために、↓にする。

string url = @"http://localhost/****";
using (var client = new HttpClient())
{
    ServicePointManager.Expect100Continue = false;//これ!!
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
    request.Content = new StringContent(@"{***}", Encoding.UTF8, "application/json");
    var response = await client.SendAsync(request);
    var restext = await response.Content.ReadAsStringAsync();
    Console.WriteLine(restext);
}

もーね・・・。って感じ。

以下、上に行きつくまでに試した事。

まず、プロキシがおかしいのかとApacheでプロキシまわりのエラーが起きたときに

とりあえずやってみる設定は↓。

# httpd.conf

SetEnv force-proxy-request-1.0 1
SetEnv proxy-nokeepalive 1

そしたら、↓を返すようになった。

417 Expectation Failed

そのほか、POSTManで指定してるヘッダ入れてみたり色々してみて、

この417から「Expect: 100-Continue」に行き着いた感じ。

何かこー、アレな感じですね。。。

.net MVC WebApi のContent-Type

.net MVCでWebApi作ってたんですが、なんだか戻りがXMLになってしまう。Jsonで返したい。

とりあえず、Chromeで取るとデフォでXMLになるっぽい?デフォでJsonにしたい。

stackoverflowに同じ質問あった。さすが。。。

回答は色々あるけど、何となく今は↓が一番良さげ?デフォを変えるなら。

using System.Net.Http.Formatting;//RequestHeaderMapping用

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
        //デフォルトのフォーマットを変える↓
        GlobalConfiguration.Configuration.Formatters.JsonFormatter.MediaTypeMappings.Add(new RequestHeaderMapping("Accept",
                          "text/html",
                          StringComparison.InvariantCultureIgnoreCase,
                          true,
                          "application/json"));
    }
}

WebApiConfigのRegisterの中で↑を追記。

あと、ちょっと違うけど、URLの指定で最後に「~~.json」とか「~~.xml」って指定させて、フォーマットを変える方法。RedmineのAPIはこれ形式すな。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API ルート
        config.MapHttpAttributeRoutes();
        config.Formatters.JsonFormatter.AddUriPathExtensionMapping("json", "application/json");
        config.Formatters.XmlFormatter.AddUriPathExtensionMapping("xml", "application/xml");
        
        //ルートの設定で{ext}が必要
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}.{ext}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

ただ、最初のデフォルト強制する奴つけてると効かなくナリ。

あと、↓の感じでURLは最後の「/」付けないと効かなかったりする。ちょっとハマッた。Routeの設定ちゃんとやればいけるっぽいの。

/api/controller/action.json/

ついでに、Routeの設定ってみんなどうしてるんでしょ。デフォのままだと、Controllerのみだから、GetとかPostしか作れないよね?

Controller増やせばいいんだけど、ファイルいっぱいできるからあまし好きじゃない。なので、action入れてやってたりするんですが、これってありなのかしら。イマイチ分かってない。

C# Jsonをいじる

C#でJsonいじる場合、定番のライブラリがあるけど、.netの機能のみでやる。

Jsonにするクラスの定義。

using System.Runtime.Serialization.Json;
[System.Runtime.Serialization.DataContract]
public class JsonItem
{
    [System.Runtime.Serialization.DataMember()]
    public string itemkey { get; set; }
    [System.Runtime.Serialization.DataMember()]
    public string itemval { get; set; }
}

書き込み

DataContractJsonSerializer dcs = new DataContractJsonSerializer(typeof(List));
MemoryStream outms = new MemoryStream();
dcs.WriteObject(outms, newList);
outms.Position = 0;
StreamReader sr = new StreamReader(outms);
string jsonStr= sr.ReadToEnd();

読み込み

DataContractJsonSerializer dcs = new DataContractJsonSerializer(typeof(List));
MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(jsonobject_string));
List readObj = (List)dcs.ReadObject(ms);

ちょっと試したいことが色々あったんだけど、また今度。

問合せ