Cross Domain WebApi Sample

Thread is closed for posting
25 posts, 0 answers
  1. AB74794F-09D3-4471-8F45-905AC91FDDD0
    AB74794F-09D3-4471-8F45-905AC91FDDD0 avatar
    240 posts
    Member since:
    Oct 2007

    Posted 14 Aug 2012 Link to this post

    Requirements

    Kendo UI Suite and Version - v2012.2.710


    jQuery Version - v 1.7.1


    Supported Browsers and Platforms - all


    Components/Widgets used - ListView



    Cross Domain Mobile WebApi Sample

    This application is an example of how you can setup your application architecture to use the ASP.NET 4.x version of the WebApi framework for nice REST-ful client communication.

    The goal here is to help demonstrate how to get past some of the pitfalls when using cross-domain communication with a web service such as WebApi.

    With this example, it simply shows how you provide GET requests to the WebApi service to return json data to the HTML client application.  There are no POST, PUT or DELETE examples in this application.  You can easily test them out using Fiddler.

    How to run this application

    1)  The first thing to do is to open the solution in Visual Studio 2010
    2)  Then select the WebApi project and then select the Debug menu and then Start without debugging.  Or press Ctrl-F5.  This will start the WebApi service so ajax calls can be made to it.
    3)  In the Presentation Layer, right-click on the index.html file in the Mobile client application, and select "View in browser".

    You should see a list of products displayed in mobile view.  You can click the "Flat" or "Grouped" tabstrip items to toggle between the two views.

    n-Tier Architecture

    This application is built with four (4) separate projects:

    -- Presentation Layer - HTML Client - a simple HTML page for the mobile application that can display either a list of Products or a grouped list.  Similar to the Kendo example, but this actually hits a datasource repository.

    -- Business Objects - POCO classes - these might be used with Entity Framework code-first

    -- Services - WebApi - this is the REST-ful service that communicates with the Data Objects repositories

    -- Data Objects - repositories - there are two (2) repositories, one for Products and one for Categories


    Data Objects

    The Repository Pattern is used here and the WebApi service implements Dependency Injection on the repository interfaces, so the underlying repository implementation can change without affecting the WebApi service.

    This example uses an in-memory example of the data source, so no database setup is necessary to run the application.

    The ProductRepository and CategoryRepository implement interfaces which are used in the WebApi controller via Dependency Injection.  These classes contain the basic REST-ful CRUD operations plus some custom operations for non-CRUD actions, like GetProductsByCategory, etc.

    In this example only the GET methods are implemented.

    WebApi Service

    This is where most of the work takes place.  The WebApi controllers query the repositories and return the proper responses to the client.

    This project was created from an Empty Web Application and then various NuGet packages.

    NuGet packages used to create this project:

    -- Microsoft.AspNet.WebApi - this is the WebApi framework
    -- Unity.WebApi - this is the Unity IoC container for WebApi
    -- WebApiContrib.Formatting.Jsonp - this is the necessary MediaTypeFormatter for cross-domain communication
    -- WebActivator - this is used to enable Unity outside of the Global.asax file

    There is a single controller for this application, the ProductController which inherits from ApiController.  It contains basic REST-ful CRUD operations, plus two extra example actions for returning Products by category or by product name.

    You can play with the REST-ful operations directly in the browser by entering any of the following sample routes:

    -- /api/product - get all products
    -- /api/product/4 - get a selected product by id
    -- /api/product/byname/crossbow - get a selected product by name
    -- /api/product/category/electronics - get products by category name

    These are the two custom actions:

    #region Custom Actions
     
    /// <summary>
    /// This is a custom action that returns Product by category.
    /// Check how the route is created in the RouteConfig.cs file in the App_Start folder.
    /// </summary>
    /// <param name="category">Category Name</param>
    /// <returns></returns>
    public List<Product> GetProductsByCategory(string category)
    {
        return _productRepository.Get(c => c.Category.CategoryName.ToLower() == category.ToLower()).ToList();
    }
     
    /// <summary>
    /// Get a Product by name.  Check the RouteConfig.cs file for routing.
    /// </summary>
    /// <param name="name">Product Name</param>
    /// <returns></returns>
    public Product GetProductByName(string name)
    {
     
        Product product = _productRepository.Get(p => p.Name.ToLower() == name.ToLower()).SingleOrDefault();
        if (product == null)
            throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
     
        return product;
    }
     
    #endregion


    Client Application

    The client application is a KendoUI Mobile application that you can easily test in a normal browser to see how it operates.

    It's a single HTML page with multiple mobile views.  One view displays a ListView with all the Products, and another view displays the Products grouped by Category.

    The Flat view displays data using a KendoUI template.

    <!--This is the template for the Flat ListView-->
    <script type="text/x-kendo-template" id="listviewHeadersTemplate">
        <h3 class="item-title">#= Name #</h3>
        <p class="item-info">#= kendo.format('{0:c}', Price) #</p>
        <p>#= Category.CategoryName #</p>
    </script>


    The Grouped view uses a simple inline template.

    template: "#= Name # - #= kendo.format('{0:c}', Price) #",

    I had help from the nice folks at Telerik to get the currency formatting working correctly.

    The data is received from the remote WebApi service via the KendoUI Datasource object.  To keep things DRY, a shared transport object is created that contains the 'read' method to get the data from the service.  It is then used with both jQuery functions for the Flat and Grouped views in different ways.

    The transport object is applied to a shared Kendo Datasource object and used with the Flat function.  And it is used with an inline kendo Datasource object for the Grouped function since this datasource object needs to contain the group method.

    This is the code for the Ajax calls using the Kendo Datasource object.

    <script>
     
        // The base remote Url
        var base_url = "http://localhost:55689";
     
        // Create a reusable shared Transport object
        var productTransport = new kendo.data.RemoteTransport({
            read: {
                url: base_url + "/api/product",
                dataType: 'jsonp'// jsonp is necessary here for cross domain calls, not just json
                type: 'GET'
            }
        });
     
        // Create a reusable shared DataSource object
        var datasource = new kendo.data.DataSource({
            transport: productTransport
        });
     
        // This function is data-bound to the flat listview
        function mobileProductDataBind() {
            $("#flat-listview").kendoMobileListView({
                dataSource: datasource,
                template: kendo.template($("#listviewHeadersTemplate").html())
            });
        }
     
        // This function is data-bound to the grouped listview
        function mobileProductGroupedDataBind() {
            $("#grouped-listview").kendoMobileListView({
                dataSource: kendo.data.DataSource.create({
                    transport: productTransport,
                    group: { field: "Category.CategoryName"}
                }),
                template: "#= Name # - #= kendo.format('{0:c}', Price) #",
                fixedHeaders: true
            });
        }
    </script>


    That's it!

    In Conclusion

    This is obviously a very simple application but it attempts to demonstrate how a real world mobile application is built with cross-domain data access calls.

    The important point to remember for cross-domain applications using JSON, is to use "jsonp".  You need to remember to set it in the client as "dataType: 'jsonp'" and include the MediaTypeFormatter for Jsonp in the Application_Start.  I have it included in the App_Start/FormattersConfig.cs file.  You can add others that you need here also.
  2. CED6735F-2460-4625-A6FB-12681A83723A
    CED6735F-2460-4625-A6FB-12681A83723A avatar
    2231 posts
    Member since:
    Jun 2020

    Posted 17 Aug 2012 Link to this post

    Hello King Wilder,

    Thank you for providing this example. I updated your Telerik points.

    Regards,
    Daniel
    the Telerik team
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  3. AB74794F-09D3-4471-8F45-905AC91FDDD0
    AB74794F-09D3-4471-8F45-905AC91FDDD0 avatar
    240 posts
    Member since:
    Oct 2007

    Posted 17 Aug 2012 Link to this post

    Daniel,

    I uploaded a slightly modified version of the application, and I thought it would overwrite the original and it didn't, and now there's two files with the same name.

    Can you delete the older zip file?  I uploaded the newer, better version today, 2012-8-17.

    Thanks,

    King Wilder
  4. CED6735F-2460-4625-A6FB-12681A83723A
    CED6735F-2460-4625-A6FB-12681A83723A avatar
    2231 posts
    Member since:
    Jun 2020

    Posted 22 Aug 2012 Link to this post

    Hi again,

    I deleted the older file.

    Regards,
    Daniel
    the Telerik team
    Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
  5. AB74794F-09D3-4471-8F45-905AC91FDDD0
    AB74794F-09D3-4471-8F45-905AC91FDDD0 avatar
    240 posts
    Member since:
    Oct 2007

    Posted 22 Aug 2012 Link to this post

    Daniel,

    Thanks for removing the older file.

    King Wilder
  6. 527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC
    527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC avatar
    13 posts
    Member since:
    Jan 2011

    Posted 17 Sep 2012 Link to this post

    Hello King, Congratulations for this sample project. I would like to ask you how do handle the nested models, in real-life scenarios where you have many related tables in Db?
  7. AB74794F-09D3-4471-8F45-905AC91FDDD0
    AB74794F-09D3-4471-8F45-905AC91FDDD0 avatar
    240 posts
    Member since:
    Oct 2007

    Posted 17 Sep 2012 Link to this post

    lulian,

    If I need nested values from related entities, I will probably assemble a viewmodel in a service layer with just the necessary properties. It really depends on the situation.

    The DbContext can build the related properties for you as long as you don't have circular references. The javascript serializer doesn't like that.

    I hope that helps.

    King Wilder
  8. 527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC
    527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC avatar
    13 posts
    Member since:
    Jan 2011

    Posted 24 Sep 2012 Link to this post

    Hi,

    and thank you for the reply. I thought that this is the solution and I already used viewmodels for loading data in grids.
    But I'm not sure how to use viewmodels in the repository - unit of work - unity architecture. Do you happen to have an example, maybe also with CRUD operations?

    Thanks,
    Iulian
  9. AB74794F-09D3-4471-8F45-905AC91FDDD0
    AB74794F-09D3-4471-8F45-905AC91FDDD0 avatar
    240 posts
    Member since:
    Oct 2007

    Posted 24 Sep 2012 Link to this post

    Lulian,

    If you are asking how to use viewmodels when performing an POST operation such as Insert, Update or Delete, you won't generally.

    Viewmodels are used primarily to send only the necessary data to the view for display.  If you are collecting data, then you just need to collect the form data which will usually conform to an existing entity.  Then this entity can simply be sent to the repository for the POST operation.

    If you have a form that contains data that might be used for more than one entity, then you are responsible for mapping the data to the correct entities before performing the POST operation.  If you are using MVC, this is simple since you can have multiple arguments in the controller actions that can bind to incoming models and the MVC framework will bind them automatically for you.  Then you can simply make multiple calls to the correct repositories for that entity.

    A controller action might look like this:

    public ActionResult InsertCustomer([Bind(Prefix="Customer")]Customer customer, [Bind(Prefix="Address")]Address address)
    {
       try
       {
          _customerRepository.Insert(customer);
          _unitOfWork.Commit();
        
          address.CustomerId = customer.Id;
        
          _addressRepository.Insert(address);
          _unitOfWord.Commit();
       }
       catch Exception(e)
       {
        
       }
    }

    I hope this helps.

    Thanks,

    King Wilder
  10. 527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC
    527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC avatar
    13 posts
    Member since:
    Jan 2011

    Posted 24 Sep 2012 Link to this post

    Ok, that's exactly the way I am using right now viewmodels for displaying data and the repositories for CRUD actions in MVC.
    But how would you use them in your example, when trying to make an insert/update.
    Can you go further with the WebApi Sample and implement CRUD action? 
    How would you send the model (or the viewmodel?) to javascript (assuming you are using kendo and MVVM), and how do you send them back to the webapi controller, and then update the database? All this time having in mind that I can't send the model (directly from DBContext) and return it to the controller because of the circular references.

    Thanks,
    Julian
  11. AB74794F-09D3-4471-8F45-905AC91FDDD0
    AB74794F-09D3-4471-8F45-905AC91FDDD0 avatar
    240 posts
    Member since:
    Oct 2007

    Posted 24 Sep 2012 Link to this post

    Julian,

    I'll work on updating the example to have CRUD examples.  Give me a few days, I've got other things I'm working on right now.

    Thanks,

    King Wilder
  12. 527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC
    527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC avatar
    13 posts
    Member since:
    Jan 2011

    Posted 25 Sep 2012 Link to this post

    Ok, great.
  13. E9B9EE48-EE6C-4F5B-837F-AEB95ADCC6F1
    E9B9EE48-EE6C-4F5B-837F-AEB95ADCC6F1 avatar
    17 posts
    Member since:
    May 2009

    Posted 23 Oct 2012 Link to this post

    Hey,

    Might I emphasize the importance of the use of JsonpMediaTypeFormatter? This can be obtained using NuGet (WebApiContrib). Took me quite some time before I stumbled on this post and get it working. Thanks for the example project.

    Cheers.
  14. AB74794F-09D3-4471-8F45-905AC91FDDD0
    AB74794F-09D3-4471-8F45-905AC91FDDD0 avatar
    240 posts
    Member since:
    Oct 2007

    Posted 23 Oct 2012 Link to this post

    Frank,

    Thanks for pointing out how to obtain this MediaTypeFormatter, I forgot to do that.  I do mention that this formatter is necessary for this application to work in the article a couple of times, though I should have mentioned how to get it.

    Thanks.
  15. 79F05B38-AEA4-4847-B7CC-44353BBB233F
    79F05B38-AEA4-4847-B7CC-44353BBB233F avatar
    24 posts
    Member since:
    Jun 2012

    Posted 11 Nov 2012 Link to this post

    King,

    You're the King!
    I have learned a lot from you!

    Folks,
    check out his work on: http://www.mvccentral.net/
    (no, he is not paying me..)

    Thank you.

    ~ Boris
  16. 527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC
    527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC avatar
    13 posts
    Member since:
    Jan 2011

    Posted 17 Nov 2012 Link to this post

    Hello King,

    what about that CRUD example? Do you think you could make just a short sample on how to implement an update method?
    I am always getting null values in the model on the server!

    Best regards,
    Julian
  17. AB74794F-09D3-4471-8F45-905AC91FDDD0
    AB74794F-09D3-4471-8F45-905AC91FDDD0 avatar
    240 posts
    Member since:
    Oct 2007

    Posted 18 Nov 2012 Link to this post

    I'll try to get to a CRUD example in the next few days.  I'll post the notice here when it's finished.

    Stay tuned!  :^)
  18. 79F05B38-AEA4-4847-B7CC-44353BBB233F
    79F05B38-AEA4-4847-B7CC-44353BBB233F avatar
    24 posts
    Member since:
    Jun 2012

    Posted 21 Nov 2012 Link to this post

    I'm totally interested and waiting for that too..

    ...and if the solution includes a repository with a non-InMemory implementation (LinqToSQL for example) to demonstrate a full CRUD and the optimistic locking technique will be absolutely awesome :)

    ~ B
  19. AB74794F-09D3-4471-8F45-905AC91FDDD0
    AB74794F-09D3-4471-8F45-905AC91FDDD0 avatar
    240 posts
    Member since:
    Oct 2007

    Posted 21 Nov 2012 Link to this post

    To Boris and Julian and others,

    I am still trying to figure this out but I'm finding it difficult to accomplish easily.  Cross domain GETs are easy, it's the PUTs and DELETEs that are giving me problems.

    As far as I understand it from what I've read on the Interweb, to make cross-domain calls work, you need to have an updated browser and use CORS.  I found a good article that describes how to make this work using a Http handler to force the PUTs and DELETEs, but I haven't had luck getting it to work in my sample application.  I know I'm missing something.

    I am building a brand new demo application that hits a SQL Server 2008 database using the Entity Framework and the DbConext. 

    The application is built as an n-Tier application using WebApi 4 in a stand-alone project.  This all works for all VERBS when I use Fiddler, it's just getting it to work from the mobile client that I'm having issues with.

    Over the coming days I will continue to work on this, since I want to be able to use it in my own mobile applications, and I'll post my outcome here, whether it be success or failure.  :^(

    I may even post my project on github.

    Stay tuned.

    Thanks,

    King Wilder
  20. 79F05B38-AEA4-4847-B7CC-44353BBB233F
    79F05B38-AEA4-4847-B7CC-44353BBB233F avatar
    24 posts
    Member since:
    Jun 2012

    Posted 21 Nov 2012 Link to this post

    King,

    Thanks for the great update! I really appreciate it.

    BTW: I'm interested in your approach even for same-domain ajax calls scenarios with WebAPI.
    (REST with WebAPI is much easier; WCF was fine, but it's pipeline was designed mostly around SOAP in my view)

    So, you can have one MVC 4 App doing both:
     1) serving the html5/css3/js for a mobile client with Kendo UI Mobile and
     2) expose within the same app bunch of REST services with Web API serving json (two different routings)

    cross-domain is of course nice to have....

    I think, the way you abstract and encapsulate common behavior in your BaseController and in your IRepository is very cool.

    I've used Albahari's PredicateBuilder to maka my DA Layer more flexible in the past.
    Very interested to hear your opinion on that..

    Looking forward for your solution on github.

    ~ Boris

  21. 527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC
    527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC avatar
    13 posts
    Member since:
    Jan 2011

    Posted 22 Nov 2012 Link to this post

    Hello King,

    and thanks for the update.
    Can you post a sample (good idea about github) even if it doesn't work on your mobile yet?

    Best regards,
    Julian
  22. 79F05B38-AEA4-4847-B7CC-44353BBB233F
    79F05B38-AEA4-4847-B7CC-44353BBB233F avatar
    24 posts
    Member since:
    Jun 2012

    Posted 06 Dec 2012 Link to this post

    King,

    Any luck with the POST, PUT & DELETE verbs on the cross-domain ajax calls?

    ~ Boris
  23. AB74794F-09D3-4471-8F45-905AC91FDDD0
    AB74794F-09D3-4471-8F45-905AC91FDDD0 avatar
    240 posts
    Member since:
    Oct 2007

    Posted 06 Dec 2012 Link to this post

    Boris,

    Not completely, and I haven't been able to work on it for a little while.  I've only been able to get GET's and POST's to work, but not PUT's and DELETE's.

    If I can find the time, I'll try to put it up on github.  I do plan on getting back on it.

    King Wilder
  24. 527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC
    527FBE0A-7381-4BA8-9FD9-7D2E0745E7DC avatar
    13 posts
    Member since:
    Jan 2011

    Posted 06 Dec 2012 Link to this post

    Maybe somebody from Telerik can help us on this task?
    I couldn't find any other server side example using kendo mobile.

    Thanks
    Julian.
  25. F4C5648B-A61E-4438-B1C1-57DB50FD48CA
    F4C5648B-A61E-4438-B1C1-57DB50FD48CA avatar
    33 posts
    Member since:
    May 2006

    Posted 02 Aug 2013 Link to this post

    Hi King, can you post what you did have on github?  I like your approach and would love to see even the posts working

    Bill
Back to Top

This Code Library is part of the product documentation and subject to the respective product license agreement.