ただの備忘録。Azure FunctionsをバックエンドAPIとして構築してるときの話。
フロントエンドにはJSONを返してたんですが、フロントに引き渡したくないデータもあるわけでして。
例えば以下のようなクラスがあるとします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class User { public string Id { get; set; } public string Name { get; set; } public int Age { get; set; } [System.Text.Json.Serialization.JsonIgnore] public string Password { get; set; } [System.Text.Json.Serialization.JsonIgnore] public string Salt { get; set; } } |
パスワードとかはフロントに引き渡す必要ないので、JsonIgnoreつけてJsonSerialize時に出力されないようにマークすると思うのです。
で、このオブジェクトを以下のようなAzure Functionsバックエンドから返していました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
using System; using System.IO; using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace FunctionApp1 { public static class Function1 { [FunctionName("Function1")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); string id = req.Query["id"]; // 認証とかUserをデータベースから取得するとか・・・ User user = new User() { Id = id, Name = "hoge", age = 21, Password = "password", Salt = "1234567890ABCDEFG" }; // 取得したUserオブジェクトをシリアライズし、不要なフィールドは除外する string json = JsonSerializer.Serialize(user); var objectResult = new ObjectResult(json); objectResult.StatusCode = 200; objectResult.ContentTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/json")); // フロントエンドにUserオブジェクトを返す return objectResult; } } } |
ただ、このコード、問題がありまして・・・。
実はObjectResult自体がJSONに変換かけてくれるにも関わらず、その前にJsonSerializeしてしまっているのです。
なので二重にJsonSerializeされたものがフロントに渡っていたという。こんな感じで。
なんか文字列になってるし、エディタでみると変にエスケープされてたりとひっちゃかめっちゃか。
.NETのC#でSystem.Text.JsonでParseしようにも全く思い通りにならずに気付きましたw・・・。フロント側(JavaScript)ではいい感じで頭とおしりのダブルクォーテーションを取り除いてよしなにしてくれていたっぽい・・・。ありがたやw
そもそも単テの段階で気づけよとか突っ込みどころは満載なんですがそれはさておき。
じゃぁ、JsonSerializeやめればいーじゃんと。ほいで35行目のJsonSerializeをやめてObjectResultのコンストラクターの引数にUserオブジェクトのインスタンスを渡してみる。
お、今度はいい感じにJSONになってる。なってるぅ。
って、渡しちゃあかんやつ、いっちゃってる!!
これはだめだ。やっぱJsonSerializer通してIgnoreさせるのはマスト。
ってことで、どーすりゃ最小限の修正規模でコード改修できんの?って2日ほど悩み続けてしまいました。
Azure Functionsが返しているIActionResult型をHttpResponseMessage型を返すようにすればうまくいくまではたどり着いたのですが、数あるバックエンド関数の戻り値の型をすべて直すのはちょいとインパクト大。
ってことでやっぱりIActionResultインターフェースのオブジェクトがベストと判断。
でJsonResultやObjectResultでSerializeを無効にできないかとか、一度JsonSerializeしてIgnoreされたJsonをもう一回Deserializeしたのを・・・などなどありとあらゆる方法試行錯誤してみたがいまいちわからん。うまくいかない。パッとしない。
そこで白羽の矢が立ったのがタイトルにもある、ContentResultさん。
ソースコードのObjectResultをContentResultに変えたのが以下のコード。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
using System; using System.IO; using System.Text.Json; using System.Text.Encodings.Web; using System.Text.Unicode; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace FunctionApp1 { public static class Function1 { [FunctionName("Function1")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); string id = req.Query["id"]; // 認証とかUserをデータベースから取得するとか・・・ User user = new User() { Id = id, Name = "hoge", Age = 21, Password = "password", Salt = "1234567890ABCDEFG" }; // 取得したUserオブジェクトをシリアライズし、不要なフィールドは除外する string json = JsonSerializer.Serialize(user, new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // camelケース Encoder = JavaScriptEncoder.Create(UnicodeRanges.All), // 漢字可 WriteIndented = true // JSONを整形 }); // フロントエンドにUserオブジェクトを返す return new ContentResult() { ContentType = "application/json", StatusCode = 200, Content = json }; } } } |
そして、ContentResultに変更して返されたレスポンスはこちら。
おー、ばっちり!ただ、今回はJsonSerializerOptionsを指定して、返すJSONのプロパティをcamelケースに設定しています。一応、改行、漢字対応のオプションも入れてみました。
ひとまずContentResultのおかげで助かりました。僕のヒーロー、ヘビロテ決定です。
コメント