SEO Friendly URL Routing in ASP.Net

I have stumbled upon this while answering to this in stackoverflow. So thought of sharing how to do this asp.net webproject.

Expectation / End result :

  1. If you have Search.aspx file in project, it will automatically become SEO friendly, i.e. you can browse it like this  “/Search” instead of “/Search.aspx”.
  2. If you need to pass parameters to that page, say product name, then you can do that using “/Search/Kindle”  instead of “/Search.aspx?productname=Kindle”.

Steps to achieve this:

Step-1

Install “Microsoft.AspNet.FriendlyUrls” from nuget package.

Open package manager console – Help. Then type following –

Install-Package Microsoft.AspNet.FriendlyUrls

Step-2

Then it will automatically add following in RouteConfig.cs.

public static class RouteConfig
{
   public static void RegisterRoutes(RouteCollection routes)
   {
      var settings = new FriendlyUrlSettings();
      settings.AutoRedirectMode = RedirectMode.Permanent;
      routes.EnableFriendlyUrls(settings); 
    }
}

Step-3

Add a webform with name say “Search.aspx”. And now if you browser http://www.example.com/Search, it will hit “Search.aspx”.

Now you are done with making SEO friendly URLS.

More Customization

Part – 1

If you want to make Search.aspx  to be called as “Search-Product”, then you can do that using following.

routes.MapPageRoute("", "Search-Product", "~/Search.aspx");

You need to add this to RouteConfig.cs, just after “routes.Enable…”

Now, if you hit this url – http://www.example.com/search-product, it will hit search.aspx.

Part -2

Now, you may need to pass parameters to Search.aspx. Yes you can do that, use following line instead of above.

  routes.MapPageRoute("Find", "Search-product/{productname}", "~/Search.aspx");

To get value of productname in Search.aspx, use following “Page.RouteData.Values[“productname”]” in page_load or any other event in Search.aspx.

Example

I have created an example. Used code suggested above. Hit following url

Output in code

Hope, it works for all those following this blog.

Advertisements
SEO Friendly URL Routing in ASP.Net

Enabling CORS in WCF

Introduction

This is an intermediate example of WCF as REST based solution and enabling CORS access, so that this WCF service can be consumed from other domains without having cross-domain issues. I will explain more on CORS in latter section, so hold on , read through problem and solution. While developing this similar solution, I have faced issues, and did not find any helpful working Article/blog, so i am posting this. Hope this will be helpful.

Background

We develop WCF service as REST service and consume that using javascript and jquery calls. This is good to start with single page application or purely javascript based application. You will never face any issue as long as wcf service hosted domain remains same as the domain where you have consumer service. The issue arises when, you started allowing other companies to consume WCF service as REST service. For e.g. you have some reporting service and exposed as REST service. You have a web portal , where this is consumed. And since this purely REST based, you want to allow 3rd party companies to consumer REST based service and show same reporting in their website. NOTE: In this case, JS used to consume WCF service will be sitting at client’s domain, but WCF domain will be your domain. And this different domain will cause cross domain issue, i.e. WCF will throw error while invoking.

Using the code

Before jumping straight into code, I want to formally introduce what is REST and CORS issue.

Representational state transfer (REST) is an abstraction of the architecture of the World Wide Web; more precisely, REST is an architectural style consisting of a coordinated set of architectural constraints applied to components, connectors, and data elements, within a distributed hypermedia system. REST ignores the details of component implementation and protocol syntax in order to focus on the roles of components, the constraints upon their interaction with other components, and their interpretation of significant data elements.– http://en.wikipedia.org/wiki/Representational_state_transfer#Framework_implementations

Cross-origin resource sharing(CORS) User agents commonly apply same-origin restrictions to network requests. These restrictions prevent a client-side Web application running from one origin from obtaining data retrieved from another origin, and also limit unsafe HTTP requests that can be automatically launched toward destinations that differ from the running application’s origin. – http://www.w3.org/TR/cors/#introduction

In this example, I will use sample WCF service , that Visual studio provides. First, we will create a WCF REST service, which can accept POST request with parameter as an object. Write a simple JS based APP to consume that. And WCF service will simply return the some prefix + received object value. As we are mainly focusing to enable CORS, I have kept this very basic.

Then I will show you , where exactly error happens. After that, solution for overcoming CORS issue.

Step#1. Lets create a WCF service project, create service contract and operation contract as shown below.

