关闭

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

1638人阅读 评论(1) 收藏 举报

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.

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:92869次
    • 积分:1337
    • 等级:
    • 排名:千里之外
    • 原创:35篇
    • 转载:43篇
    • 译文:0篇
    • 评论:4条
    文章分类