しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Twitter API のように拡張子で出力フォーマットを変えてみる

http://api.twitter.com/1/statuses/home_timeline.xml

みたいなやつです。xml を json に変えると json になります。

元々はたけはらさんの Jsonable 属性を見て思ったんですけど、ルーティングを定義して昔作った XmlResult を使ったらいい感じになりました。

とりあえずさっくりとルーティングを定義します。

routes.MapRoute(
    "API",
    "api/{action}.{format}",
    new { controller = "api" }
);

非常に簡単ですね。ぶっちゃけ説明要らないレベルですけど、これで

/api/product.xml

というような URL にマッチするようになります。

そしてコントローラを作ります。

public class ApiController : Controller
{
    [OutputFormat]
    public ActionResult Product()
    {
        var model = new Product
        {
            Id = 10,
            Name = "かなり凄い商品",
            Price = 1980,
            Count = 10,
        };
        return View(model);
    }
}

普通に View メソッドを読んで ViewResult を返しています。OutputFormat という属性が付いていますが、これが今回のキモです。

それでは OutputFormat 属性のソースコードをぺたり。

public class OutputFormatAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var viewResult = filterContext.Result as ViewResult;

        switch (filterContext.RouteData.GetRequiredString("format"))
        {
            case "json":
                filterContext.Result = new JsonResult { Data = viewResult.Model, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
                break;

            case "xml":
                filterContext.Result = new XmlResult { Data = viewResult.Model };
                break;
        }

        base.OnActionExecuted(filterContext);
    }
}

やっていることは Jsonable 属性とほぼ一緒で、今回の奴はルートデータからフォーマットを引っ張ってきているだけです。XmlResult は JsonValueProviderFactory と JsonResult の XML 版を作ってみた - しばやん雑記 にあります。

/api/product.xml にアクセスした場合には以下のような XML が返ってきます。

<?xml version="1.0" encoding="utf-8"?>
<Product xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Id>10</Id>
  <Name>かなり凄い商品</Name>
  <Price>1980</Price>
  <Count>10</Count>
</Product>

/api/product.json にアクセスした場合には以下のような JSON が返ってきます。

{"Id":10,"Name":"かなり凄い商品","Price":1980,"Count":10}

ルーティングが柔軟だからこそ出来る芸当という感じですね。