[ServiceContract]
public interface IService1
{

    [OperationContract]
    [WebInvoke(UriTemplate = "/TestMethod", Method = "POST", BodyStyle = WebMessageBodyStyle.Bare, RequestFormat = WebMessageFormat.Json
      )]
    string TestMethod(CompositeType value);

}

Step#2 Definition of CompositeType is –

[DataContract]
public class CompositeType
{
    bool boolValue = true;
    string stringValue = "Hello ";

    [DataMember]
    public bool BoolValue
    {
        get { return boolValue; }
        set { boolValue = value; }
    }

    [DataMember]
    public string StringValue
    {
        get { return stringValue; }
        set { stringValue = value; }
    }
}

Step#3 Then, create service class. Following is the code for this.

public class Service1 : IService1

{
    public string TestMethod(CompositeType value)
    {
        return string.Format("You entered: {0}", value.StringValue);
    }
}

Step#4 Assume it is hosted somewhere ( http://www.example1.com ) and test with fiddler whether it works. Following is the result.

1

Hurray!, it is working fine, see Result – 200 status.

Step#5 I have a simple javascript ( this will be in a HTML file) to invoke this REST based method.  The html file is hosted in – http://localhost Source code for javascript part in html file.

$(document).ready(function () {
	$("button").click(function () {
		alert("clicked");
		var data = $("#txt").val();
		var postdata = {};
		var data_obj = {"BoolValue" : "true" , "StringValue": data}
		postdata["value"] =  data_obj; 

		var url = "https://www.example.com/testwcf/service1.svc/TestMethod";
		$.ajax({
			type: "POST",
			url: url,
			contentType: "application/json; charset=utf-8",
			data: JSON.stringify(postdata),
			dataType: "json",
			success: function(data) {console.log(data);},
			error: function(a,b,c) {console.log(a);}
		});
	});
});

—————–HTML Part————-

Enter something <input id="txt" type="text" /><button>Get WCF data</button>

Now, when i execute this javascript it will throw error. Following is the error message from browser console.

OPTIONS https://www.example.com/wcfv1/service1.svc/TestMethod   test1.html:1
XMLHttpRequest cannot load https://www.example1.com/wcfv1/service1.svc/TestMethod. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. The response had HTTP status code 405.

Following is the browser request payload info.

2

And it is not working anymore with other domain javascript call Frown | :( .

If you closely look into this – we are invoking WCF with “POST” request , but it shows request method as “OPTIONS”. This is because, POST, PUT, DELETE methods are unsafe methods and cross domain requests first makes a preflight request i.e. OPTIONS request to see if that succeeds means server responds/sends OK signal to that , then only it will again make actual POST request.

Also, note that it sends various request headers such as “Access-Control-Request-Headers”, “Access-Control-Request-Method”.

What it means? –  We as wcf service developer need to responds to that OPTIONS htttp request.

How to do that? –  Add global.asax file and add following code to Application_BeginRequest. Following is the code snippet.

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "http://localhost);
    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, PUT, DELETE");

        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
        HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
        HttpContext.Current.Response.End();
    }
}

As you can see from above, i am allowing origin to “http://localhost&#8221;, so that if javascript is placed in this domain and that is making call to WCF, then it will be allowed. Also i have added request response header that we should send as part of OPTIONS Request header.

This is extremely important decision :  You can always use “*” for Access-Control-Allow-Origin, but for security reason that is discouraged. Because you are opening access to all to invoke your WCF server as REST Service from anywhere. Whereas you should know, to whom you are providing access for CORS and put those domains here only.

This is basic thing i am doing here, you can make those thing configurable.

So we are done with this setup, and i am going to deploy this solution, see if that helps.

Conclusion :

Now, i am using same javascript as above , and just hosted changed WCF code into some other virtual directory(testwcf). So when i issue ajax request, see that it has made 2 requests – OPTIONS, POST. Refer below screenshot.

3

We will analyse both request details, so first, see what is OPTIONS request’s response and how that is different from 1st attempt with non-CORS WCF.

4

As you can see that, now our WCF service responded with all required response headers such as “access-control-allow-* ” . – Note: we have done these in global.asax.

So when this request succeed , then browser made 2nd request i.e. actual POST . Lets check the details of that.

5

Now, you can see that, it actually made request payload and see that response header ( see Status code- 200 OK), it succeed and has some content-length.

Points of Interest

