问题
I'm building a mvc application which will communicate with flash via AMF, anybody knows if there is any AMF ActionResult available on the web ?
EDIT:
using @mizi_sk answer (but without using IExternalizable) I did this ActionResult:
public class AmfResult : ActionResult
{
private readonly object _o;
public AmfResult(object o)
{
_o = o;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.ContentType = "application/x-amf";
using (var ms = new MemoryStream())
{
// write object
var writer = new AMFWriter(ms);
writer.WriteData(FluorineFx.ObjectEncoding.AMF3, _o);
context.HttpContext.Response.BinaryWrite(ms.ToArray());
}
}
}
but the response handler on the flash side doesn't get hit.
in Charles at the Response->Amf tab I see this error:
Failed to parse data (com.xk72.amf.AMFException: Unsupported AMF3 packet type 17 at 1)
this is the raw tab:
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Mon, 14 May 2012 15:22:58 GMT
X-AspNet-Version: 4.0.30319
X-AspNetMvc-Version: 3.0
Cache-Control: private
Content-Type:application/x-amf
Content-Length: 52
Connection: Close
3/bingo.model.TestRequest param1coins name
and the Hex tab:
00000000 11 0a 33 2f 62 69 6e 67 6f 2e 6d 6f 64 65 6c 2e 3/bingo.model.
00000010 54 65 73 74 52 65 71 75 65 73 74 0d 70 61 72 61 TestRequest para
00000020 6d 31 0b 63 6f 69 6e 73 09 6e 61 6d 65 01 04 00 m1 coins name
00000030 06 05 6a 6f jo
回答1:
The trick is to use FluorineFx.IO.AMFMessage
with AMFBody as a result object and set a Content
property.
You can see this in Charles proxy with other working examples (I've used great WebORB examples, specifically Flash remoting Basic Invocation AS3)
I've updated AMFFilter to support Response
parameter that AMFBody needs. Maybe it could be solved more elegantly by some current context cache, don't know.
Code follows:
public class AmfResult : ActionResult
{
private readonly object _o;
private readonly string _response;
public AmfResult(string response, object o)
{
_response = response;
_o = o;
}
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.ContentType = "application/x-amf";
var serializer = new AMFSerializer(context.HttpContext.Response.OutputStream);
var amfMessage = new AMFMessage();
var amfBody = new AMFBody();
amfBody.Target = _response + "/onResult";
amfBody.Content = _o;
amfBody.Response = "";
amfMessage.AddBody(amfBody);
serializer.WriteMessage(amfMessage);
}
}
For this to work, you need to decorate method on the controller with AMFFilter
public class AMFFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.ContentType == "application/x-amf")
{
var stream = filterContext.HttpContext.Request.InputStream;
var deserializer = new AMFDeserializer(stream);
var message = deserializer.ReadAMFMessage();
var body = message.Bodies.First();
filterContext.ActionParameters["target"] = body.Target;
filterContext.ActionParameters["args"] = body.Content;
filterContext.ActionParameters["response"] = body.Response;
base.OnActionExecuting(filterContext);
}
}
}
which would look something like this
[AMFFilter]
[HttpPost]
public ActionResult Index(string target, string response, object[] args)
{
// assume target == "TestMethod" and args[0] is a String
var message = Convert.ToString(args[0]);
return new AmfResult(response, "Echo " + message);
}
Client side code for reference
//Connect the NetConnection object
var netConnection: NetConnection = new NetConnection();
netConnection.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
netConnection.connect("http://localhost:27165/Home/Index");
//Invoke a call
var responder : Responder = new Responder( handleRemoteCallResult, handleRemoteCallFault);
netConnection.call('TestMethod', responder, "Test");
private function onNetStatus(event:NetStatusEvent):void {
log(ObjectUtil.toString(event.info));
}
private function handleRemoteCallFault(...args):void {
log(ObjectUtil.toString(args));
}
private function handleRemoteCallResult(message:String):void {
log(message);
}
private static function log(s:String):void {
trace(s);
}
If you would like to return fault, just change this line in AMFResult
amfBody.Target = _response + "/onFault";
I like ObjectUtil.toString() formatting, but just remove it if you don't have Flex linked.
BTW, do you really need this in ASP.NET MVC? Maybe simple ASHX handler would suffice and performance would be better, I don't know. The MVC architecture is a plus I presume.
回答2:
I haven't seen an ActionResult implemented on the web but there's FluorineFX.NET which supports AMF.
回答3:
AFAIK there is not any implementation of AMF ActionResult, but you can create your own using class FluorineFx.IO.AMFWriter
from FluorineFx.NET sources
Consider creating an opensource project somewhere (for example on github)
EDIT 1 - Sample
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluorineFx.IO;
using System.IO;
using FluorineFx.AMF3;
using System.Diagnostics;
namespace AMF_SerializationTest
{
class Program
{
static void Main(string[] args)
{
var ms = new MemoryStream();
// write object
var writer = new AMFWriter(ms);
writer.WriteData(FluorineFx.ObjectEncoding.AMF3, new CustomObject());
Debug.Assert(ms.Length > 0);
// rewind
ms.Position = 0;
// read object
var reader = new AMFReader(ms);
var o = (CustomObject)reader.ReadData();
// test
Debug.Assert(o.I == 3);
Debug.Assert(o.S == "Hello");
}
}
public class CustomObject : IExternalizable
{
private int i = 3;
public int I
{
get { return i; }
}
private string s = "Hello";
public string S
{
get { return s; }
}
public void ReadExternal(IDataInput input)
{
i = input.ReadInt();
s = input.ReadUTF();
}
public void WriteExternal(IDataOutput output)
{
output.WriteInt(i);
output.WriteUTF(s);
}
}
}
来源:https://stackoverflow.com/questions/9974392/amf-actionresult-for-asp-net-mvc