locked
$.ajax error callback question RRS feed

  • Question

  • User-1104215994 posted

    Hi,

    I am trying to get an error message from the controller action and display it on the razor page. Somehow I am getting 500 Internal Server Error instead of 429 There is no product code.

    Here is my related javascript where I alert error message on razor page.

    var serviceUrl = '/GameBanks/PinAsync?quantity=' + quantity + '&game=' + selText + '&email=' + email + '&prc=' + price;
                                $.ajax({
                                    type: "GET",
                                    url: serviceUrl,
                                    dataType: 'json',
                                    success: function (data) {
                                        //alert(JSON.stringify(data));
                                        ShowResult(data);
                                        $("#spinner").remove();
                                        $("#btn_add").html('Add');
                                   
                                    }, error: function (xhr, status, error) {
                                        $("#spinner").remove();
                                        $("#btn_add").html('Add');
                                        var errorMessage = xhr.status + ': ' + xhr.statusText + ': ' + status + ': ' + error;
    
                                        alert('Error - ' + errorMessage);
                                    }
    
                                });

    Here is my controller action which returns error:

    [HttpGet]
            public async Task<IActionResult> PinAsync(int quantity, string game, string email, int prc)
            {
                var price = 0;
                try
                {
                    string result = null;
                    var rnd = new Random();
                    var dList = new DemoList();
    
                    if (prc > 0)
                    {
                        price = prc;
                    }
    
                    var games = new Game {Product = game, Quantity = quantity};
    
                    var content = await games.CallGameAsync("Palas", "12345", game, quantity);
                    var deserializedResult = JObject.Parse(content);
    
                    result = result + "Serial: "+ (string)deserializedResult["coupons"]?[0]?["Serial"] + "\tPin: " + (string)deserializedResult["coupons"]?[0]?["Pin"] +"\n";
                    dList.gameList = result;
    
                    
                    return new JsonResult(dList);
                }
                catch (Exception e)
                {
                    var timestamp = DateTime.UtcNow;
                    //NLOG
                    NLog(logger2, "Test " + e.Message, timestamp, 0);
                    throw new Exception(e.Message);
                }
            }

    On the controller catch, I am throwing 429 There is no product code but on javascript side error displayed is 500 Internal Server. How can I fix this?

    Monday, August 3, 2020 10:27 AM

