简体   繁体   中英

asp.net call asynchronous webMethod with Jquery

I'm trying to create an asp application, where I can write a departure and a destination city. I would like to implement the google AutoComplete on this textbox.

I've developed async method which return the autocomplete function of google when you insert a letter or more called 'getAutoComplete'.

Now I want to access it from my javascript, with an ajax request.

here is my code for the aspx

<%@ Page Async="true" Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="googleApiWeb._Default" %>
<asp:Content runat="server" ID="BodyContent" ContentPlaceHolderID="MainContent">
<ol class="round">

    <div id="DEPARTURE_CITY" style="margin-top: 15px; margin-left: 15px;">
        <asp:Label ID="lblDepartureCity" runat="server" Text="Ville de départ"></asp:Label>
        <asp:TextBox runat="server" ID="txbDepartureCity" ClientIDMode="Static" Style="width: 150px;"></asp:TextBox>
    </div>
    <div id="DESTINATION_CITY" style="margin-top: 15px; margin-left: 15px;">
        <asp:Label ID="lblDestinationCity" runat="server" Text="Ville d'arrivé"></asp:Label>
        <asp:TextBox runat="server" ID="txbDestinationCity" ClientIDMode="Static" Style="width: 150px;"></asp:TextBox>
    </div>
    <asp:Button ID="buttonValider" runat="server" Text="Valider" ClientIDMode="Static" OnClick="buttonValider_Click" />
    <asp:Label ID="lbl1" runat="server" Text=""></asp:Label>
    <asp:Label ID="lbl2" runat="server" Text=""></asp:Label>
</ol>
<script type="text/javascript">
    $(document).ready(function () {
        //Autocomplete
        $("#txbDepartureCity").bind("change paste keyup", function () {
            $.ajax({
                type: "POST",
                url: "/Default.aspx/getAutoComplete",
                data: "{'Element':'" + $(this).val() + "'}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (response) {
                    alert(response.d);
                }
            });
        });

        $("#txbDestinationCity").bind("change paste keyup", function () {
            $.ajax({
                type: "POST",
                url: "/Default/googleAutoComplete",
                data: "{'Element':'" + $(this).val() + "'}",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (response) {
                    if (response.d == "NOK") {
                        //errorResponseAlert();
                    }
                }
            });


        });
    });
</script>

Now on my code behind

namespace googleApiWeb
{
public partial class _Default : Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    #region WebMethod autocomplete GoogleApi

    [WebMethod(EnableSession = true)]
    public static string googleAutoComplete(string Element)
    {
        string x = getAutoComplete(Element);

        return "";
    }

    [WebMethod(EnableSession = true)]
    private static async Task<string> getAutoComplete(string Element)
    {
        // Adresse à compléter
        string location = Element;

        // Message d'erreur s'il y en a
        string error = "";

        // Créer une instance d'auto complete
        AutoComplete autoComplete = new AutoComplete();

        // Options de l'auto complete
        AutoCompleteOptions options = new AutoCompleteOptions()
        {
            // Obtenir les résultats en français
            Language = LanguageEnum.French,

            // Pays dans lequel vous souhaitez limiter vos résultats
            // Attention : format ISO 3166-1 : "us", "fr", "de", ...
            CountryFilter = "fr",

            // Type de résultats à retourner.
            // Ex : Regions retourne le code postal, ce que ne fait pas Cities.
            // Si cette option est vide, tous les résultats sont retournés.
            TypeFilter = TypeFilterEnum.Regions
        };

        // Clé API
        string key = "XXXXXXX";

        // Appel asynchrone de la méthode de la dll

        var listOfLocation = await autoComplete.GetAutoComplete(location, key, null).ContinueWith(t =>
        {
            // S'il y a une erreur
            if (t.IsFaulted)
            {
                // Message de l'exception
                error = t.Exception.InnerException.Message;

                return null;
            }

            // Résultat
            return t.Result;
        });

        var result = listOfLocation.First();

        return result;
    }

