Saturday 20 September 2014

WebApi & Bundling Optimization In MVC3 Project


If you are interested in using  WebApi and Bondling  features in your MVC3 project this post is for you. Fortunately, WebApi and Bondling both have been implemented in individual packages  and are not depends on MVC 4 framework so your are able to use them in your mvc3 project.
Let install following package by Nuget:
Microsoft.AspNet.WebApi
Microsoft.AspNet.Web.Optimization
These packages bring WebApi and Bundling to your project. Easy!!! But still there are few steps to go to make it a lovely solution.
WebApi
Routing mapping is the first step to have a great WebApi combination in our project. I love the way that MVC 4 organized its stuff so lets add a folder to the project and name it App_Start. AddWebApiConfig  file to this folder containing following code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static class WebApiConfig
    {
        public static string DefaultApi = "DefaultApi";
 
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: DefaultApi,
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
 
            // Comment this line if you want to response as a XML
            GlobalConfiguration.Configuration.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
        }
    }
 }
Its the same code you can find in a MVC4 project and its a place to define and map all your webapi routs. If you want  have change default XML serialization to JSON, you should have the last line of thie code in Register method otherwise you must comment it. As you can see, by default WebApi routes start with a “api/” .
Now, lets add a new class in a new file  to the folder with following content:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class RouteConfig
{
    public static string Default = "Default";
 
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
        routes.MapRoute(
            name: Default,
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}
I just extracted and moved the routing configuration to a separated class. By this way it would be easier to manage and maintain.
Third file in the folder is FilterConfig:
1
2
3
4
5
6
7
public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}
Its going to manage our filters in a separated class and file to keep everything clean and tidy.
Last file in this folder would BondleConfig and I will talk about that soon in Bundling.
In the next step we should update the Global.asax.cs file as following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
 
            // confign webapi settings
            WebApiConfig.Register(GlobalConfiguration.Configuration);
 
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
 
            // confign mvc website's routing settings
            RouteConfig.RegisterRoutes(RouteTable.Routes);
 
            // config bundling settings
            // set  debug="false" in   in web.config to have optimization
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
Now we have a nice and tidy start up class which is calling all our configuration class one by one and set up the application. It is done, WebApi is ready to go so lets to create a folder as WebApi in the project and add out first WebApi controller.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class PersonController : ApiController
   {
       // GET api/person
       public List Get()
       {
           return DB.Persons;
       }
 
       // GET api/person/1
       public Person Get(int id)
       {
           var person= DB.Persons.Single(p => p.Id == id);
           if (person != null) return person;
           throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
       }
 
       // POST api/person
       public HttpResponseMessage Post([FromBody] Person person)
       {
           DB.Persons.Add(person);
 
           // Compose location header that tells how to get this person
           // e.g. ~/api/person/5
           var response = Request.CreateResponse(HttpStatusCode.Created, person);
           response.Headers.Location =  new Uri(Url.Link(WebApiConfig.DefaultApi, new { id = person.Id }));
 
           return response;
       }
 
       // PUT api/person/1
       public HttpResponseMessage Put([FromBody] Person person)
       {
           var response = new HttpResponseMessage(HttpStatusCode.NoContent);
 
           var existingPerson = DB.Persons.SingleOrDefault(p => p.Id == person.Id);
           if (existingPerson != null)
           {
               existingPerson.Name = person.Name;
           }
           else
           {
               response = new HttpResponseMessage(HttpStatusCode.NotFound);
           }
 
           return response;
       }
 
       // DELETE api/person/1
       public HttpResponseMessage Delete(int id)
       {
           var response = new HttpResponseMessage(HttpStatusCode.NoContent);
 
           var existingPerson = DB.Persons.SingleOrDefault(p => p.Id == id);
           if (existingPerson != null)
           {
               DB.Persons.Remove(existingPerson);
           }
           else
           {
               response = new HttpResponseMessage(HttpStatusCode.NoContent);
           }
 
           return response;
       }
   }
DB is a fake class that provides us a list of person for testing. I don’t go in the details for the WebApi actions (Mehtods) and leave it for later.
Bundling & Optimization
To use bundling facilities in MVC3 lets add a new class in App_Start folder with the following code to config our bundles.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    // For more information on Bundling, visit http://go.microsoft.com/fwlink/?LinkId=254725
    public static void RegisterBundles(BundleCollection bundles)
    {
 
        bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                    "~/Scripts/jquery-{version}.js"));
 
        bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
                    "~/Scripts/jquery-ui-{version}.js"));
 
        bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                    "~/Scripts/jquery.unobtrusive*",
                    "~/Scripts/jquery.validate*"));
 
        // Use the development version of Modernizr to develop with and learn from. Then, when you're
        // ready for production, use the build tool at http://modernizr.com to pick only the tests you need.
        bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                    "~/Scripts/modernizr-*"));
 
        bundles.Add(new StyleBundle("~/bundles/css").Include("~/Content/site.css"));
 
        bundles.Add(new StyleBundle("~/bundles/themes/base/css").Include(
                    "~/Content/themes/base/jquery.ui.core.css",
                    "~/Content/themes/base/jquery.ui.resizable.css",
                    "~/Content/themes/base/jquery.ui.selectable.css",
                    "~/Content/themes/base/jquery.ui.accordion.css",
                    "~/Content/themes/base/jquery.ui.autocomplete.css",
                    "~/Content/themes/base/jquery.ui.button.css",
                    "~/Content/themes/base/jquery.ui.dialog.css",
                    "~/Content/themes/base/jquery.ui.slider.css",
                    "~/Content/themes/base/jquery.ui.tabs.css",
                    "~/Content/themes/base/jquery.ui.datepicker.css",
                    "~/Content/themes/base/jquery.ui.progressbar.css",
                    "~/Content/themes/base/jquery.ui.theme.css"));
    }
}
I hope you know how bundling works otherwise please have look at here.
Also we have to update _Layout.cshtml  to you our bundle as well.
@using System.Web.Optimization
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<meta charset="utf-8" />
@ViewBag.Title
 
    @Styles.Render("~/bundles/css")
    @Scripts.Render("~/bundles/modernizr")</pre>
<div class="page"><header>
<div id="title">
<h1>My MVC Application</h1>
</div>
<div id="logindisplay">@Html.Partial("_LogOnPartial")</div>
<nav>
<ul id="menu">
    <li>@Html.ActionLink("Home", "Index", "Home")</li>
    <li>@Html.ActionLink("About", "About", "Home")</li>
</ul>
</nav></header><section id="main">@RenderBody()</section><footer></footer></div>
<pre>    @Scripts.Render("~/bundles/jquery")
 
    @RenderSection("scripts", required: false)
To use bundle helper class you have to add the its namespace to the page as following which is not a nice idea.
1
@using System.Web.Optimization
Undoubtedly, updating web.config file in View folder is a better solution. so you should add System.Web.Optimization to the list in system.web.webPages.razor section.
I hope you enjoy these lovely features in your project. you can download the sample project formhere

No comments:

Post a Comment

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...