Monday, May 1, 2017

Asp.net Core/MVC routing

Routing is necessary for controlling the accessible path in the URL.

1) In the Startup.Configure proc, you can add a route to "ContactUs" that looks like this:

            app.UseMvc(route =>
            {
                // adding a new route for http://localhost:57477/contactUs
                route.MapRoute(name: "contact_us",
                            template: "contactUs",
                            defaults: new { controller = "Home", action = "ContactUs" });

                // if the path in the url is blank, call HomeController.
                route.MapRoute(name: "default",
                                template: "{controller=Home}/{action=Index}/{id?}");

            });

And the HomeController looks like below. Simple and straight forward.

    public class HomeController : Controller
    {
        // returns the default page.
        public IActionResult Index()
        {
            return View();
        }

        //returns the contact us page.
        public IActionResult ContactUs()
        {
            return View();
        }
    }

2) You may also add a new route by adding HttpGet attribute to the method and add Route attribute to the class.

    [Route("[controller]")]
    public class ValuesController : Controller
    {
        // GET values
        // http://localhost:57477/values/
        [HttpGet]
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET values/5
        // http://localhost:57477/values/7890
        [HttpGet("{id}")]
        public string Get(int id)
        {
            return "value=" + id.ToString();
        }

        // GET values/getjson/5
        //http://localhost:57477/values/getjson/1234
        // returns the data in json format.
        [HttpGet("GetJson/{id?}")]
        public IActionResult GetJson(int id)
        {
            var v = new { v = "value=" + id.ToString() };
            return new ObjectResult(v);
        }
    }

Tuesday, April 25, 2017

Compiling asp.net 3.5 manually

I have a ASP.Net web project that was done in VS2010 and now I want to publish it with VS2015. Then, the compiler complained that there are many compilation errors such as CS0117 (method or extension method not found) and CS0246 (type or namespace not found). After hours of research on the web, still cannot find any solution to have VS2015 to publish the ASP.net 3.5 project.

Finally, I decided not to use VS2015 to publish the old project. Instead using command line to publish it even though the steps are a bit lengthy.

Here is the steps:

1. Run VS2015 and open the ASP.net 3.5 web project.
2. Choose Release mode.
3. Choose Rebuild Solution.
4. Upon rebuild done, copy the entire website folder into "d:\manual-compile" folder (or whichever folder that you like).
5. Then, run the following command in command prompt:

C:\Windows\Microsoft.NET\Framework\v2.0.50727\aspnet_compiler.exe -v /website1 -p d:\manual-compile\website1 -u -fixednames d:\manual-compile\website1-compiled


Wait for a while and you will see the precompiled version of ASP.net appeared in "d:\manual-compile\website1-compiled" folder. There you go.


Creating Asp.net Core/MVC from a miminal Asp.net Core project template

Creating a new project from the Web Application template is troublesome in deleting the unwanted files. Let's try to create a minimal Asp.net Core project (not an Empty project).

First thing, you have to create a Web API Asp.net Core project and then follow the following steps to complete the project.

1. Add HomeController class into Controllers folder and the code is shown as follow:

     public class HomeController : Controller
    {
        // returns the default page.
        public IActionResult Index()
        {
            return View();
        }

        //returns the contact us page.
        public IActionResult ContactUs()
        {
            return View();
        }
    }

2. Modify the Configure() in Startup.cs

            app.UseMvc(route =>
            {
                // adding a new route for http://localhost:xxx/contactUs
                route.MapRoute(name: "contact_us",
                            template: "contactUs",
                            defaults: new { controller = "Home", action = "ContactUs" });

                // if the path in the url is blank, call HomeController.
                route.MapRoute(name: "default",
                                template: "{controller=Home}/{action=Index}/{id?}");

            });

3.  Add Index.cshtml and ContactUs.cshtml into Views\Home folder.

In the Index page,  it looks like this:

    @{
        ViewData["Title"] = "Home";
    }

    <h1>MY APP</h1>
    <h2>This is Home page</h2>
    <a href="ContactUs">Jump to Contact Us page</a>

In the ContactUs page, it looks like this:

    @{
        ViewData["Title"] = "Contact Us";
    }

    <h1>MY APP</h1>
    <h2>This is Contact Us page</h2>
    <a href="">Jump to Home page</a>