    #endregion

    protected void buttonValider_Click(object sender, EventArgs e)
    {
        GetTrip();
        getAutoComplete(txbDepartureCity.Text);
    }
}
}

and the error is POST http://localhost:51460/Default.aspx/getAutoComplete 500 (Internal Server Error)

How can I manage this, in order to get on the ajax success function the result of my method. The aim of this, is to obtain the result with no callback.

Thank you

Ok, so I figured out how to solve this. In order to call in ajax a web method, the web method have to return a string. So this method cannot be async.

I added this class on my code behind

public static class AsyncHelpers
{
    /// <summary>
    /// Execute's an async Task<T> method which has a void return value synchronously
    /// </summary>
    /// <param name="task">Task<T> method to execute</param>
    public static void RunSync(Func<Task> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        synch.Post(async _ =>
        {
            try
            {
                await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();

        SynchronizationContext.SetSynchronizationContext(oldContext);
    }

    /// <summary>
    /// Execute's an async Task<T> method which has a T return type synchronously
    /// </summary>
    /// <typeparam name="T">Return Type</typeparam>
    /// <param name="task">Task<T> method to execute</param>
    /// <returns></returns>
    public static T RunSync<T>(Func<Task<T>> task)
    {
        var oldContext = SynchronizationContext.Current;
        var synch = new ExclusiveSynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(synch);
        T ret = default(T);
        synch.Post(async _ =>
        {
            try
            {
                ret = await task();
            }
            catch (Exception e)
            {
                synch.InnerException = e;
                throw;
            }
            finally
            {
                synch.EndMessageLoop();
            }
        }, null);
        synch.BeginMessageLoop();
        SynchronizationContext.SetSynchronizationContext(oldContext);
        return ret;
    }

    private class ExclusiveSynchronizationContext : SynchronizationContext
    {
        private bool done;
        public Exception InnerException { get; set; }
        readonly AutoResetEvent workItemsWaiting = new AutoResetEvent(false);
        readonly Queue<Tuple<SendOrPostCallback, object>> items =
            new Queue<Tuple<SendOrPostCallback, object>>();

        public override void Send(SendOrPostCallback d, object state)
        {
            throw new NotSupportedException("We cannot send to our same thread");
        }

        public override void Post(SendOrPostCallback d, object state)
        {
            lock (items)
            {
                items.Enqueue(Tuple.Create(d, state));
            }
            workItemsWaiting.Set();
        }

        public void EndMessageLoop()
        {
            Post(_ => done = true, null);
        }

        public void BeginMessageLoop()
        {
            while (!done)
            {
                Tuple<SendOrPostCallback, object> task = null;
                lock (items)
                {
                    if (items.Count > 0)
                    {
                        task = items.Dequeue();
                    }
                }
                if (task != null)
                {
                    task.Item1(task.Item2);
                    if (InnerException != null) // the method threw an exeption
                    {
                        throw new AggregateException("AsyncHelpers.Run method threw an exception.", InnerException);
                    }
                }
                else
                {
                    workItemsWaiting.WaitOne();
                }
            }
        }

        public override SynchronizationContext CreateCopy()
        {
            return this;
        }
    }

And now in my webmethod

    [WebMethod(EnableSession = true)]
    public static string googleAutoComplete(string Element)
    {
        string result = "";

        List<string> lstReturnGoogle = AsyncHelpers.RunSync<List<string>>(() => getAutoComplete(Element));
        if (lstReturnGoogle != null)
        {
            result = "[";

            foreach (string t in lstReturnGoogle)
            {
                result += '"' + t + '"' + ',';
            }
            result = result.Remove(result.Length - 1, 1);
            result += "]";
            result.Replace('\\', ' ');
        }

        return result;
    }

With this, I can call my Webmethod in ajax, and have the correct response.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM