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

前端 未结 5 1825
星月不相逢
星月不相逢 2021-01-25 21:32

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 sta

相关标签:
5条回答
  • 2021-01-25 22:11

    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
    
    0 讨论(0)
  • 2021-01-25 22:13

    Ok, this solution solves your problem but imply some changes in your code:

    In the view, when you call Url.Action, you're trying to generate a webapi route from a mvc controller. You can see this question where I say how to do this.

    Basically the idea is using Url.RouteUrl with an extra route value httproute = true.

    Now, you need to change some parts in your code to be able to use this:

    Firstly, you are using attributes to define your web api routes, therefore this routes will be added after the routes defined in your WebApiConfig class, and as I mencioned in the answer of the referenced question, Url.RouteUrl will return the first route that match the route values. So, you need to declare the route in WebApiConfig before the default route:

    /* attributes removed */
    public class LandingPageController : ApiController
    {
        public HttpResponseMessage GetQuadrantData(string unit, string begdate, string enddate)
        {
            ...
    
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.MapHttpAttributeRoutes();
    
            /* Route added before the default one */
            config.Routes.MapHttpRoute(
                name: "QuadrantData",
                routeTemplate: "api/{unit}/{begdate}/{enddate}"
            );
    
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
    

    If you don't do this, the default route will be matched since {id} is optional and unit, begdate and enddate will be passed as query params.

    Once fixed this issue, you have another problem, to be able to use Url.RouteUrl you need to know all the route values (unit, begdate, enddate), that are availables only when the script function is executed. But all razor code in your .cshtml is processed in the server when the html is generated for the client.

    As you can see, when the javascript is executed you are not executing '@Url.RouteUrl...' or '@Url.Action...', at this time you are just handling a string with value returned when the razor expression was processed.

    One thing you can do is pass "template" values to the Url.RouteUrl call, and replace it when the script is executed with the real values:

    var url = '@Url.RouteUrl(new
          {
              unit = "(unit)",
              begdate = "(begdate)",
              enddate = "(enddate)",
              httproute = true
          })'
    
    url = url.replace("(unit)", unitval)
             .replace("(begdate)", begdateval)
             .replace("(enddate)", enddateval)
    
    $.ajax({
        type: 'GET',
        url: url,
        contentType: 'application/json',
        cache: false,
        success: function (returneddata) {
        },
        error: function () {
            alert('hey, boo-boo!');
        }
    });
    

    After the replaces the value of the variable url will be like this /api/ABUELOS/2016-08-07/2016-08-13.


    If you don't want to move the routes to WebApiConfig, you can set a name in RouteAttribute:

    [RoutePrefix("api")]
    public class LandingPageController : ApiController
    {
        [Route("{unit}/{begdate}/{enddate}", Name = "QuadrantData")]
        public HttpResponseMessage GetQuadrantData(string unit, string begdate, string enddate)
        {
            ...
    

    And specify the route name in Url.RouteUrl call:

    var url = '@Url.RouteUrl("QuadrantData", new
          {
              unit = "(unit)",
              begdate = "(begdate)",
              enddate = "(enddate)",
              httproute = true
          })'
    

    This way is less intrusive in your code, but you need to know the name of the route you want to use.

    Hope this helps.

    0 讨论(0)
  • 2021-01-25 22:16

    Regarding your point:

    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."

    Modifying your Ajax call like shown below should solve your problem and will send a GET request to the mapped url (Showing with button click event handler):

     $("#btnGetData").click(function () {
    
       var unitval = 'ABUELOS';
       var begdateval = '2016-08-21';
       var enddateval = '2016-08-27';
    
       $.getJSON("api/" + unitval + "/" + begdateval + "/" + enddateval,
          function (Data) {
            // do what ever you want with the success response
          })
          .fail(
            function (jqXHR, textStatus, err) {
              // do what ever you want with the failed response
          });
     });
    
    0 讨论(0)
  • 2021-01-25 22:21

    Personally not a big fan of using @Url.Action in javascript code; this is how I make ajax calls:

    • Create a hidden field in layout page to store the root url - <input type="hidden" id="hdnRoot" value="@Url.Content("~/")" />
    • Have a constants json file to keep my client side constants (constants.js) - assigns the hidden field value to serviceRoot variable:

      (function () {
      window.constants = {
      
          serviceRoot: $("#hdnRoot").val() + "api/",
          objectState: {
              added: "Added",
              modified: "Modified",
              unchanged: "Unchanged",
              deleted: "Deleted"
          },
         ..other values...
      };
      })();
      
    • Will modify your ajax call as:

      var root = window.constants.serviceRoot;
      $.ajax({
                  type: 'GET',
                  url: root + 'LandingPage/GetQuadrantData/'+unitval+'/'+begdateval+'/'+enddateval,
                //  data: { unit: unitval, begdate: begdateval, enddate: enddateval },
                  contentType: 'application/json',
                  cache: false,
                  success: function (returneddata) {
                  },
                  error: function () {
                      alert('hey, boo-boo!');
                  }
              });
      
       [RoutePrefix("api")]
       public class LandingPageController : ApiController {
      
         [HttpGet]
         [Route("GetQuadrantData/{unit}/{begdate}/{enddate}", Name="GetQuadrantDataFromLandingPage")]
         public HttpResponseMessage GetQuadrantData(string unit, string begdate, string enddate) 
      
    0 讨论(0)
  • 2021-01-25 22:36

    You're only giving your jQuery the controller and action names when it needs the full url to complete the request. Also, your end point has an 'api' prefix. You need your url property to be something like http://localhost:{port}/api/{controller}/{action} if your server is running locally.

    0 讨论(0)
提交回复
热议问题