4. Add _Layout.cshtml into Views\Shared folder and the content looks like this:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />

        <title>@ViewData["Title"] - My App </title>
           
        <environment names="Development">
            <link rel="stylesheet" href="~/css/site.css" />
        </environment>
        <environment names="Staging,Production">
            <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
        </environment>

    </head>
    <body>
   
        <div class="container body-content">
            @RenderBody()

            <hr />
            <footer>
                <p>&copy; 2016 - My App</p>
            </footer>
        </div>

        <environment names="Development">
            <script src="~/lib/jquery/dist/jquery.js"></script>
            <script src="~/js/site.js" asp-append-version="true"></script>
        </environment>
        <environment names="Staging,Production">
            <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
                    asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
                    asp-fallback-test="window.jQuery">
            </script>
            <script src="~/js/site.min.js" asp-append-version="true"></script>
        </environment>       

        @RenderSection("scripts", required: false)
    </body>
    </html>

5. Add _ViewStart.cshtml into Views folder with the following content. Now, the Asp.net will merge all the views with this "_Layout.cshtml".

    @{
        Layout = "_Layout";
    }

6. Add site.css into wwwroot\css folder with the following contents:

    body {
        font-family:Tahoma;
        font-size: 12pt;
        color: #757575;
        margin: 0;
    }

7. Press F5 to run your website.

8. You might notice that the site.css was not loaded. Let's continue by calling up the NuGet Package Manager. Search the "Microsoft.AspNetCore.StaticFiles" and include it into your project.

9. In Configure() in the Startup.cs file, add the following code before the app.UseMvc()

    app.UseStaticFiles();

10. Press F5 to run your website and the css file will be loaded into the browser.


Tuesday, February 7, 2017

Something about Asp.net Core/MVC

This is something that you should know about Asp.net Core:

1) To create a link that points to the correct virtual directory, you can use the following:

  <a href="~/api/mybook">See my books</a>

OR

   var s = " root is= " + Url.Content("~/");

2) To expose the controller class to the desired URL path, you have to add the "Route" class attribute. In the following case, "MyBook" service is accessible through "http://localhost:1234/api/MyBook" while "Values" is accessible through "http://localhost:1234/values/"

   [Route("api/[controller]")
   public class MyBookController : Controller {..}

    [Route("[controller]")]
    public class ValuesController : Controller {..}


In Startup.cs:

            app.UseMvc(routes =>
            {              
                // set the default route
                routes.MapRoute(
                        name: "default",
                        template: "{controller=Home}/{action=Index}/{id?}"
                    );
            });

3) To publish the static files, you must called out the NuGet and then add the reference to this library "Microsoft.AspNetCore.StaticFiles". After that, you will be able to use the code in Startup.Configure() proc:

   app.UseStaticFiles();

4) To setup the application in IIS, first, you need to add an application pool with "No Managed Code" option. Then, publish a new virtual application that points to the output path. For example,

   src\WebApplication1\bin\Release\PublishOutput

5)

5.1) To return the HTML content, you must return the View() and the Index.cshtml file must be stored in the Views\MyBook folder.

        [HttpGet]
        public ActionResult Index()
        {
            var l = this._my_book.GetList();
            return View(l);
        }

5.2) To return the value as plain content type:

       [HttpGet("{id}")]
        public string Get(int id)
        {
            return "value=" + id.ToString();
        }

5.3) To return the data in JSON format:

        [HttpGet("{id}", Name = "GetTodo")]
        public IActionResult GetById(string id)
        {
            var item = TodoItems.Find(id);
            if (item == null)
            {               
                return NotFound();
            }

            return new ObjectResult(item);
        }

6) Receiving the dynamic JObject param:

          [HttpPost]
          public System.Net.Http.HttpResponseMessage Post([FromBody]Newtonsoft.Json.Linq.JObject value)
          {
             //...
          }

7)

protected bool CheckStatus304(DateTime lastModified)
        {
            //http://weblogs.asp.net/jeff/304-your-images-from-a-database
            if (!String.IsNullOrEmpty(Request.Headers["If-Modified-Since"]))
            {
                CultureInfo provider = CultureInfo.InvariantCulture;
                var lastMod = DateTime.ParseExact(Request.Headers["If-Modified-Since"], "r", provider).ToLocalTime();
                if (lastMod == lastModified.AddMilliseconds(-lastModified.Millisecond))
                {
                    Response.StatusCode = 304;
                    //Response.StatusDescription = "Not Modified";
                    return true;
                }
            }

            //Response.Cache.SetCacheability(HttpCacheability.Public);
            //Response.Cache.SetLastModified(lastModified);

            //use this instead of the above:
            //string timeString = lastModified.ToUniversalTime().ToString("R");           
            //Response.Headers.Append("Last-Modified", timeString);

            return false;
        }

Thursday, January 19, 2017

Setting up the delete permission in IIS

The following content was copied from this reference:

http://stackoverflow.com/questions/7334216/iis7-permissions-overview-applicationpoolidentity

ApplicationPoolIdentity is actually the best practice to use in IIS7. It is a dynamically created, unprivelaged account. To add file system security for a particular application pool see IIS.net's "Application Pool Identities". The quick version:

If you application pool is named "DefaultAppPool" (just replace this text below if it is named differently)

    Open Windows Explorer
    Select a file or directory.
    Right click the file and select "Properties"
    Select the "Security" tab
    Click the "Edit" and then "Add" button
    Click the "Locations" button and make sure you select the local machine. (Not the Windows domain if the server belongs to one.)
    Enter "IIS AppPool\DefaultAppPool" in the "Enter the object names to select:" text box. (Don't forget to change "DefaultAppPool" here to whatever you named your application pool.)
    Click the "Check Names" button and click "OK".


Tuesday, October 4, 2016

Using partial HTML UI + ASHX to speed up page loading

Just to share with everyone that I developed many LOB app (line of business application) and I'm still developing new LOB for my clients. LOB is very different from blog engine, corporate websites and static websites. In LOB, we can happily ignore the contents to be "readable" by SEO because some of the contents were loaded by AJAX.

As per my last blog dated 26th-June-2016, I mentioned the new strategy: JQuery + AJAX + partial HTML UI. Now, the question is how is partial HTML UI that can help out is speeding up the page loading? The answer is simple, we need to rely on the browser cache by checking the "If-Modified-Since" flag in the request header. Then, responding either status code 304 (resource has not been modified) or returning the partial HTML to the browser.

You will find tons of references if you are searching for "asp.net If-Modified-Since". Below is one of the reference that I found:

  http://madskristensen.net/post/use-if-modified-since-header-in-aspnet

The down side of this strategy is that the user will feel a bit slower on the first request to load the full page. But, the subsequent page loading or if the user is requesting for the same page, then, the time taken will be shorter. For example, we want to develop a page for user to key in the sales invoice and it allows the user from choosing the item from the list. The sales invoice is stored in a HTML file (not ASPX) and the item list HTML design is stored in another HTML file (where this item list will be reused by supplier invoice).

One of the advantage using this strategy is that it allows all these partial HTML file to be hosted in CDN (Content Delivery Network). Then, the whole LOB app will be loaded faster than using only one ASPX which could be crazily huge and hard to reuse some of the HTML design.

Note: "partial HTML UI" can be refer as "template" and it does not contains the HEAD and BODY tags. It just contains some DIV-s which eases the web designer to design and test in the browser. You don't need a programmer to start full coding but just some simple JQuery and AJAX to complete the demo.


Tuesday, August 2, 2016

Color settings in VS2015

After working years and years on computer, it's time to adjust the color in VS2015. This can be done by selecting Options from Tools menu. Then, look for "Fonts and Colors" under Environment and you may start editing the colors for "Text Edit":

  • Plan text - adjust the background color to RGB(228, 228, 228)
I'm adjusting the background color to green color for the following options:
  • Brace Matching RGB(0, 185, 0)
  • Brace Matching (Rectangle) RGB(0, 128, 0)
  • Hightlighted Definition RGB(185, 194, 154)
  • Hightlighted Reference RGB(249, 182, 100)
You may have to adjust the text color to any color that you like:
  • Preprocessor keyword
  • User Types - xxx
You may also need these add-on:

ColorThemeEditor.vsix - to change the VS color scheme.

https://marketplace.visualstudio.com/items?itemName=VisualStudioProductTeam.VisualStudio2015ColorThemeEditor

VisualCommander_263.vsix - to automate some text to be injected and binding the command to shortcut key.

https://marketplace.visualstudio.com/items?itemName=SergeyVlasov.VisualCommander

Sample code to inject current date, time & my name to the text editor:

Sub Run(DTE As EnvDTE80.DTE2, package As Microsoft.VisualStudio.Shell.Package) Implements VisualCommanderExt.ICommand.Run

        Dim textSelection As EnvDTE.TextSelection
        textSelection = CType(DTE.ActiveDocument.Selection(), EnvDTE.TextSelection)
        textSelection.Text = "//" + System.DateTime.Now.ToString("d.MMM.yy") + ", myName-"

    End Sub

Don't forget to customize the "keyboard" and bind it to any shortcut that you like. As for myself, I reset CTRL+[0 to 9] and bind it with my command.