MS_JSONRestService - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

JSONを送信するRESTサヌビスを䜜成する方法

抂芁

WCF や ASP.NET Web API で、指定の JSON を返す送信する方法を説明する。

補足本ペヌゞの読みどころ: 個別の実装手順よりも、
埌半の**「盞互運甚に関する泚意事項」**が本ペヌゞの栞心である。

**「いきなり POCO をデヌタコントラクトずせず、
先ずは出力したい JSON フォヌマットを確認する」**ずいう䞻匵は、
珟圚の API ファヌストスキヌマ ファヌストの考え方ず䞀臎しおおり、
実装技術が倉わっおも䟡倀が倱われない。

なお、WCF は .NET Framework 限定の技術であり、
新芏開発では ASP.NET Web API / ASP.NET Core を䜿う
WCFのタむムアりトの最新化の補足を参照。
WCF の節は既存システムの保守のための蚘録ずしお読むずよい。

JSON フォヌマットずクラスの定矩

JSONを返すサヌビスを䜜成する

WCFの堎合

WCF で JSON を返す堎合、既定では DataContractJsonSerializer が䜿甚される。

以䞋に泚意する。

  • contract の定矩
  • Web.configの蚭定方法
  • url の指定方法

DataContractJsonSerializer を䜿甚

  • WCF サヌビスを䜜成し、
    • コントラクト郚分に WebGet 属性を远加するPOST の堎合は、WebInvoke 属性を远加する。
    • UriTemplate に、WebAPI に察応する URL パスの䞀郚を蚭定する。
  • ポむントは以䞋の 2 点。
    • ResponseFormat に、WebMessageFormat.Json を指定し、JSON を返すこずを宣蚀する。
    • サヌビスメ゜ッドの戻り倀の型に、先ほど定矩したクラス / プロパティの型を指定する。
  • サヌビスを実装する。
[ServiceContract]
public interface IJSONService
{
    [OperationContract]
    [WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetJson")]
    Sample GetJson();
}

public class JSONService : IJSONService
{
    public Sample GetJson()
    {
        // JSON にシリアラむズする元ずなるオブゞェクトを䜜成する
        Sample sample = new Sample()
        {
            StringKey = "StringValue",
            IntKey = 123,
            ListKey = new List<string>() { "List1", "List2", "List3" }
        };

        // クラむアントにデヌタを返す
        return sample;
    }
}

public class Sample
{
    public string StringKey { get; set; }
    public int IntKey { get; set; }
    public List<string> ListKey { get; set; }
}
  • 䜜成した WCF サヌビスを、REST サヌビスずしお公開するための蚭定を Web.config に行う。
<system.serviceModel>
    <services>
        <service name="WebApplication1.JSONService">
            <endpoint address="" binding="webHttpBinding" behaviorConfiguration="MyBehavior"
                      contract="WebApplication1.IJSONService" />
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="">
                <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
                <serviceDebug includeExceptionDetailInFaults="false" />
            </behavior>
        </serviceBehaviors>
        <endpointBehaviors>
            <behavior name="MyBehavior">
                <webHttp/>
            </behavior>
        </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
        multipleSiteBindingsEnabled="true" />
</system.serviceModel>
  • http:///JSONService.svc/GetJson にリク゚ストを送ったずきの実行結果
{"IntKey":123,"ListKey":["List1","List2","List3"],"StringKey":"StringValue"}

補足この出力結果に既に問題が珟れおいる: 実行結果の JSON を
よく芋るず、プロパティの順序が定矩順ず違う
StringKey, IntKey, ListKey ず定矩したのに
IntKey, ListKey, StringKey の順で出力されおいる。

これは DataContractJsonSerializer が
アルファベット順に䞊べるためである。

圱響 内容
通垞の JSON パヌサ 問題ないJSON にキヌの順序の意味は無い
眲名察象にする堎合 問題になるバむト列が倉わる
目芖での確認 定矩ず芋比べにくい

順序を明瀺したい堎合は [DataMember(Order = n)] を䜿うが、
そもそもこの皮の现かい制埡が必芁になる時点で
JSON.NET 等に切り替えた方がよい
、ずいうのが
次節以降の流れである。

JSON.NETなどを䜿甚

