سؤال

أحاول تقديم واجهة برمجة تطبيقات مريحة بسيطة إلى مشروع ASP MVC الخاص بي. لن يكون لدي سيطرة على عملاء واجهة برمجة التطبيقات هذه، وسوف يمر XML عبر طريقة مرحلة ما بعد الاستفادة من المعلومات اللازمة لتنفيذ بعض الإجراءات على جانب الخادم وتوفير XML مع نتيجة الإجراء. ليس لدي مشاكل في إرسال XMLS مرة أخرى، فإن المشكلة تتلقى XML عبر منشور. لقد رأيت بعض أمثلة JSON، ولكن نظرا لأنني لن أتحكم في عملائلي (قد يكون حتى telnet من وجهة نظري) لا أعتقد أن JSON سيعمل. هل انا صائب؟

لقد رأيت أمثلة حيث يقوم العملاء ببساطة ببساطة ببساطة بتنسيق النموذج الصحيح كجزء من نص الطلب ثم تحليل ASP للرسالة، وتتوفر البيانات كإشتراك (؟ Param1 = Value1¶m2 = Value2 &، إلخ). ومع ذلك، أريد أن تمر XML نقية كجزء من جسم الرسالة.

شكرا لمساعدتك،

هل كانت مفيدة؟

المحلول 2

يمكن تحقيق ذلك باستخدام ActionFilterTattribute. يتقاطع مرشحات العمل بشكل أساسي الطلب قبل أو بعد نتيجة الإجراء. لذلك قمت فقط ببناء سمة مرشح الحركة المخصصة لنتيجة عمل آخر. هنا هو ما فعلته:

public class RestAPIAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpContextBase httpContext = filterContext.HttpContext;
        if (!httpContext.IsPostNotification)
        {
            throw new InvalidOperationException("Only POST messages allowed on this resource");
        }
        Stream httpBodyStream = httpContext.Request.InputStream;

        if (httpBodyStream.Length > int.MaxValue)
        {
            throw new ArgumentException("HTTP InputStream too large.");
        }

        int streamLength = Convert.ToInt32(httpBodyStream.Length);
        byte[] byteArray = new byte[streamLength];
        const int startAt = 0;

        /*
         * Copies the stream into a byte array
         */
        httpBodyStream.Read(byteArray, startAt, streamLength);

        /*
         * Convert the byte array into a string
         */
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < streamLength; i++)
        {
            sb.Append(Convert.ToChar(byteArray[i]));
        }

        string xmlBody = sb.ToString();

        //Sends XML Data To Model so it could be available on the ActionResult

        base.OnActionExecuting(filterContext);
    }
}

ثم في طريقة نتيجة الإجراء على وحدة التحكم الخاصة بك، يجب عليك القيام بشيء مثل هذا:

    [RestAPIAttribute]
    public ActionResult MyActionResult()
    {
        //Gets XML Data From Model and do whatever you want to do with it
    }

آمل أن يساعد ذلك شخص آخر، إذا كنت تعتقد أن هناك طرق أكثر أناقة للقيام بذلك، فأعلمني بذلك.

نصائح أخرى

@ Freddy - أحب نهجك وتحسينه مع التعليمات البرمجية التالية لتبسيط قراءة الدفق:

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpContextBase httpContext = filterContext.HttpContext;
        if (!httpContext.IsPostNotification)
        {
            throw new InvalidOperationException("Only POST messages allowed on this resource");
        }

        Stream httpBodyStream = httpContext.Request.InputStream;
        if (httpBodyStream.Length > int.MaxValue)
        {
            throw new ArgumentException("HTTP InputStream too large.");
        }

        StreamReader reader = new StreamReader(httpBodyStream, Encoding.UTF8);
        string xmlBody = reader.ReadToEnd();
        reader.Close();

        filterContext.ActionParameters["message"] = xmlBody;

        // Sends XML Data To Model so it could be available on the ActionResult
        base.OnActionExecuting(filterContext);
    }

ثم في وحدة التحكم، يمكنك الوصول إلى XML كسلسلة:

[RestAPIAttribute]    
public ActionResult MyActionResult(string message)    
{         

}

أعلم أنه يمكنك إنشاء مصنع موفر قيمة مخصص. سيتيح لك ذلك أيضا التحقق من صحة النماذج الخاصة بك عند نشرها قبل محاولة حفظها. فيل هاك لديه وظيفة بلوق حول نسخة JSON من هذا المفهوم نفسه. المشكلة الوحيدة هي أنني لا أعرف كيفية تنفيذ واحد هذا النوع من الشيء من XML.

المنظمة البحرية الدولية هي أفضل طريقة لإنجازها هي كتابة مزود قيمة مخصص، وهذا هو مصنع يعالج تعيين الطلب في قاموس النماذج. أنت لا ترث فقط من ValideProviderFactory والتعامل مع الطلب إذا كان من النوع "النص / XML" أو "تطبيق / XML".

مزيد من المعلومات:

فيل هاك

مدونتي

MSDN.

protected override void OnApplicationStarted()
{
    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);

    ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());
    ValueProviderFactories.Factories.Add(new XmlValueProviderFactory());
}

xmlvalueprovider.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Web.Mvc;
using System.Xml;
using System.Xml.Linq;

