ASP.NET MVC – Create easy REST API with JSON and XML(转)

转载 2011年01月19日 18:11:00

ASP.NET MVC – Create easy REST API with JSON and XML

From: http://aleembawany.com/2009/03/27/aspnet-mvc-create-easy-rest-api-with-json-and-xml/

 

Mar 27th, 2009

So I just hopped on the ASP.NET MVC bandwagon. As my first task, I undertook custom Action Filters for returning either JSON or XML as determined by the HTTP Request. Fortunately Omar AL Zabir did most of the work for creating a RESTful API with ASP.NET MVC.

JSON and XML Action Filter Code

The following is a filter which makes the whole thing much cleaner. The filter looks for Content-Type headers in the HTTP request. If it matches text/xml then Plain Old XML (POX) is returned and if it matches application/json the output is JSON. This eliminates the need to write separate actions for JSON/XML and Views.


using
 
System
;
 
using System . Web ;  
using System . Web . Mvc ;  
using System . IO ;  
using System . Xml ;  
using System . Text ;  
using System . Collections . Generic ;  
using System . Web . Script . Serialization ;  
using System . Runtime . Serialization ;  
using System . Reflection ;  
using System . Reflection . Emit ;  
 
namespace AleemBawany . ActionFilters  
{  
 
public class JsonPox : ActionFilterAttribute  
 
{  
   
private String [] _actionParams ;  
 
   
// for deserialization  
   
public JsonPox ( params String [] parameters )  
   
{  
     
this . _actionParams = parameters ;  
   
}  
 
   
// SERIALIZE  
   
public override void OnActionExecuted ( ActionExecutedContext filterContext )  
   
{  
     
if (!( filterContext . Result is ViewResult )) return ;  
 
     
// SETUP  
      UTF8Encoding utf8
= new UTF8Encoding ( false );  
     
HttpRequestBase request = filterContext . RequestContext . HttpContext . Request ;  
     
String contentType = request . ContentType ?? string . Empty ;  
     
ViewResult view = ( ViewResult )( filterContext . Result );  
     
var data = view . ViewData . Model ;  
 
     
// JSON  
     
if ( contentType . Contains ( "application/json" ) || request . IsAjaxRequest ())  
     
{  
       
using ( var stream = new MemoryStream ())  
       
{  
         
JavaScriptSerializer js = new JavaScriptSerializer ();  
 
         
String content = js . Serialize ( data );  
          filterContext
. Result = new ContentResult  
         
{  
           
ContentType = "application/json" ,  
           
Content = content ,  
           
ContentEncoding = utf8 
         
};  
       
}  
 
     
}  
 
     
// POX  
     
else if ( contentType . Contains ( "text/xml" ))  
     
{  
       
// MemoryStream to encapsulate as UTF-8 (default UTF-16)  
       
// http://stackoverflow.com/questions/427725/  
       
//  
       
// MemoryStream also used for atomicity but not here  
       
// http://stackoverflow.com/questions/486843/  
       
using ( MemoryStream stream = new MemoryStream ( 500 ))  
       
{  
         
using ( var xmlWriter =  
           
XmlTextWriter . Create ( stream ,  
             
new XmlWriterSettings ()  
             
{  
               
OmitXmlDeclaration = true ,  
               
Encoding = utf8 ,  
               
Indent = true  
             
}))  
         
{  
 
           
new DataContractSerializer (  
              data
. GetType (),  
             
null , // knownTypes  
             
65536 , // maxItemsInObjectGraph  
             
false , // ignoreExtensionDataObject  
             
true , // preserveObjectReference - overcomes cyclical reference issues  
             
null // dataContractSurrogate  
             
). WriteObject ( stream , data );  
         
}  
 
          filterContext
. Result = new ContentResult  
         
{  
           
ContentType = "text/xml" ,  
           
Content = utf8 . GetString ( stream . ToArray ()),  
           
ContentEncoding = utf8 
         
};  
       
}  
     
}  
   
}  
 
   
// DESERIALIZE  
   
public override void OnActionExecuting ( ActionExecutingContext filterContext )  
   
{  
 
     
if ( _actionParams == null || _actionParams . Length == 0 ) return ;  
 
     
HttpRequestBase request = filterContext . RequestContext . HttpContext . Request ;  
     
String contentType = request . ContentType ?? string . Empty ;  
     
Boolean isJson = contentType . Contains ( "application/json" );  
 
     
if (! isJson ) return ;  
     
//@@todo Deserialize POX  
 
     
// JavascriptSerialier expects a single type to deserialize  
     
// so if the response contains multiple disparate objects to deserialize  
     
// we dynamically build a new wrapper class with fields representing those  
     
// object types, deserialize and then unwrap  
     
ParameterDescriptor [] paramDescriptors =  
          filterContext
. ActionDescriptor . GetParameters ();  
     
Boolean complexType = paramDescriptors . Length > 1 ;  
 
     
Type wrapperClass ;  
     
if ( complexType )  
     
{  
       
Dictionary parameterInfo = new Dictionary ();  
       
foreach ( ParameterDescriptor p in paramDescriptors )  
       
{  
          parameterInfo
. Add ( p . ParameterName , p . ParameterType );  
       
}  
        wrapperClass
= BuildWrapperClass ( parameterInfo );  
     
}  
     
else  
     
{  
        wrapperClass
= paramDescriptors [ 0 ]. ParameterType ;  
     
}  
 
     
String json ;  
     
using ( var sr = new StreamReader ( request . InputStream ))  
     
{  
        json
= sr . ReadToEnd ();  
     
}  
 
     
// then deserialize json as instance of dynamically created wrapper class  
     
JavaScriptSerializer serializer = new JavaScriptSerializer ();  
     
var result = typeof ( JavaScriptSerializer )  
             
. GetMethod ( "Deserialize" )  
             
. MakeGenericMethod ( wrapperClass )  
             
. Invoke ( serializer , new object [] { json });  
 
     
// then get fields from wrapper class assign the values back to the action params  
     
if ( complexType )  
     
{  
       
for ( Int32 i = 0 ; i < paramDescriptors . Length ; i ++)  
       
{  
         
ParameterDescriptor pd = paramDescriptors [ i ];  
          filterContext
. ActionParameters [ pd . ParameterName ] =  
              wrapperClass
. GetField ( pd . ParameterName ). GetValue ( result );  
 
       
}  
     
}  
     
else  
     
{  
       
ParameterDescriptor pd = paramDescriptors [ 0 ];  
        filterContext
. ActionParameters [ pd . ParameterName ] = result ;  
     
}  
   
}  
 
   
private Type BuildWrapperClass ( Dictionary parameterInfo )  
   
{  
     
AssemblyName assemblyName = new AssemblyName ();  
      assemblyName
. Name = "DynamicAssembly" ;  
     
AppDomain appDomain = AppDomain . CurrentDomain ;  
     
AssemblyBuilder assemblyBuilder =  
          appDomain
. DefineDynamicAssembly ( assemblyName , AssemblyBuilderAccess . Run );  
     
ModuleBuilder moduleBuilder =  
          assemblyBuilder
. DefineDynamicModule ( "DynamicModule" );  
     
TypeBuilder typeBuilder =  
          moduleBuilder
. DefineType ( "DynamicClass" ,  
         
TypeAttributes . Public | TypeAttributes . Class );  
 
     
foreach ( KeyValuePair entry in parameterInfo )  
     
{  
       
String paramName = entry . Key ;  
       
Type paramType = entry . Value ;  
       
FieldBuilder field = typeBuilder . DefineField ( paramName ,  
                    paramType
, FieldAttributes . Public );  
     
}  
 
     
Type generatedType = typeBuilder . CreateType ();  
     
// object generatedObject = Activator.CreateInstance(generatedType);  
 
     
return generatedType ;  
   
}  
 
 
}  
}  