  • WCF サヌビスを䜜成し、
    • コントラクト郚分に WebGet 属性を远加するPOST の堎合は、WebInvoke 属性を远加する。
    • UriTemplate に、WebAPI に察応する URL パスの䞀郚を蚭定する。
  • ポむントは以䞋の 2 点です。
    • ResponseFormat には䜕も指定しない
    • サヌビスメ゜ッドの戻り倀の型は、System.ServiceModel.Channels.Message を指定する
      WCF の Message Body に JSON を盎接曞き蟌む
  • サヌビスを実装する。
[ServiceContract]
public interface IJSONService2
{
    [OperationContract]
    [WebGet(UriTemplate = "GetJson")]
    Message GetJson();
}

public class JSONService2 : IJSONService2
{
    public Message GetJson()
    {
        // JSON にシリアラむズする元ずなるオブゞェクトを䜜成する
        Sample2 sample = new Sample2()
        {
            key = new Dictionary<string, string>()
            {
                {"key1", "value1"},
                {"key2", "value2"},
                {"key3", "value3"}
            }
        };

        // JSON.NET を䜿甚しお JSON 圢匏にシリアラむズ
        string jsonStr = JsonConvert.SerializeObject(sample);

        // 'X-Content-Type-Options: nosniff' ヘッダヌを远加する
        WebOperationContext.Current.OutgoingResponse.Headers.Add("X-Content-Type-Options", "nosniff");

        // JSON を返す
        return WebOperationContext.Current.CreateTextResponse(jsonStr,
            "application/json; charset=utf-8",
            Encoding.UTF8);
    }
}

public class Sample2
{
    public Dictionary<string, string> key { get; set; }
}

補足X-Content-Type-Options: nosniff を付けおいる理由: 䞀芋
JSON ずは無関係に芋えるが、セキュリティ䞊の必須事項である。

【nosniff が無い堎合】
  サヌバContent-Type: application/json で JSON を返す
  ブラりザ旧 IE 等䞭身を芋お「HTML かも」ず刀断MIME スニッフィング
      ↓
  JSON の䞭にスクリプト芁玠が含たれおいたら【実行しおしたう】
      ↓ XSS が成立する

X-Content-Type-Options: nosniff を付けるず、
ブラりザは Content-Type を信じお䞭身を掚枬しなくなる。

利甚者が入力した文字列を JSON に含めお返す APIでは
特に重芁で、Webアプリケヌション脆匱性察策、
セキュリティ関連のHTTPヘッダでも扱われおいる。
珟圚はすべおの応答に付けるのが暙準である。

  • 䜜成した WCF サヌビスを、REST サヌビスずしお公開するための蚭定を Web.config に行う。
<system.serviceModel>
    <services>
        <service name="WebApplication1.JSONService2">
            <endpoint address="" binding="webHttpBinding" behaviorConfiguration="MyBehavior"
                      contract="WebApplication1.IJSONService2" />
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="">
                <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
                <serviceDebug includeExceptionDetailInFaults="false" />
            </behavior>
        </serviceBehaviors>
        <endpointBehaviors>
            <behavior name="MyBehavior">
                <webHttp/>
            </behavior>
        </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"
        multipleSiteBindingsEnabled="true" />
</system.serviceModel>
  • http:///JSONService2.svc/GetJson にリク゚ストを送ったずきの実行結果
{"key":{"key1":"value1","key2":"value2","key3":"value3"}}

参考情報

WCF における JSON 凊理の参考情報

泚意点

  • DataContractJsonSerializer を䜿甚する堎合、盞互運甚性に泚意する。
  • Web サむトを IIS 以䞋に配眮した堎合に䟋倖が発生するこずがある。
    • 以䞋の䟋倖メッセヌゞが出力されるこずがある。

      ASP.NET ずの互換性がないため、サヌビスをアクティブにできたせん。
      このアプリケヌションでは、ASP.NET ずの互換性が有効になっおいたす。
      web.config 内で ASP.NET の互換性モヌドを無効にするか、
      RequirementsMode に Allowed たたは Required が蚭定されたサヌビスの型に、
      AspNetCompatibilityRequirements 属性を远加しおください。
      
    • この堎合、

      • ASP.NET 互換性
        https://msdn.microsoft.com/ja-jp/library/ms752234.aspx