public class XmlValueProviderFactory : ValueProviderFactory
{

    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        var deserializedXml = GetDeserializedXml(controllerContext);

        if (deserializedXml == null) return null;

        var backingStore = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

        AddToBackingStore(backingStore, string.Empty, deserializedXml.Root);

        return new DictionaryValueProvider<object>(backingStore, CultureInfo.CurrentCulture);

    }

    private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, XElement xmlDoc)
    {
        // Check the keys to see if this is an array or an object
        var uniqueElements = new List<String>();
        var totalElments = 0;
        foreach (XElement element in xmlDoc.Elements())
        {
            if (!uniqueElements.Contains(element.Name.LocalName))
                uniqueElements.Add(element.Name.LocalName);
            totalElments++;
        }

        var isArray = (uniqueElements.Count == 1 && totalElments > 1);


        // Add the elements to the backing store
        var elementCount = 0;
        foreach (XElement element in xmlDoc.Elements())
        {
            if (element.HasElements)
            {
                if (isArray)
                    AddToBackingStore(backingStore, MakeArrayKey(prefix, elementCount), element);
                else
                    AddToBackingStore(backingStore, MakePropertyKey(prefix, element.Name.LocalName), element);
            }
            else
            {
                backingStore.Add(MakePropertyKey(prefix, element.Name.LocalName), element.Value);
            }
            elementCount++;
        }
    }


    private static string MakeArrayKey(string prefix, int index)
    {
        return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
    }

    private static string MakePropertyKey(string prefix, string propertyName)
    {
        if (!string.IsNullOrEmpty(prefix))
            return prefix + "." + propertyName;
        return propertyName;
    }

    private XDocument GetDeserializedXml(ControllerContext controllerContext)
    {
        var contentType = controllerContext.HttpContext.Request.ContentType;
        if (!contentType.StartsWith("text/xml", StringComparison.OrdinalIgnoreCase) &&
            !contentType.StartsWith("application/xml", StringComparison.OrdinalIgnoreCase))
            return null;

        XDocument xml;
        try
        {
            var xmlReader = new XmlTextReader(controllerContext.HttpContext.Request.InputStream);
            xml = XDocument.Load(xmlReader);
        }
        catch (Exception)
        {
            return null;
        }

        if (xml.FirstNode == null)//no xml.
            return null;

        return xml;
    }
}

لماذا لا يمكنهم اجتياز XML كسلسلة في منشور النموذج؟

مثال:

public ActionResult SendMeXml(string xml)
{
  //Parse into a XDocument or something else if you want, and return whatever you want.
  XDocument xmlDocument = XDocument.Parse(xml);

  return View();
}

يمكنك إنشاء منشور نموذج وإرساله في حقل نموذج واحد.

أنا أحب الإجابة من freddy والتحسين من bowbowerm. إنه موجز ويحافظ على تنسيق الإجراءات القائمة على النماذج.

لكن فحص ISPostnotification لن يعمل في رمز الإنتاج. لا يتحقق من الفعل HTTP حيث يبدو أن رسالة الخطأ تنطوي عليها، ويتم تجريدها من سياق HTTP عند تعيين علامة تصحيح التحديد إلى FALSE. هذا موضح هنا:httpcontext.ispostnotification هو خطأ عند تصحيح التجميع هو خطأ

آمل أن يحفظ هذا شخص ما يوم 1/2 من طرق تصحيح الأخطاء بسبب هذه المشكلة. هنا هو الحل دون هذا الاختيار:

public class XmlApiAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        HttpContextBase httpContext = filterContext.HttpContext;
        // Note: for release code IsPostNotification stripped away, so don't check it!
        // https://stackoverflow.com/questions/28877619/httpcontext-ispostnotification-is-false-when-compilation-debug-is-false            

        Stream httpBodyStream = httpContext.Request.InputStream;
        if (httpBodyStream.Length > int.MaxValue)
        {
            throw new ArgumentException("HTTP InputStream too large.");
        }

        StreamReader reader = new StreamReader(httpBodyStream, Encoding.UTF8);
        string xmlBody = reader.ReadToEnd();
        reader.Close();

        filterContext.ActionParameters["xmlDoc"] = xmlBody;

        // Sends XML Data To Model so it could be available on the ActionResult
        base.OnActionExecuting(filterContext);
    }
}
...
public class MyXmlController 
{ ...
    [XmlApiAttribute]
    public JsonResult PostXml(string xmlDoc)
    {
...

لطيف - جيد!،

ما الكائن الذي حصلت عليه في طريقة وحدة التحكم الخاصة بي لمعالجة XML؟

أنا أستخدم بهذه الطريقة:

على ActionFilter، قمت بملء النموذج مع:

        .
        .

        string xmlBody = sb.ToString();

        filterContext.Controller.ViewData.Model = xmlBody;

وعلى طريقة جهاز التحكم الخاصة بي، أحصل على النموذج على النحو التالي:

        string xmlUserResult = ViewData.Model as string;

        XmlSerializer ser = new XmlSerializer(typeof(UserDTO));
        StringReader stringReader = new StringReader(xmlUserResult);
        XmlTextReader xmlReader = new XmlTextReader(stringReader);
        UserDTO userToUpdate = ser.Deserialize(xmlReader) as UserDTO;
        xmlReader.Close();
        stringReader.Close();

هل هذا تنفيذ صحيح؟

شكرا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top