Last Updated: 28 March, 2010

Usage Example

To use this code in your Controller Action, you simply need to decorate it with the [JsonPox] attribute:

// Depending on HTTP Content-Type header
 
// this returns JSON, XML or the default View  
 
[ JsonPox ]  
public ActionResult Index ()  
{  
       
ArrayList stuff = new ArrayList ();  
        stuff
. Add ( "Hello World" );  
        stuff
. Add ( 999 );  
        stuff
. Add ( 1.0001 );  
       
ViewData . Model = stuff ;  
       
return View ();  
}  

Sample Output

If Content-Type: text/xml HTTP header is present the output is:


<
A
rrayOfAnyType
 
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
 
xmlns:xsd
=
"http://www.w3.org/2001/XMLSchema"
>
 
 
<anyType xsi:type = "xsd:string" > Hello World </anyType>  
 
<anyType xsi:type = "xsd:int" > 999 </anyType>  
 
<anyType xsi:type = "xsd:double" > 1.0001 </anyType>  
</ A rrayOfAnyType >  

For the HTTP header Content-Type: application/json the output is:

[
"Hello World"
,
999
,
1.0001
]
 

And if neither of those headers are present, the default View is returned.

Latest version: If you intend to use it, get the latest bits for JsonPoxFilter.cs here . The source is open so feel free to fork it or ping me if you want to get write access to the repo.

REST on ASP.Net MVC (and Future)

MVC和REST是两种不同的世界观. 前者更多的是对行为的建模,后者则更强调数据(状态及状态的变迁). 前者给出的是内部实现方面的指导, 把程序结构分离为Model, View及Controller....

ASP.NET MVC 3: Layouts and Sections with Razor

[转自] http://weblogs.asp.net/scottgu/archive/2010/12/30/asp-net-mvc-3-layouts-and-sections-with-razor...
  • ccgang
  • ccgang
  • 2012年06月29日 18:43
  • 953

Server-Side Paging with the Entity Framework and ASP.NET MVC 3

Julie Lerman Download the Code Sample In my February Data Points column, I showed off the jQuer...

Upload files in ASP.NET MVC with JavaScript and C#

GOOGLE的地址被封了,转过来,方便需要的人 https://cmatskas.com/upload-files-in-asp-net-mvc-with-javascript-and-c/ ...
  • Ani
  • Ani
  • 2016年05月18日 21:05
  • 474

ASP.NET MVC with Entity Framework and CSS

  • 2017年09月25日 17:03
  • 29.94MB
  • 下载

Asp.Net MVC及Web API框架配置会碰到的几个问题及解决方案(转)

前言 刚开始创建MVC与Web API的混合项目时,碰到好多问题,今天拿出来跟大家一起分享下。有朋友私信我问项目的分层及文件夹结构在我的第一篇博客中没说清楚,那么接下来我就准备从这些文件怎么分文...
  • xwnxwn
  • xwnxwn
  • 2015年01月27日 17:17
  • 526

Consuming ASP.NET WEB API using ASP.NET MVC4 and RestSharp

原文:http://www.codeproject.com/Articles/826359/Consuming-ASP-NET-WEB-API-using-ASP-NET-MVC-and-Re 相关...
  • Joyhen
  • Joyhen
  • 2015年03月16日 02:21
  • 1744
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:ASP.NET MVC – Create easy REST API with JSON and XML(转)
举报原因:
原因补充:

(最多只允许输入30个字)