If you find it interesting and have some suggestion , put a comment , am i ready to interact with you.

Download source code here.

Enabling CORS in WCF

Azure Mobile Service with Pusher integration (Real Time APP)

Azure Mobile Service: (here after refer as AMS) provides ready to use service for building mobile apps ( android, windows app, iOS) or simple javascript based App. If you planning to build a mobile App having cloud as backend, and you are planing to start directing building App, then it is perfect choice to using AMS. This provides CRUD operation as API having persistent entities. It uses SQL Azure as DB, and exposes API as REST. Following are some of advantages.

  1. CRUD operation with Cloud DB, access API from every where ( client side, server side).
  2. Social Signon integration, no need to write FB,TW API to integrate, just few config, that will make things work.
  3. Notification Hub integration – Send Push notification to any device ( android, iOS, windows ) without caring which format the device will accept.

Pusher : Pusher is a simple hosted API for quickly, easily and securely adding realtime bi-directional functionality via WebSockets to web and mobile apps, or any other Internet connected device. — http://pusher.com/docs . This has support for array of language support libraries.

To Build Real Time with AMS and Pusher – you need to have Azure and Pusher subscription.

What will be the end result – We will build a Collaborative TodoList manager, if multiple users opened same list, then anyone can add/remove/complete the task and it will be seamlessly visible to both.

How we will do this – We will use basic TodoList manager that azure provides and add RealTime functionality to this using Pusher.I will walk you through detailed step to implement this.

1. Login to Azure portal, create a Mobile Service with javascript backend.

2. Select that mobile service , click on Get Started, this will get you to following .

Azure_MS

3. Create todoitem table and download javascript todo App.

4. Create and Login to Pusher account. After loggin in, click on “New App”, it will create an App .You need to note down App ID, Key and Secret. Following is the screenshot for this.

Pusher_key

5. In Azure portal , select created Mobile Service, Click on Data, then select TodoItem table. Click on script, select insert operation. Then you should get following .

todoitem_script

Here you will get inbuilt function called “insert”. To integrate with Pusher, you have to include pusher library using –

var Pusher = require('pusher');

Then on every item insert operation we will push the details to Pusher using following code.

function publishItemCreatedEvent(item) {
 // Ideally these settings would be taken from config
 var pusher = new Pusher({
 appId: 'XXX',
 key: 'XXXXXXXXXXXXXXX',
 secret: 'XXXXXXXXXXXXXXX'
 });
 // Publish event on Pusher channel
 pusher.trigger( 'test_channel', 'OnInsert', item );
 }

Detailed explanation for above –  I have created a pusher instance using required credential such as appid, key, secret. Then i have triggered a message, with a channel name as “test_channel”, an event name as “OnInsert” and required object “item”.

Then we will invoke this function in success handler of insert operation. So that for every todo insert operation , pusher will know something. We will have to do the same steps for update, delete as well.

6. Open “index.html” from downloaded sample , mentioned in step # 3 & add following snippet to the HTML file.

<script src="http://js.pusher.com/2.2/pusher.min.js" type="text/javascript"></script>
 <script type="text/javascript">
 var pusher = new Pusher('PUSHER_KEY');
 var channel = pusher.subscribe('test_channel');
 channel.bind('OnInsert', function(data) {
      alert( "Hooray, Someone created task " + data.text);
      var newelem = $('<li>').attr('data-todoitem-id', data.id)
                             .append($('<button class="item-delete">Delete</button>'))
                             .append($('<input type="checkbox" class="item-complete">')
                             .prop('checked', false))
                             .append($('<div>')
                             .append($('<input class="item-text">')\                             .val(data.text)));
      $('#todo-items').fadeOut().append(newelem).fadeIn(100);
      $('#summary').html('<strong>' + $("#todo-items li").length + '</strong> item(s)');}
 );
 </script>

Detailed explanation for above : First, I have added pusher javascript reference. Then instantiated pusher by providing PUSHER_KEY , subscribed to channel named “test_channel”.

Finally, I have done binding for event “OnInsert” for the above channel, so that if there is insert ->Azure will send Puhser that new item added–>Pusher will intimate this HTML App , as this is subscriber to test_channel and listening to “OnInsert”.

I have uploaded this solution to pastebin, download that and replace with required key – (Pusher keys), and it should start working.

I have already published same article in MSDN  .

Azure Mobile Service with Pusher integration (Real Time APP)