Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Danny Blatant 91 posts 358 karma points
    Oct 14, 2014 @ 17:05
    Danny Blatant
    0

    Help implimenting restful API please! 404 when I pass parameters

    Hi everyone,

    I'd really appriciate a consise response to my issue here. I'm sure it's something lots of people do regularly so here goes.

    I have followed the docs on umbraco.tv on implimenting ApiControllers. And indeed when I don't try to pass parameters in it works fine! However I need to pass parameters and this is where I'm falling over...

    I also have a situation where I have a custom model, a SurfaceController that supplies the origional Partial, an ApiController that will supply the updated content based on the parameter. Is there a way to make my SurfaceController accessible via ajax?? I need to get this working without additional frameworks as this is an existing site and restarting with a famework can't happen!

    What follows is my code example. If anyone can suggest how to make this work I'd appriciate it!

    namespace MyProject.Controllers
    {
    
        public class TestApiController : UmbracoApiController
        {
    
            public TestApiController(){ }
    
            [HttpPost]
            public string ReturnString()
            {
                return "hello ajax return";
            }
    
            [HttpPost]
            public string ReturnDynamicString(string part)
            {
                return "hello ajax, you sent me " + part;
            }
        }
    }
    

    Pretty simple huh?? Well, the ReturnString fucntion works perfectly, I can get to it via the URL /umbraco/api/testapi/returnstring and the ajax call works. However I can't get the second function to work, I get a message that the second call is not accessible via GET when I try to inspect the URL, which makes sense (no parameters). Here's my Javascript snippet:

        $.ajax({
            url: '/umbraco/api/TestApi/ReturnDynamicString/',
            async: false,
            type: 'POST',
            dataType: 'json',
            contentType: 'application/json, charset=utf-8',
            data: {
                "part": "test"
            }
        }).success(function (data) {
            alert(data);
        }).error(function () {
            alert("ULTIMATE FAIL");
        });
    

    So guys in a nutshell, 2 questions...

    1. Can I call methods on my SurfaceController via ajax?? (makes no sense to me to have one controller for initial "index" view and another to supply ajax updates, when they are based on the same model)

    2. Can anyone make my code above work??

    Thanks in advance,

    Danny "Blatant"

  • Danny Blatant 91 posts 358 karma points
    Oct 14, 2014 @ 17:07
    Danny Blatant
    0

    PS, I also tried to JSON.stringify the data object too...

    $.ajax({
        url: '/umbraco/api/TestApi/ReturnDynamicString/',
        async: false,
        type: 'POST',
        dataType: 'json',
        contentType: 'application/json, charset=utf-8',
        data: JSON.stringify({
            "part": "test"
        })
    }).success(function (data) {
        alert(data);
    }).error(function () {
        alert("ULTIMATE FAIL");
    });
    

    No dice...

  • Danny Blatant 91 posts 358 karma points
    Oct 15, 2014 @ 10:41
    Danny Blatant
    0

    As an update it seems I've over compartmentalised over this. Of course the Surface controller is able to respond to ajax requests. I actually found it way easier, My mind boggles as to why they would publish tutorials on the ApiController when it's so difficult to impliment, yet so easy in a normal SurfaceController...

    Anyway, to solve I modified the surface controller as below. I Added the HttpPost attribute to allow the Controller to accept the ajax post and a simple check on the HtppContext seems to make it an AJAX only method:

    namespace MyProject.Controllers
    {
      public class MySurfaceController : SurfaceController
        {
    
          //a simple test
          [HttpPost]
          public JsonResult TestJson()
          {
            return Json("Hello Surface Ajax");
          }
    
          //passing multiple values
          [HttpPost]
          public JsonResult TestJsonWithParam(int brand, string test)
          {
            //only ajax requests please
            if (!HttpContext.Request.IsAjaxRequest()) { return Json(new { success = false } ); }
    
            return Json("Hello Surface Ajax with param " + test + " - " + brand.ToString() + " Called with context ");
        }
      }
    }
    

    So that makes querstion 1 somewhat covered. It seems so much moe logical to me for the SurfaceController to supply views and ajax responses, and it can use it's own custom model of course, you just need the main PartialView and a @Html.Action stub within a MacroPartial if you want it available in the RTE.

    Regards,

    Danny "Blatant"

  • Sebastiaan Janssen 5058 posts 15520 karma points MVP admin hq
    Oct 15, 2014 @ 13:56
    Sebastiaan Janssen
    0

    So I dove into this and there's some interesting oddities here. The following code will work:

        $.ajax({
            url: '/umbraco/api/TestApi/ReturnDynamicString/',
            type: 'POST',
            async: false,
            dataType: 'json',
            data: { '': 'test' }
        }).success(function (data) {
            console.log(data);
        }).error(function () {
            console.log('ULTIMATE FAIL');
        });
    

    But you'll also need to update your ApiController, notice the extra [FromBody] here:

    public string ReturnDynamicString([FromBody]string part)
    

    Read more about this here: http://encosia.com/using-jquery-to-post-frombody-parameters-to-web-api/

    I would still recommend you use UmbracoApiController (which inherits from ApiController, we just add a few little extra handy things), not only because it's more lightweight than using a SurfaceController but also because ApiController is much better at doing content negotiation and automatically returning properly formatted data (see http://stackoverflow.com/a/9495189/5018 )

  • Danny Blatant 91 posts 358 karma points
    Oct 15, 2014 @ 14:36
    Danny Blatant
    0

    Hi Sebastiaan,

    Thanks for taking the time to look into this in some more detail for me.

    I see how the [FromBody] attribute works, it makes alot of sense really!

    Thank you also for explaining a bit more about the UmbracoApiController, in my full case I had issues with returning IPublishedContent even with the content negotiation and properly formatted returns so I ended up making my own Model that would serialise properly and worked from that.

    Also, in my circumstance I am using my Partial view in an RTE Macro. If I was to develop an UmbracoApiController approch to this how would I do away with the SurfaceController I make so that within the PartialView I can call @Html.Actnon("partial", "model")??

    In order to benifit from the light weight apiController Id need to get rid of this SurfaceControllr otherwise it somewhat defeats the light weight logic as the project is now cartrying both!

    Would I Forget about the SurfaceController and put my Partial View code straight into the MacroPartialView? If so, how would I pass my custom Model to the PMV? I tried the below (which resembles what I would do with a normal Partial View) but this is an error as PartialViewMacroPage doesn't accept type arguments...

    @inherits Umbraco.Web.Mvc.PartialViewMacroPage<BMyProject.Models.MyModel>
    

    Or would a surfaceController in fact be more suitable in this case where I'd like (require?) a SurfaceController for the initial view and/or manual url navigation of the page (for instance no Javascript) and extend this with ajax?

    Thanks for the response!

    Danny "Blatant"

  • Sebastiaan Janssen 5058 posts 15520 karma points MVP admin hq
    Oct 15, 2014 @ 16:03
    Sebastiaan Janssen
    0

    Only use REST API controllers if you're need to be working in a RESTful way (in general: javascript/ajax)

    @inherits Umbraco.Web.Mvc.UmbracoViewPage<MyModel> should work - you can also do route hijacking where make a particular document type always go through a controller of your choosing and return a strongly typed partial view - see my blog http://cultiv.nl/blog/whats-this-umbraco-route-hijacking-all-about/

  • Danny Blatant 91 posts 358 karma points
    Oct 17, 2014 @ 16:23
    Danny Blatant
    0

    Thanks for the feedback Seb,

    I have since made an UmbracoApiController that serves KML for google maps, and in this case we really benifit from the streamlined ApiController inheritance! I had to force the formatter to be always XML but hey that wasn't too bad.

    As the Google Map has it's own SurfaceController and view (for the markup and RTE embedding) it was easy to simply add the map code here and point the map to the Kml Api, everything went swimmingly!

    I think for the case where we want a SurfaceController anyway (some sort of Html that we can get via Html.Action, PVM or not!) it's still more semantic to initialise the model in the Surface controller and extend the controller with JSON style responses as it means no seperate ApiController, but hey I guess thats down to implimentation!

Please Sign in or register to post replies

Write your reply to:

Draft