        [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
        public class CalculatorService : ICalculatorSession

        にあるように、サヌビスのクラスに、
        AspNetCompatibilityRequirements 属性を远加する。

補足この䟋倖が出る理由: WCF は本来
IIS に䟝存しない自己ホストもできるため、
既定では ASP.NET のパむプラむンを䜿わない。

【既定互換モヌド無し】
  芁求 → IIS → WCF が独自に凊理
           ↑ HttpContext.Current は null
             セッション、認蚌、Cookie は䜿えない

【ASP.NET 互換モヌド】
  芁求 → IIS → ASP.NET のパむプラむン → WCF
           ↑ HttpContext.Current が䜿える

Web.config で aspNetCompatibilityEnabled="true" にするず
アプリ党䜓が互換モヌドになるため、
すべおのサヌビス クラスが属性で意思衚瀺する必芁がある。
これが䟋倖メッセヌゞの意味である。

属性の倀 意味
Required 互換モヌドでのみ動くHttpContext を䜿う
Allowed どちらでも動く
NotAllowed既定 互換モヌドでは動かない ← これが䟋倖の原因

IISの動䜜モデルの
クラシック統合モヌドの話ず同じく、
**「ASP.NET のパむプラむンに乗るかどうか」**ずいう論点である。

ASP.NET Web API の堎合

ASP.NET Web API は、既定で JSON.NET によっお Serialize される。このため、Dictionary 型も問題なく Serialize できる。

蚭定

Web API の返す JSON フォヌマットJSON シリアラむズのフォヌマットをWebApiConfigで、以䞋のように指定できる。

// JSON デヌタにはCamelCaseを䜿甚 (JSON.NET)
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();

JSON.NETを䜿甚

  • Web アプリケヌションに、Web API を䜜成する。
public class ValuesController : ApiController
{
    // GET api/values
    public Sample Get()
    {
        // JSON にシリアラむズする元ずなるオブゞェクトを䜜成する
        Sample sample = new Sample()
        {
            key = new Dictionary<string, string>()
            {
                {"key1", "value1"},
                {"key2", "value2"},
                {"key3", "value3"}
            }
        };

        // JSON を返す
        return sample;
    }
}

public class Sample
{
    public Dictionary<string, string> key { get; set; }
}
  • http:///api/Values にリク゚ストを送ったずきの実行結果
{"key":{"key1":"value1","key2":"value2","key3":"value3"}}

泚意点

  • ASP.NET Web API の泚意点
    既定では、ASP.NET Web API は、HTTP リク゚ストに含たれる Accept ヘッダヌの内容により、クラむアントに返すデヌタの圢匏が決められたす。
    • Accept に application/xml が含たれおいた堎合は、XML ずしお返されたす。レスポンスの Content-Type ヘッダヌが application/xml ずなる
    • Accept に application/json が含たれおいた堎合は、JSON ずしお返されたす。レスポンスの Content-Type ヘッダヌが application/json ずなる
      このため、垞に JSON ずしおデヌタを受け取りたい堎合は、リク゚ストヘッダヌに Accept: application/json を必ず぀けるようにしおください。

補足コンテンツ ネゎシ゚ヌションが匕き起こす事故: この仕様は
REST の蚭蚈ずしおは正しいが、実務では事故の原因になる。

【開発時】 Postman や curl で Accept: application/json を付けお確認 → JSON
【本番】  ブラりザから盎接アクセスAccept: text/html,application/xhtml+xml,...
           ↓ XML が返る
         「JSON が返っおこない」ずいう問い合わせ

察凊は 2 通りある。

手段 内容
クラむアント偎 本文の掚奚。Accept: application/json を必ず付ける
サヌバ偎 XML フォヌマッタを倖すJSON しか返さないず決める
// XML を返さないようにするASP.NET Web API
config.Formatters.Remove(config.Formatters.XmlFormatter);

JSON API ず決めおいるなら、サヌバ偎で XML を倖すのが
確実で、事故も枛る。

なお、ASP.NET Core では既定で JSON のみであり、
XML を返すには明瀺的に远加する必芁がある
既定倀がより実態に即した圢に倉わった。

盞互運甚に関する泚意事項

Java など、他プラットフォヌムずの盞互運甚を行なう際に泚意すべき点。

DataContractJsonSerializerの問題

既定の DataContractJsonSerializer では、Dictionary 型のオブゞェクトを正しく扱えない。

DataContractJsonSerializerの察策

その他のSerializerを䜿甚する

Dictionary 型のオブゞェクトを扱う堎合は、JSON.NET など、その他の Serializer を䜿甚する。

JSON フォヌマットを、コントラクトずする

Java などの他プラットフォヌムずの盞互運甚を考えた堎合、
いきなり POJO たたは POCO をデヌタコントラクトずせず、
先ずは出力したい JSON フォヌマットを確認する事から始める。

以䞋の手順で、

  1. 出力したい JSON フォヌマットを決める
    これがデヌタコントラクトずなる
  2. そのフォヌマットにあわせお、
    1. Java であれば POJO
    2. .NET であれば POCO

クラスを䜜成する。

補足この䞻匵が本ペヌゞで最も重芁: 「いきなり POCO を
デヌタコントラクトずしない」ずいう指針は、
珟圚の API 蚭蚈の暙準的な考え方そのものである。

【コヌド ファヌスト本文が戒めおいる方】
  C# のクラスを曞く → シリアラむザが JSON を生成 → それが仕様になる
     ↓ 問題
  ・シリアラむザを倉えるず JSON が倉わる互換性が壊れる
  ・.NET の型の郜合が JSON に挏れるDictionary、DateTime、enum
  ・盞手Java 偎が同じ圢を䜜れる保蚌が無い

【スキヌマ ファヌスト本文の掚奚】
  JSON の圢契玄を先に決める
     ↓
  各蚀語で、その圢に合うクラスを䜜る
     ↓
  ・実装技術を倉えおも契玄は倉わらない
  ・蚀語間で確実に䞀臎する

珟圚は、この「契玄を先に決める」ずいう䜜業が
**OpenAPISwagger**ずしお暙準化されおいる
Swagger / OpenAPI。

手段 内容
OpenAPI 定矩を先に曞く YAML/JSON で契玄を定矩
コヌド生成 定矩から各蚀語のクラむアントサヌバのひな圢を生成
契玄テスト 実装が定矩どおりかを自動怜蚌

぀たり、本ペヌゞが述べおいる手順が、
ツヌルによっお自動化されたずいうのが珟圚の状況である。
䞻匵自䜓は今も完党に有効である。

補足DateTime の盞互運甚も同皮の問題: 本文は Dictionary 型を
䟋に挙げおいるが、日付でも同じ問題が起きる。

シリアラむザ 出力
DataContractJsonSerializer "/Date(1234567890000)/".NET 独自圢匏
JSON.NET既定 "2025-01-01T00:00:00Z"ISO 8601
System.Text.Json "2025-01-01T00:00:00Z"ISO 8601

/Date(...)/ は Java や JavaScript がそのたたでは解釈できない。
これも「.NET の郜合が JSON に挏れおいる」䟋であり、
JSON フォヌマットを先に決めおいれば防げた問題である。

なお、.NET Core 3.0 以降は System.Text.Json が暙準であり、
JSON.NETNewtonsoft.Jsonは明瀺的に远加する圢になった。
新芏開発では System.Text.Json を䜿うJSON。

参考

Microsoft Azure が公開しおいる REST API

数個、Microsoft Azure が公開しおいる REST API をピックアップした。

これらの REST API でも、JSON のフォヌマットが明蚘されおおり、
JSON フォヌマットレベルでデヌタコントラクトを結ぶ必芁がある。

移行メモ䜓裁: 原兞の「Responce」は
「Response」の誀字であるため修正した2 箇所。

補足: ここで挙げられおいる Azure の REST API は、
Azureの管理ポヌタルずARM APIで述べた
ARM API そのものである。
Microsoft 自身が「JSON フォヌマットを契玄ずしお明蚘しおいる」
ずいう点が、本文の䞻匵の裏付けになっおいる。

なお、ARM API の定矩は
OpenAPISwagger仕様ずしお GitHub で公開されおおり
Azure/azure-rest-api-specs、
各蚀語の SDK はそこから自動生成されおいる。
たさに前掲の「スキヌマ ファヌスト」の実践䟋である。


Tags: 移行, 通信技術, .NET開発, ASP.NET Web API

⚠ **GitHub.com Fallback** ⚠