B. Clay Shannon B. Clay Shannon - 3 months ago 14
reST (reStructuredText) Question

Why is my REST method not being called by this jQuery?

I have a similar question about jQuery button click handler code not being fired at all here.

In this case, it is being fired (when the jQuery is added to a static page (Index.cshtml)), but my REST method is still not being reached. The console message explaining why is:

The resource cannot be found.

Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.

Requested URL: /LandingPage/GetQuadrantData

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1055.0


This is the jQuery, which indeed gets fired when the button is clicked, but the REST call is not being made:

<script>
$(function () {
var btnGetData = document.getElementById('btnGetData');
btnGetData.addEventListener("click", function () {
alert("It works");
var unitval = 'ABUELOS';
var begdateval = '2016-08-07';
var enddateval = '2016-08-13';

$.ajax({
type: 'GET',
url: '@Url.Action("GetQuadrantData", "LandingPage")',
data: { unit: unitval, begdate: begdateval, enddate: enddateval },
contentType: 'application/json',
cache: false,
success: function (returneddata) {
},
error: function () {
alert('hey, boo-boo!');
}
});
});
});
</script>


The REST method is set up like so, in LandingPageController.cs:

[Route("{unit}/{begdate}/{enddate}")]
public HttpResponseMessage GetQuadrantData(string unit, string begdate, string enddate)
{
_unit = unit;
_beginDate = begdate;
_endDate = enddate;
. . .


I have a breakpoint on the "_unit = unit;" line, but it is not being reached; why not? What am I missing here?

The REST controller class under discussion begins like so:

[RoutePrefix("api")]
public class LandingPageController : ApiController


Note: If I use this in the jQuery ("Controller" appended):

url: '@Url.Action("GetQuadrantData", "LandingPageController")'


...both "GetQuadrantData" and "LandingPageController" are red in the IDE (Visual Studio) editor - the letters are red.

OTOH, if I use this (sans "Controller" appended):

url: '@Url.Action("GetQuadrantData", "LandingPage")'


(which has worked for me in the past, leaving the "Controller" off the Controller name), both "GetQuadrantData" and "LandingPage" are UNDERLINED in red, but the font remains the normal color.

UPDATE



Using Nico's link in his comment below, I changed the jQuery "url" line to this:

url: '@Url.Action("GetQuadrantData", "LandingPage", new { httproute = "" })',


...but it still didn't cause the Controller method to be reached.

UPDATE 2



Stepping through the javascript in the (Chrome) browser, I see that the "url" line has been dynamically changed from what it is at design time:

url: '@Url.Action("GetQuadrantData", "LandingPage", new { httproute = "" })',


...to:

url: '/api/LandingPage?action=GetQuadrantData',


I reckon what it should really have been resolved to is more like:

url: '/api/ABUELOS/2016-08-14/2016-08-20',


Am I right? Why isn't it being resolved like so?

If I manually enter that URL in the browser, so that the URL bar reads "
http://localhost:52194/api/ABUELOS/2016-08-21/2016-08-27
" it works - the method is reached and it "does its thing."

UPDATE 3



I tried this, too:

$(function () {
$("#btnGetData").click(function () {
document.body.style.cursor = 'wait';
$.ajax({
type: "GET",
url: '@Url.Action("GetQuadrantData", "LandingPage")',
success: function (retval) {
$("body").append($(retval));
document.body.style.cursor = 'pointer';
},
error: function () {
alert('error in btnGetData');
}
}); // end AJAX
}); // end click
}); // end ready function


...but only saw "error in btnGetData"

UPDATE 4



My most recent failure is this:

$("#btnGetData").click(function () {
document.body.style.cursor = 'wait';
var unitval = $('#unitName').val();
var begdateval = $('#datepickerFrom').val();
var enddateval = $('#datepickerTo').val();
$.ajax({
type: 'GET',
url: '@Url.Action("GetQuadrantData", "LandingPage")',
data: { unit: unitval, begdate: begdateval, enddate: enddateval },
cache: false,
success: function (returneddata) {
alert($(returneddata));
},
error: function () {
alert('error in ajax');
}
});
});


Again, I see only "error in ajax"

UPDATE 5



Note: The maximum bounty (200 points, I think it is) will be awarded to whoever can solve this dilemma. If more than one person does, then the bounty goes to whoever solves it best (which basically to me means in the most straightforward and easy-to-implement manner.

In the interest of full disclosure and desperation, here is the entire contents (with boring/meaningless parts elided with ellipsis dots) of the first page that displays when the WEB API app runs (from \Views\Home\Index.cshtml:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>eServices Reporting - Customer Dashboard</title>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

<!-- jQuery library -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

<!-- Latest compiled JavaScript -->
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->

<style>
body {
padding-top: 20px;
padding-bottom: 20px;
background-color: white;
}
. . .
</style>
<script>
$(function () {
$("#btnGetData").click(function () {
document.body.style.cursor = 'wait';
var unitval = "ABUELOS"; //$('#unitName').val();
var begdateval = $('#datepickerFrom').val();
var enddateval = $('#datepickerTo').val();
$.ajax({
type: 'GET',
url: '@Url.Action("GetQuadrantData", "LandingPage")',
data: { unit: unitval, begdate: begdateval, enddate: enddateval },
cache: false,
success: function (returneddata) {
alert($(returneddata));
},
error: function () {
alert('error in ajax');
}
});
});

}); // end ready function
</script>
</head>

<body>
<div class="container body-content">
<div class="jumbotronjr">
<div class="col-md-3" style="margin-top: 0.6cm">
<img src="http://www.proactusa.com/wp-content/themes/proact/images/pa_logo_notag.png" height="86" width="133" alt="PRO*ACT usa logo">
</div>
<div class="col-md-9">
<label class="titletext" style="margin-top: 0.2cm;">Customer Dashboard</label>
<br />
<label class="titletextjr" style="margin-top: -2.2cm;" id="unitName">Craftworks</label>
<label class="cccsfont"> for the week of August 14 </label>
<input class="smalldatepicker" type="date" id="datepickerFrom" name="daterangefrom" value="2016-08-14">
</input>
<label class="cccsfont"> to </label>
<input type="date" class="smalldatepicker" id="datepickerTo" name="daterangeto" value="2016-08-20">
</input>
<button class="btn green smallbutton" id="btnGetData" name="btnGetData">SUBMIT</button>
</div>
</div>

<div class="row">
<div class="col-md-12">
<hr />
</div>
</div>

<div class="row">
<div class="col-md-12">
</div>
</div>

<div class="row">
<div class="col-md-6">
<div class="topleft">
<h2 class="sectiontext">Top 10 Items Purchased</h2>

<table>
<tr>
<th>Item Code</th>
<th>Description</th>
<th class="rightjustifytext">Qty</th>
</tr>
<tr>
<td>101200</td>
<td>ASPARAGUS, STANDARD 11/1#</td>
<td class="rightjustifytext">32</td>
</tr>
<tr>
<td>140200</td>
<td>MUSHROOMS, MEDIUM 10#</td>
<td class="rightjustifytext">20</td>
</tr>
<tr>
<td>140000</td>
<td>MUSHROOMS, BUTTON 10#</td>
<td class="rightjustifytext">14</td>
</tr>
<tr>
<td>127100</td>
<td>LETTUCE, ROMAINE 24 CT </td>
<td class="rightjustifytext">14</td>
</tr>
<tr>
<td>300123</td>
<td>BEANS, GREEN TRIM 2/5# (BAGS)</td>
<td class="rightjustifytext">13</td>
</tr>
<tr>
<td>173100</td>
<td>POTATOES, 50 CT IDAHO</td>
<td class="rightjustifytext">12</td>
</tr>
<tr>
<td>234225</td>
<td>BERRIES, STRAWBERRY 1# CLAM</td>
<td class="rightjustifytext">11</td>
</tr>
<tr>
<td>188500</td>
<td>TOMATOES, GRAPE 12/1 PT</td>
<td class="rightjustifytext">10</td>
</tr>
<tr>
<td>122500</td>
<td>LETTUCE, ICEBERG LINER 24 CT</td>
<td class="rightjustifytext">10</td>
</tr>
<tr>
<td>121050</td>
<td>LETTUCE, GREEN LEAF 24 CT</td>
<td class="rightjustifytext">10</td>
</tr>
</table>
</div>
</div>

<div class="col-md-6">
<div class="topright">
<h2 class="sectiontext">Pricing Exceptions - Weekly Recap</h2>
<label class="redfont cccs">Red denotes Contract Item Overages</label>
</br>
<label class="cccs">For Weyand on the pricing week of - 7/31/2016</label>
<table>
<tr>
<th>PRO*ACT Member</th>
<th class="rightjustifytext">Total Occurrences of Summary Items</th>
<th class="rightjustifytext">Total Summary Exceptions</th>
<th class="rightjustifytext">Total Percentage of Summary Exceptions</th>
</tr>
<tr>
<td style="width:30%">Stern</td>
<td style="width:23%" class="rightjustifytext">205</td>
<td style="width:23%" class="rightjustifytext">2</td>
<td style="width:24%" class="rightjustifytext">99.02%</td>
</tr>
<tr>
<td>Hardies Dallas</td>
<td class="rightjustifytext">1,597</td>
<td class="rightjustifytext">0</td>
<td class="rightjustifytext">100.00%</td>
</tr>
<tr>
<td>Hardies South</td>
<td class="rightjustifytext">612</td>
<td class="rightjustifytext">1</td>
<td class="rightjustifytext">99.84%</td>
</tr>
<tr>
<td>Go Fresh</td>
<td class="rightjustifytext">482</td>
<td class="rightjustifytext">0</td>
<td class="rightjustifytext">100.00%</td>
</tr>
<tr>
<td>Segovias</td>
<td class="rightjustifytext">1,360</td>
<td class="rightjustifytext">2</td>
<td class="rightjustifytext">99.85%</td>
</tr>
<tr>
<td>Potato Spec</td>
<td class="rightjustifytext">1,605</td>
<td class="rightjustifytext">0</td>
<td class="rightjustifytext">100.00%</td>
</tr>
<tr>
<td class="rightjustifytext bold">TOTAL</td>
<td class="rightjustifytext bold">5,861</td>
<td class="rightjustifytext bold">5</td>
<td class="rightjustifytext bold">99.79%</td>
</tr>
</table>
</div>
</div>
</div>

<div class="row">
<div class="col-md-6">
<div class="bottomleft">
<h2 class="sectiontext">Forecasted Spend - $9,814.81</h2>
<table>
<tr>
<th>Item Code</th>
<th class="rightjustifytext">Last Week's Usage</th>
<th class="rightjustifytext">This Week's Price</th>
<th class="rightjustifytext">Forecasted Spend</th>
</tr>

<tr>
<td>261650</td>
<td class="rightjustifytext">49</td>
<td class="rightjustifytext">3.14</td>
<td class="rightjustifytext">153.86</td>
</tr>
<tr>
<td>231083</td>
<td class="rightjustifytext">52</td>
<td class="rightjustifytext">1.25</td>
<td class="rightjustifytext">65.00</td>
</tr>
<tr>
<td>398980</td>
<td class="rightjustifytext">46</td>
<td class="rightjustifytext">4.95</td>
<td class="rightjustifytext">227.70</td>
</tr>
<tr>
<td>351135</td>
<td class="rightjustifytext">40</td>
<td class="rightjustifytext">0.75</td>
<td class="rightjustifytext">30.00</td>
</tr>
<tr>
<td>398036</td>
<td class="rightjustifytext">42</td>
<td class="rightjustifytext">3.00</td>
<td class="rightjustifytext">126.00</td>
</tr>
<tr>
<td>208110</td>
<td class="rightjustifytext">42</td>
<td class="rightjustifytext">2.50</td>
<td class="rightjustifytext">105.00</td>
</tr>
<tr>
<td>102800</td>
<td class="rightjustifytext">1835</td>
<td class="rightjustifytext">2.25</td>
<td class="rightjustifytext">4,128.75</td>
</tr>
<tr>
<td>367050</td>
<td class="rightjustifytext">1910</td>
<td class="rightjustifytext">1.95</td>
<td class="rightjustifytext">3,724.50</td>
</tr>
<tr>
<td>173100</td>
<td class="rightjustifytext">66</td>
<td class="rightjustifytext">19.00</td>
<td class="rightjustifytext">1,254.00</td>
</tr>
<tr>
<td class="bold">TOTAL</td>
<td class="bold rightjustifytext">4082</td>
<td class="bold rightjustifytext">--</td>
<td class="bold rightjustifytext">$9,814.81</td>
</tr>
</table>
</div>
</div>

<div class="col-md-6">
<div class="bottomright">
<h2 class="sectiontext">Delivery Performance</h2>
<table>
<tr>
<th>PRO*ACT Distributor</th>
<th>Restaurant Location</th>
<th class="rightjustifytext">Avg Order Amount</th>
<th class="rightjustifytext">Avg Package Count</th>
<th class="rightjustifytext">Total Sales</th>
</tr>

<tr>
<td>Sunrise FL</td>
<td>A1A ALEWORKS - #4405 - ST. AUGUSTINE</td>
<td class="rightjustifytext">$475.78</td>
<td class="rightjustifytext">28.50</td>
<td class="rightjustifytext">$1,903.10</td>
</tr>
<tr>
<td>Sunrise FL</td>
<td>RAGTIME TAVERN - #4404 - ATLANTIC BEACH</td>
<td class="rightjustifytext">$221.46</td>
<td class="rightjustifytext">17.50</td>
<td class="rightjustifytext">$885.82</td>
</tr>
<tr>
<td>Sunrise FL</td>
<td>SEVEN BRIDGES - #4403 - JACKSONVILLE</td>
<td class="rightjustifytext">$367.49</td>
<td class="rightjustifytext">22.67</td>
<td class="rightjustifytext">$1,102.47</td>
</tr>
<tr>
<td>T&T</td>
<td>BIG RIVER - #4201 - CHATTANOOGA</td>
<td class="rightjustifytext">$396.06</td>
<td class="rightjustifytext">22.83</td>
<td class="rightjustifytext">$2,376.34</td>
</tr>
<tr>
<td>T&T</td>
<td>BIG RIVER - #4205 - HAMILTON PL</td>
<td class="rightjustifytext">$424.74</td>
<td class="rightjustifytext">26.00</td>
<td class="rightjustifytext">$1,698.95</td>
</tr>
<tr>
<td class="bold">TOTAL</td>
<td></td>
<td class="bold rightjustifytext">3,770.42</td>
<td class="bold rightjustifytext">23.50</td>
<td class="bold rightjustifytext">$1,592.60</td>
</tr>
</table>
</div>
</div>
</div>

</div>

</body>
</html>


UPDATE 6



I know there's more to Arturo's answer than this, but so far just adding this:

config.Routes.MapHttpRoute(
name: "QuadrantData",
routeTemplate: "api/{unit}/{begdate}/{enddate}"
);


...has helped, as the breakpoint in my method is being reached. Unfortunately, it squawks "SyntaxError: Unexpected token < in JSON at position 0"

Answer

You have a few issues with how you are generating the route and how you are trying to access it.

You Web API action is using attribute routing so by default there is no route name to match like in the convention-based routing.

Update the route attribute to include a name to find in the route table.

[RoutePrefix("api")]
public class LandingPageController : ApiController {

    [HttpGet]
    [Route("{unit}/{begdate}/{enddate}", Name="QuadrantData")]
    public HttpResponseMessage GetQuadrantData(string unit, string begdate, string enddate) {
        _unit = unit;
        _beginDate = begdate;
        _endDate = enddate;
        //...other code
    }

    //...other code
}

Next even though you have the name you also need to include the template parameters in order to get a match from MVC and have it generate the url in the template you defined on the action.

To generate link to Web API, it would look like this

@Url.RouteUrl(routeName : "QuadrantData", routeValues : new { httpRoute = true , unit = "ABUELOS", begdate = "2016-08-07", enddate = "2016-08-13"  })

or

@Url.HttpRouteUrl(routeName : "QuadrantData", routeValues : new { unit = "ABUELOS", begdate = "2016-08-07", enddate = "2016-08-13"  })

which would add the httpRoute to the route values.

Reference: Construct url in view for Web Api with attribute routing

Now with your approach out of the way, I would suggest the following alternative approach.

KISS principle. Change Web API (REST) endpoint to POST and change its template.

[RoutePrefix("api")]
public class LandingPageController : ApiController {

    //eg POST api/QuadrantData
    [HttpPost]
    [Route("QuadrantData", Name="GenerateQuadrantData")]
    public HttpResponseMessage QuadrantData(string unit, string begdate, string enddate) {
        _unit = unit;
        _beginDate = begdate;
        _endDate = enddate;
        //...other code
    }

    //...other code
}

and send the data in the body of a JSON POST request

$(function () {
    $("#btnGetData").click(function () {
        document.body.style.cursor = 'wait';
        var unitval = "ABUELOS"; //$('#unitName').val();
        var begdateval = $('#datepickerFrom').val();
        var enddateval = $('#datepickerTo').val();

        var jsonBody = JSON.stringify({ unit: unitval, begdate: begdateval, enddate: enddateval });

        $.ajax({
            type: 'POST',
            url: '@Url.HttpRouteUrl("GenerateQuadrantData", null)',
            contentType: 'application/json',
            dataType: 'json',
            data: jsonBody,
            cache: false,
            success: function (returneddata) {
                alert($(returneddata));
            },
            error: function () {
                alert('error in ajax');
            }
        });
    });

}); // end ready function