All replies

  • User475983607 posted

    I do not see any code that returns a "429" status!   Please take a few seconds to run your code through the debugger and make sure you are not making assumptions.

    Monday, August 3, 2020 10:51 AM
  • User-1104215994 posted

    Is it OK for you?

    https://ibb.co/H2Ggpg3

    Monday, August 3, 2020 11:02 AM
  • User475983607 posted

    cenk1536

    Is it OK for you?

    https://ibb.co/H2Ggpg3

    You made an assumption.

    The 429 is an HTTP response from REST call made from the PinAsync action.  The code catches HTTP error in the catch block then throws a new exception.  This causes the PinAsync method to returns a 500 status with a 429 error message.  The browser's dev tools shows the HTTP response and all you had to do is look. 

    As a peer review, I recommend returning an 429 HTTP status if that's what the client expects.  Re-throwing an exception wastes resources. Tink about it, the  framework has to write the entire stack trace.  Also the design double serializes the response.

    Monday, August 3, 2020 11:30 AM
  • User-1104215994 posted

    I changed it to throw, e.Message is 429 Invalid Product code. How can I display it on the alert? I don't get it.

    Monday, August 3, 2020 12:42 PM
  • User475983607 posted

    I changed it to throw, e.Message is 429 Invalid Product code. How can I display it on the alert? I don't get it.

    It is up you to share the code you wanted reviewed.  Clearly, the community cannot comment on code that we cannot see.

    Monday, August 3, 2020 1:11 PM
  • User-1104215994 posted

    Here is the razor:

    @{
        ViewData["Title"] = "OyunPalas";
    }
    
    <h2 class="text-center font-weight-bolder mb-2" style="margin-bottom: 3.5rem !important">OyunPalas Online E-Pin Purchase</h2>
    <!DOCTYPE html>
    
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>OyunPalas</title>
    
        <script src="~/lib/jquery/jquery.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
        <script src="~/lib/bootstrap/js/bootstrap.js"></script>
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.css" />
        <link href="~/lib/bootstrap/css/bootstrap.css" rel="stylesheet" />
    
    </head>
    <style>
        div.hidden
        {
            display: none
        }
    </style>
    
    <form class="needs-validation" novalidate>
        <div id="spinner-map-right-click" class="d-flex justify-content-center hidden">
            <div class="spinner-border hidden" role="status" >
                <span class="sr-only">Loading...</span>
            </div>
        </div>
    
        <div class="container-fluid">
    
    
    
            <div class="row">
    
    
                <div class="col-md-5">
    
                    <div class="row">
    
                        <div class="col-md-4">
    
                        </div>
    
                        <div class="form-group col-md-5 input-group">
                            <select class="custom-select" id="validationCustom04" required>
                                <option selected disabled value="">Choose...</option>
                                <option value="000000001585">2100 ZA</option>
                                <option value="000000001570">10 TL'lik Steam Bakiyesi</option>
                                <option value="000000001571">25 TL'lik Steam Bakiyesi</option>
                                <option value="000000001572">Bim 400 Riot Points</option>
                                <option value="000000001573">Bim 840 Riot Points</option>
                                <option value="000000001574">Gameforge 6 TRY E-Pin</option>
                                <option value="000000001575">50 Hükümdarlık</option>
                                <option value="000000001582">Bim 10 TL Razer Gold Pin</option>
                                <option value="000000001583">Bim 25 TL Razer Gold Pin</option>
                                <option value="000000001705">20 TL'lik Steam Bakiyesi</option>
                                <option value="018000006508">50 Hükümdarlık</option>
                                
                            </select>
                            <div class="invalid-feedback">
                                Please select a game.
                            </div>
    
    
                        </div>
    
                    </div>
    
                    <div class="row">
    
                        <div class="col-md-4">
    
                        </div>
    
                        <div class="form-group col-md-5 input-group">
    
                            <div class="input-group-prepend">
                                <span class="input-group-text" id="basic-addon1">Quantity</span>
                            </div>
                            <input type="number" class="form-control " id="quantity" name="Quantity" value="1" aria-describedby="basic-addon1" min="1" max="1"/>
    
                        </div>
    
                    </div>
    
                    <div class="row">
    
                        <div class="col-md-4">
    
                        </div>
    
                        <div class="form-group col-md-5 input-group">
                            <div class="input-group-prepend">
                                <span class="input-group-text" id="basic-addon2">E-mail&nbsp;&nbsp;&nbsp;&nbsp;</span>
                            </div>
    
                            <input type="email" class="form-control" aria-describedby="basic-addon2" required id="email"/>
                            <div class="invalid-feedback">
                                Please provide a valid email.
                            </div>
                        </div>
    
    
    
                    </div>
    
                    <div class="row">
    
                        <div class="col-md-4">
    
                        </div>
    
                        <div class="form-group col-md-5">
    
                            <button id="btn_add" type="submit" class="btn btn-info">Add</button>
                            <input id="btn_clear" type="button" value="Clear" class="btn btn-warning"/>
    
                        </div>
    
                    </div>
    
                </div>
    
                <div class="col-md-7">
    
                    <div class="form-group container-fluid">
                        <textarea class="form-control" id="result" rows="5" readonly></textarea>
    
                    </div>
                    <div class="form-group row container-fluid">
                        <div class="col">
                            <input id="btn_purchase" type="button" value="Purchase" class="btn btn-success"/>
                           
                        </div>
                       
                    </div>
    
                </div>
    
            </div>
        </div>
    
    </form>
    
    
    <script>
       
          
            (function () {
                'use strict';
                window.addEventListener('load', function () {
                    
                    var forms = document.getElementsByClassName('needs-validation');
    
                 
                    var validation = Array.prototype.filter.call(forms, function (form) {
    
                        form.addEventListener('submit', function (event) {
                            if (form.checkValidity() === false) {
                                event.preventDefault();
                                event.stopPropagation();
                            } else {
                                event.preventDefault();
                                $("#btn_add").html(
                                    '<span class="spinner-border spinner-border-sm" role="status" id="spinner" aria-hidden="true"></span> Loading...'
                                );
                                var selText = $("#validationCustom04").val();
                                var quantity = $("#quantity").val();
                                var email = $("#email").val();
                                var price = $("#total").val();
    
                                if ($("#total").val() !== '') {
    
                                    price = $("#total").val();
    
                                }
    
                                var serviceUrl = '/GameBanks/A101PinAsync?quantity=' + quantity + '&game=' + selText + '&email=' + email + '&prc=' + price;
                                $.ajax({
                                    type: "GET",
                                    url: serviceUrl,
                                    dataType: 'json',
                                    success: function (data) {
                                        //alert(JSON.stringify(data));
                                        ShowResult(data);
                                        $("#spinner").remove();
                                        $("#btn_add").html('Add');
                                   
                                    }, error: function (xhr, status, error) {
                                        $("#spinner").remove();
                                        $("#btn_add").html('Add');
                                        var errorMessage = xhr.status + ': ' + xhr.statusText + ': ' + status + ': ' + error;
    
                                        alert('Error - ' + errorMessage);
                                    }
    
                                });
                            }
                            form.classList.add('was-validated');
                        }, false);
                    });
                }, false);
    
            })();
    
        function ShowResult(data) {
    
    
            $('#result').append(data.gameList);
            $('#total').val(data.price);
    
    
        }
        $("#btn_clear").click(function () {
            $("#email").val("");
            $("#quantity").val(1);
            $("#validationCustom04").val("");
            $("#result").val("");
            $("#total").val("");
        });
        $("#btn_purchase").click(function () {
            $('.hidden').show();
            
            var result = $("#result").val();
            var total = $("#total").val();
            var email = $("#email").val();
            //alert(result);
            var serviceURL = '/GameBanks/Send';
            $.ajax({
                type: "GET",
                url: serviceURL,
                dataType: 'json',
                data: {
                    body: result,
                    total: total,
                    email: email
                },
                success: function (data) {
                    if (true) {
                        $('.hidden').hide();
                        alert("Email sent to " + email);
                        $("#email").val("");
                        $("#quantity").val(1);
                        $("#validationCustom04").val("");
                        $("#result").val("");
                        $("#total").val("");
                    } else {
                        $('.hidden').hide();
                        alert("Email did not send to " + email);
                    }
                }
            });
        });
    
    
    </script>
    

    Here is the controller action:

    [HttpGet]
            public async Task<IActionResult> A101PinAsync(int quantity, string game, string email, int prc)
            {
                var price = 0;
                try
                {
                    string result = null;
                    var rnd = new Random();
                    var dList = new DemoList();
    
                    if (prc > 0)
                    {
                        price = prc;
                    }
    
                    var games = new Game {Product = game, Quantity = quantity};
    
                    var content = await games.CallGameAsync("Palas", "12345", game, quantity);
                    var deserializedResult = JObject.Parse(content);
    
                    result = (string)deserializedResult["coupons"]?[0]?["Serial"] + ":" + (string)deserializedResult["coupons"]?[0]?["Pin"] + "\n";
                    dList.gameList = result;
                    //NLOG
                    NLogPin(logger, User.Identity.Name, DateTime.Now, result);
                    
                    return new JsonResult(dList);
                }
                catch (Exception e)
                {
                    
                    //NLOG
                    NLog(logger2, "OyunPalas " + e.Message, DateTime.UtcNow,0);
                    throw;
                }
            }
    
            private void NLogPin(Logger logger, string user, DateTime timestamp, string result)
            {
                var sb = new StringBuilder();
                sb.AppendLine("Serial/Pin activator: " + user);
                sb.Append("***");
                sb.Append("Date: " + timestamp.ToLongDateString());
                sb.Append("***");
                sb.Append("Result: " + result);
    
                logger.Info(sb.ToString());
            }
    

    Here is the api caller:

    public async Task<string> CallGameAsync(string username, string password, string product, int quantity)
            {
                HttpResponseMessage response = null;
                try
                {
                    //TEST
                    var request = (HttpWebRequest)WebRequest.Create("http://111.11.11.11:1900/api/v2/web/game/purchase");
    
                    var svcCredentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(username + ":" + password));
    
                    request.Headers.Add("Authorization", "Basic " + svcCredentials);
                    request.Method = "POST";
                    request.KeepAlive = false;
                    request.ContentType = "application/x-www-form-urlencoded";
    
                    var content = new FormUrlEncodedContent(new[]
                    {
                        new KeyValuePair<string, string>("productCode", product),
                        new KeyValuePair<string, string>("quantity",quantity.ToString()),
                        new KeyValuePair<string, string>("shopNo","OyunPalas"),
                        new KeyValuePair<string, string>("safeNo","OyunPalas"),
                        new KeyValuePair<string, string>("cashierNo","OyunPalas")
    
                    });
    
                 
                    var formUrlEncodedContent = content;
                    var urlEncodedString = await formUrlEncodedContent.ReadAsStringAsync();
    
                    using (var streamWriter = new StreamWriter(await request.GetRequestStreamAsync()))
                    {
                        streamWriter.Write(urlEncodedString);
                    }
    
                  
                    var httpResponse = (HttpWebResponse)(await request.GetResponseAsync());
    
                    response = new HttpResponseMessage
                    {
                        StatusCode = httpResponse.StatusCode,
                        Content = new StreamContent(httpResponse.GetResponseStream()),
                    };
    
                    //Read response
                    var htmlResponse = await response.Content.ReadAsStringAsync();
    
                    return htmlResponse;
                }
                catch (Exception e)
                {
                    //NLOG
                    NLog(logger2, "OyunPalas " + e.Message);
                    throw;
                }
    
                
    
            }

    Monday, August 3, 2020 1:34 PM
  • User475983607 posted

    You did not fix the problem.  You are still throwing an exception.  Web API clients expect an defined error format.  If you want to use the ASP.NET default, that's up to you.  The response looks similar to the following.  There is nothing stopping you from reading the response in the AJAX handler.  

    {
        "Message": "An error has occurred.",
        "ExceptionMessage": "The remote server returned a 429.",
        "ExceptionType": "System.Exception",
        "StackTrace": "   at ApiDemo.ApiControllers.DefaultController.Get() in 
    C:\\Users\\michael.gebhard\\Documents\\Visual Studio 2019\\Projects\\NetApps\\ApiDemo\\ApiControllers\\DefaultController.cs:line 20\r\n
    at lambda_method(Closure , Object , Object[] )\r\n
    at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass6_2.<GetExecutor>b__2(Object instance, Object[] methodParameters)\r\n
    at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)\r\n
    at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)\r\n
    --- End of stack trace from previous location where exception was thrown ---\r\n
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
    at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__1.MoveNext()\r\n---
    End of stack trace from previous location where exception was thrown ---\r\n
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
    at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__5.MoveNext()\r\n
    --- End of stack trace from previous location where exception was thrown ---\r\n
    at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n
    at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n
    at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__15.MoveNext()" }

    I recommend using try...catch as designed.  The idea is to catch an exception and handling the exception.  I would return a standard HTTP response to the client.  It is much easier to handle errors on the client when there is a known format.

    https://docs.microsoft.com/en-us/aspnet/web-api/overview/error-handling/exception-handling

    Monday, August 3, 2020 2:37 PM
  • User-1104215994 posted

    How can I get the status code from exception e?

    catch (Exception e)
                {
                    
                    //NLOG
                    NLog(logger2, "OyunPalas " + e.Message, DateTime.UtcNow,0);
    
                    return StatusCode(429,e.Message);
    
    
                }

    Tuesday, August 4, 2020 7:43 AM
  • User475983607 posted

    How can I get the status code from exception e?

    I assume, CallGameAsync follows the same design as your code snippets above and does not handling exceptions properly. 

    var content = await games.CallGameAsync("Palas", "12345", game, quantity);

    Tuesday, August 4, 2020 10:47 AM