S. ten Brinke
S. ten Brinke

Reputation: 2973

Jquery Ajax call doesn't hit the action method/returns entire HTML page

The solution to my problem can be found below.

I can not get my code to work. I need to send an AJAX call to an action method in my controller that has to return a string (for now). But when I activate the AJAX call, it returns the HTML source code of the page I'm currently on And it does not even hit the desired controller action.
This is my code:

Ajax call

<script>

    function addtoCart()
    {
        amount = prompt("Hoeveel producten wilt u toevoegen aan uw winkelwagen?", 1);
        //find product
        var product = {
            "id": '@Model.ID',
            "naam": "@Model.Naam",
            "afbeelding": "@Model.Afbeelding",
            "beschrijving": "@Model.Beschrijving",
            "prijs": "@Model.Prijs",
            "aantal": amount
        };
        $.ajax({
            url: '@Url.Action("storeProductInSession", "Product")',
            type: 'POST',
            data: JSON.stringify(product),
            dataType: 'json', //Right now, this gives an "Unexpected Token < error". This is probably because when the method returns the entire HTML document, it won't be able to render <!doctype html> as JSON.
            contentType: 'application/json; charset=utf-8',
            success: function (result)
            {
                console.log(result);
            },
            error: function (jqXHR, textStatus, errorThrown)
            {
                console.log(product);
                console.log(jqXHR);
                console.log(textStatus);
                console.log(errorThrown);
            },
            async: true
        });

        //sessionStorage.setItem('@Model.ID', JSON.stringify(product)); This is for later
    }

StoreProductInSession(The method that has to be called)

 [HttpPost]
        public string storeProductInSession(object product)/*, string naam, string afbeelding, string beschrijving, decimal prijs, int aantal)*/
        {
            return "This method is going to do session stuff, but for now it just has to return this string";

            //Get the POST input from the AJAX call

            //Add the input to a JSON array

            //add the array to the session

            //Return the array

            //let javascript convert that array to the cart <li>
        }

It still returns the entire HTML page, and even if the object product parameter is incorrect, I would be happy to see that the ajax call actually hits that method. I am thinking that there could be a problem between the call and my routing, but I am not sure. Here's that code:

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapRoute(
name: "Product",
url: "Product/{id}",
defaults: new { controller = "Product", action = "Index" }
);
        routes.MapRoute(
            name: "Categorie",
            url: "Categorie/{id}",
            defaults: new { controller = "Categorie", action = "Index" }
            );
        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Welkom", action = "Index", id = UrlParameter.Optional }
        );


    }

If I would even be able to simply give the ID to the StoreProductInSession action, I could work from there. I'm going to work with the user's session, so I think a GET would not be safe enough.

Solution

A big thanks to @Dolan for helping out! The problem was my routing!

Ajax Call

<script>

    function addtoCart()
    {
        amount = prompt("Hoeveel producten wilt u toevoegen aan uw winkelwagen?", 1);
        //find product
        var product = {
            "id": '@Model.ID',
            "naam": "@Model.Naam",
            "afbeelding": "@Model.Afbeelding",
            "beschrijving": "@Model.Beschrijving",
            "prijs": "@Model.Prijs",
            "aantal": amount
        };
        $.ajax({
            url: '@Url.Action("storeProductInSession", "Product")',
            type: 'POST',
            data: JSON.stringify(product),
            dataType: 'html', //I tried entering 'json' but that gave me a 'unexpected < token' error.
            contentType: 'application/json; charset=utf-8',
            success: function (result)
            {
                console.log(result);
            },
            error: function (jqXHR, textStatus, errorThrown)
            {
                console.log(product);
                console.log(jqXHR);
                console.log(textStatus);
                console.log(errorThrown);
            },
            async: true
        });
</script>

storeProductInSession

 [HttpPost]
        public string storeProductInSession(int id, string naam, string afbeelding, string beschrijving, decimal prijs, int aantal)
        {
            return String.Format("Product given to the ajax call: {0}, {1}, {2}, {3}, {4}, {5}", id, naam, afbeelding, beschrijving, prijs, aantal);
}

Routing

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
    name: "Product",
    url: "Product/{id}",
    defaults: new { controller = "Product", action = "Index" },
    constraints: new { id = "\\d+" }
);

            routes.MapRoute(
                name: "ProductAction",
                url: "Product/storeProductInSession",
                defaults: new { controller = "Product", action = "storeProductInSession" }
            );

            routes.MapRoute(
                name: "Categorie",
                url: "Categorie/{id}",
                defaults: new { controller = "Categorie", action = "Index" }
            );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Welkom", action = "Index", id = UrlParameter.Optional }
                );


        }

Upvotes: 2

Views: 5472

Answers (2)

Donal
Donal

Reputation: 32713

You should use the URL helper:

url: '@Url.Action("storeProductInSession", "Product")',

For more information, see here.

OK, I think the issue with your product object. It is not an object, rather a string. The ' characters around your product object are the issue - JavaScript thinks it is a string - not an object. It should be something like this:

var product = {
            "id": "@Model.ID",
            "naam": "@Model.Naam",
            "afbeelding": "@Model.Afbeelding",
            "beschrijving": "@Model.Beschrijving",
            "prijs": "@Model.Prijs",
            "aantal": " + amount + "
        };

See here: http://jsfiddle.net/donal/mo776xe0/1/

Also, if you know id will be an integer, you can do this:

var product = {
            "id": @Model.ID,
            "naam": "@Model.Naam",
            "afbeelding": "@Model.Afbeelding",
            "beschrijving": "@Model.Beschrijving",
            "prijs": "@Model.Prijs",
            "aantal": " + amount + "
        };

Can you try this:

[HttpPost]
public string storeProductInSession(object product)
{
    return "This method is going to do session stuff, but for now it just has to return this string";

}

Looking at your routes, I can see the problem.

It is assuming storeProductInSession is the id of the Product you are looking for and therefore going to the Index action of the Product controller.

What we need is a constraint. A constraint will tell the route to only use it if the id is a number (digit). For example: constraints: new { id = @"\d+" }

Therefore, you need to change the routes to this (You also need a route for the Product/storeProductInSession action):

routes.MapRoute(
    name: "Product",
    url: "Product/{id}",
    defaults: new { controller = "Product", action = "Index" },
    constraints: new { id = "\\d+" }
);

routes.MapRoute(
    name: "ProductAction", 
    url: "Product/storeProductInSession", 
    defaults: new {controller = "Product", action = "storeProductInSession"}
);

routes.MapRoute(
    name: "Categorie",
    url: "Categorie/{id}",
    defaults: new { controller = "Categorie", action = "Index" }
);

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Welkom", action = "Index", id = UrlParameter.Optional }
    );

Therefore, when you are asking for Product/storeProductInSession. It will see that storeProductInSession is not a digit and then move on to the next route.

The next route will point it at the Product controller and the storeProductInSession action.

Upvotes: 2

JuaRoAl
JuaRoAl

Reputation: 201

Try this, remove "dataType: html" and change this: (echo instead return):

[HttpPost]
        public string storeProductInSession(int id, string naam, string afbeelding, string beschrijving, decimal prijs, int aantal)
        {
            echo "This method is going to do session stuff, but for now it just has to return this string";

        }

Upvotes: 0

Related Questions