Using the WCF webHttpBinding with ASP.NET Membership providers, Part 1
Part 2: How to secure the service operations using an ASP.NET membership provider
Table of contents
Introduction
Dear reader, the title is trying to give an introduction into a topic that I have been working on recently. The title could be different, as there are many aspects of what this article is about, therefore I would like to narrative introduce the topic of this article. The first thing is to identify the problem. The "what it is" is harder to define here than it is on other problems so let me just list the aspects of this article:You should keep on reading this article if:
- You want to implement an application that publishes a web service using Microsoft .NET 3.0+.
- You want to create the web service utilizing the Windows Communication Foundation (WCF) classes.
- You want to provide URLs for each operation (here: or access it via HTTP GET) of the web service.
- You would like to consume the web service from a website using JavaScript in an AJAX like style.
- You authenticate against the website and the web service using a standard or custom ASP.NET MembershipProvider of any kind.
- You DON'T need a SOAP-web service and want to return data in a plain XML or a JSON serialized form.
You don't need to read this article if:
- You are trying to develop a SOAP-web service.
- You want to know how to enable SSL on your web service.
- You want to know how to write an ASP.NET Membership provider.
If you don't need to read it I still invite you to :) I will start by trying to describe the problem. After that, I will give a solution and then discuss the aspects of the solution. Let's start by defining the problem.
Problem: Making service operations accessible via HTTP Get or Post.
1. You want to write a web service using WCF and make the operations of the service accessible via HTTP Get-Requests. This could be useful if you want to call the operation by typing a URL into a browser address field. Another scenario would be that you would like to write a client in JavaScript that sends http requests to the URLs of these operations asynchronously (AJAX like).
Solution
Create an assembly that contains the contract and the implementation of your web service. As a project template choose web application project. In the web application project add a *.svc file, that references your service, and add an endpoint definition to your web.config file. There you will specify the binding for your service and configure how the service will behave.
If you know how to implement WCF *.SVC services you won't have problems to create the project yourself quickly using the prior instructions. All in all the result will look something like this:
As you can see, in the web project I have created a subfolder that contains my service; the rest can stay in the root folder of your application. I Let's see how you create all this in separate steps.
1. Create a service contract.
Note that you must add a reference to the System.ServiceModel assemblies first, as they contain the classes used by the WCF. You can do this by right clicking the project folder and choosing "Add Reference". In the following dialog select the ".NET"-tab. Choose "System.ServiceModel" and "System.ServiceModel.Web" from the list and click OK.
using System.ServiceModel;
using System.ServiceModel.Web;
namespace BindingTest.Service
{
[ServiceContract(Name = "MyHelloWorldService")]
public interface IMyWebService
{
[OperationContract]
string HelloXmlWorld();
[OperationContract]
string HelloJsonWorld();
}
}
2. Create the implementation of the service.
using System.ServiceModel.Activation;
namespace BindingTest.Service
{
public class MyWebService : IMyWebService
{
public string HelloXmlWorld() { return "Hello Xml World!"; }
public string HelloJsonWorld() { return "Hello, JSON World!"; }
}
}
3. Add a service hosting file to your web application.
If you have visual studio 2008 installed you will find the *.svc file template when right clicking the project and choosing Add --> New Item. I don't actually know the caption for it, I think it says something with Web Service but I am not sure, as I am currently writing this using VS05. If you cant find it, read on;
If you are using Visual Studio 2005 like me just add a new *.txt file. In the Name-Field rename the file to
MyWebService.svcOpen the file and add the following content:
<%@ServiceHost language="C#" Debug="true" Service="BindingTest.Service.MyWebService" %>
4. Change the web applications web.config so it hosts your service. Add the following service endpoint configuration in the <system.serviceModel> section
<system.serviceModel>
<services>
<service name="BindingTest.Service.MyWebService">
<endpoint address=""
binding="webHttpBinding"
contract="BindingTest.Service.IMyWebService"
</service>
</services>
</system.serviceModel>
5. Make the methods accessible via HTTP GET or HTTP POST
Here, some more steps are needed!
- Enable Web Script, add the following service behavior configuration to your web.config (somewhere in the <system.serviceModel>):
<behaviors>
<endpointBehaviors>
<behavior name="EnableGetMethods">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
- Adjust your endpoint, so it uses the behavior, that you just added. For this, add the following line to your service endpoint configuration as an attribute of the <endpoint>-Element:
behaviorConfiguration="EnableGetMethods"
- Enable your service to be accessed via HTTP Methods.
For this, add the following attributes to the operations in your service contract.
[WebGet]
...means that this method can be accessed via HTTP GET,
[WebInvoke]
...means that this method can be accessed via HTTP POST.
The Format you choose is actually self explanatory. If the RequestFormat is set to Json this method will return Json-encoded data, when set tp Xml it's Xml.
using System.ServiceModel;
using System.ServiceModel.Web;
namespace BindingTest.Service
{
[ServiceContract(Name = "MyHelloWorldService")]
public interface IMyWebService
{
[OperationContract]
[WebGet(RequestFormat = WebMessageFormat.Xml,
ResponseFormat = WebMessageFormat.Xml)]
string HelloXmlWorld();
[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
string HelloJsonWorld();
}
}
6. Test your service!
Go ahead, build the solution. After that start your hosting project. You can either do that by clicking the Play Button, pressing F5 or hosting the project in IIS. If you set up everything correctly, you should be able to access everything via your browser. To do so, open your favorite browser and type in:
http://localhost:1349/Service/MyWebService.svc/
Note that the port number behind the localhost depends on your project settings. The Visual Studio Development Webserver automatically assigns a random port number. I set mine to be 1349, if you are using my solution it will be the same. If you created the project files yourself you can find out the port number of your website project by right clicking the project in your solution explorer and choosing Properties --> Web.
If everything went fine you will see something like this:
This indicates that your service was created successfully. If you see this file you can go even further. Try typing these two addresses in the browser address field:
http://localhost:1349/Service/MyWebService.svc/HelloXmlWorld
http://localhost:1349/Service/MyWebService.svc/HelloJsonWorld
If everything went right you will see the following pages:
If you can see this Message your service operation is running correctly. Access it using HTTP POST.
The first image shows an Xml-Document that was returned. Your service was therefore created and built correctly.
The second image indicates also that everything went right, although the statement might be different. The statement is something like: "The method is not allowed". The word "method" here refers to the access method, and not to the method you are trying to call.
So the method of your web service implementation class (the "web service operation") is just fine, but the method used to access it is not allowed. When typing the URL you are accessing the service using the GET Method of HTTP. This method was prohibited by us, because we used the [WebInvoke] Attribute on the contract, which only allows this method to be accessed using HTTP POST like this:
[OperationContract]
[WebInvoke(RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
Congratulations, you have provided your first web service via HTTP methods.
7. Write a client website that consumes your service
- Add a webform to your host project. Right click your project and choose Add --> New Item --> Web Form, in the name field rename it to "<filename>.aspx". In my example I already have a Default.aspx. You can use this file if you haven't deleted it.
- To consume the web service let's use a JavaScript framework. Add the framework of
your choice by downloading the appropriate *.js file and adding it by using Add
--> Existing Item, then browse to the file.
I like MooTools a lot, but jQuery would be fine as well. Note that I don't provide jQuery code here, but you will find the awesome documentation of it helpful. If you want to get MooTools in order to follow this example you can download it here.
Depending on your browser the file might be renamed to *.js.txt, rename it to *.js before adding it to the project folder. .
- As you can see in the screenshot of the project at the beginning of this article I saved the MooTools framework into a subfolder. This will come in handy later so I recommend you do it as well.
- To the Default.aspx file add the following code in the header segment of the html
code:
<script type="text/javascript" src="Scripts/mootools-1.2.4-core-yc.js"></script> <script type="text/javascript"> var baseUrl = "http://localhost:1350/Service/MyWebService.svc/"; window.addEvent("domready", function () { new Request({ url : baseUrl + "HelloXmlWorld", method : "get", onSuccess : function (text, xml) { alert(text); } }).send(); new Request.JSON({ url : baseUrl + "HelloJsonWorld", method : "post", onSuccess : function (json, text) { alert(text); } }).send(); }); </script> -
After that right click the Default.aspx and choose "View in Browser". If you set up everything correctly, your browser will open up and display something like this:
Hello Xml World!
If this Message pops up when visiting Default.aspx the JavaScript code was installed correctly.
Discussion
The concept
This first part has a simple solution that might have shown several implementations
that were new to you, as they were for me, when I was first trying to build an
architecture like this. I would argue that the architecture that resulted from the
described implementation
looks something like the following diagram:
First, we have a host. The host is a web application with whom consumers (websites in the same domain) communicate, in order to fetch data from it. They do this, by calling the methods of the service directly by their names, which are publically accessible. The data would usually originate from the business logic, who ultimatively would retrieve it from some sort of database (if you are in a classical three layer architecture). The data is packed into special packages (not to say messages) such as XML or JSON encoded strings. This functionality is provided by the classes of the WCF without your actually having to do anything.
The implementation
Let's have a look at the actual implementations that we just created.
Endpoint configuration of the service host.
As you might have noticed we did not assign an address to the service endpoint. That is, because the host already offers an address for the service. The key is the *.svc file which defines the address (URL) where the service will be opened. It is relative to the web server root folder, that hosts the project, as the *.svc file is managed by that server.
The webHttpBinding
You will have noticed that we used a very special binding. Be sure to have the binding set to webHttpBinding - not wsHttpBinding or anything else! Why did you never hear about it before you ask? That is actually a very good question. Although it is one of the pre-configured bindings that comes with the WCF I found this binding to be treated like a disliked relative or something. I found that most developers find this binding pretty strange or even esoteric and solve most of their problems using the wsHttpBinding. And I must say - I understand why it is like that. One reason would be the poor support for security as it only supports Transport security, which for the web 2.0 isn't the preferred way of securing your stuff. Maybe it was back in the old web 1.0 days. We will see more of that in the section "Downsides" of this discussion and also the second part of this article.
For our purposes it is the only standard binding that is applicable besides writing a custom binding. It supports a "Deep Linking" for the service operations, which would mean as much as "providing a URL for each service operation". This is achieved by applying the <enableWebScript />-Element to the binding configuration. This element can only be added to a binding configuration of webHttpBinding.
The binding doesn't serialize the passed data with the same overhead as a SOAP-Envelope would generate and supports just sending custom data, serialized as plain xml or JSON. Some people call services like this POX-Services or even POX-ReST services, but there is actually more to that definition than this article can cover.
By the way: POX is an acronym for Plain Old XML.POX-objects are opposed to complex XML schemas such as those provided by RDF or the WS-* specification that are for example generated by the wsHttpBinding or the BasicHttpBinding (which, despite its name produces a WS-1 BP output).
For more information on these attributes visit its MSDN documentation pages:
<webHttpBinding>-Element
webHttpBindingClassUsing the [WebGet] and [WebInvoke] Attributes
In order to define which HTTP-Method can be used to access the service operations we have to apply the [WebGet] or the [WebInvoke]-Attributes to the operations in our service contract. The [WebGet]-Attribute allows a method do be accessed using HTTP GET. Also, Parameters can be send using GET, provided in a form of
http://Server/Service/Method?param1=val1¶m2=val2
which means that all parameters that go into the operation can be taken from the URL. This allows a "Deep Linking", in case the service returns the same result depending on the parameter values. The post method works in the same way, but doesn't show the parameters in the URL.The Attributes themselves only define which method will be used to access them. The type of payload that they create is defined by their parameters:
[WebInvoke(RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
Here we specified, that this Operation (accessed via HTTP POST) will accept incoming requests as JSON and return a Response also encoded in JSON. Note that the serialization works not only for strings a methods output parameter but also for MessageContracts or any other object that is marked [Serializable]. In fact the format just specifies the type of formatter that the WCF will call upon execution of this operation in order to serialize and deserialize the responses and requests.
There is also the chance of configuring the URL that comes in (similar to URL rewriting).For more information on these attributes visit their MSDN documentation page:
WebGetAttributeClass
WebInvokeAttributeClassThe <enableWebScript>-Element
The <enableWebScript>-Element must be applied before using the [WebGet] and [WebInvoke] Attributes to your operation contracts, otherwise you will get a result which says something like "Destination unreachable". Without this element both contract attributes don't work.
For more information on this element visit its MSDN documentation:
http://msdn.microsoft.com/en-us/library/bb675191.aspxConsidering MooTools
You might ask: Why are you not using jQuery as a framework? That is for no reason actually. One thing like better about MooTools is that it offers a complete Class-Hierarchy and inheritance model, rather than just a heavy fluent-api for javascript. There are sh*t-loads of plug-ins available and MooTools has no need to hide before jQuery. Give it a try.
The shown code starts two requests. The root URL is the same, but one wants to consume JSON and the other wants to consume XML. There is an abstraction for that within the MooTools framework, as you can clearly see. You can set the HTTP methods and the handlers. Also you can set the request headers (not shown) and further event handlers.
Benefits
The shown architecture has some benefits:
- It is easy to build, which is done mostly declaratively in the web.config.
- It can be tested using a browser.
- It is very flexible and versatile.
- It doesn't have as much overhead as others.
- When utilizing HTTP GET you can have a Deep Linking for your operations and their parameters.
- You have the flexibility of WCF at your hands.
- Doesn't need complex interpretation on the client side, thus resulting in very sleek clients, as long as they understand JSON or XML.
- The last point results in very thin clients.
Downsides
Of course, there are downsides to this approach:
- There is no real security. The example which is shown here doesn't offer any security, and should only be used for public services. One example would be offering RSS or ATOM feeds for a website, that is publicly visible anyway.
- Extending security is a pain - which will be discussed in part 2 of this article.
- In case of JavaScript clients cross domain calls will cause serious problems, however this is a problem of the client and not of the service.
- There is WSDL support available but still it might be a problem when generating proxies...
- ...especially if you are using custom data formats in XML or JSON.
Alternatives
If you would like to use the same approach there are hardly any choices, as the [WebGet] and [WebInvoke]-Attributes require the <enableWebScript>-to be set to configure the endpoint. It should be applied only to the webHttpBinding. I don't know if it works with other bindings but I think using the same approach (Get/Post) would require you to write your own custom binding.
Because of the security reasons I would not recommend using this approach for internet applications that are supposed to be private. Here I would rather use a wsHttpBinding which supports message-security.
Further resources
Die wunderliche Wankelmütigkeit eines sonst so braven Fuchses.
Ähnlich wars mit Nero. Oh Gott habe ich Nero geliebt, bis ich dann nach Jahren den Wortwitz verstanden habe - NERO BURNING ROM - Nero burning Rome . Dadurch erklärte sich auch das schmucke Desktop-Icon von Nero, welches ein brennendes Kolosseum zeigt. Das ist intelligent, lustig und falsch zugleich, denn Nero starb im Jahre 68 n. Chr., das Kolosseum in Rom wurde aber erst zwischen 72 und 80 n.Chr. erbaut. Wie auch immer, Nero war mal echt toll, man konnte mit einem effizienten und mächtigen Dialog sofort alls brennen. Dann kam langsam dieses sau blöde Nero Express, das alle Funktionen hinter irgendwelchen Zeichen verstecken wollte. Nach und nach, durch Hinzufügen der nichtsnutzigen Backup-, Bildbearbeitungs- und Videotools, etc. wurde Nero langsam zu einer überfrachteten, riesigen Mega-Suite, die sich erst nach 5 Minuten öffnete, wenn man nur 2 Minuten lang eine CD brennen wollte. Ich reserviere hier an dieser Stelle mal provokativ den Begriff "Schnapsbrennerei" für diesen "Zustand". Doch halt, was ist seither geschehen? Wir schreiben das Jahr 2010 und Nero hat einen Schritt Back to the roots gemacht! Wir sind bei Version 9 und es gibt sogar eine - kostenlose - Lite Version von Nero. Brennen Kopieren. Sonst nix, kthxbye! Super gemacht, Nero Ag., kleine Software, die macht was sie soll, das finde ich ansprechend. Dass es kostenlos ist wird dabei nebensächlich, immerhin war Nero eine Weile lang mal bei jedem Brenner dabei. Wer möchte kann sich die Lite Version hier herunterladen.
Updates sind was Feines, aber halt auch nicht immer, wie unser Negativbeispiel am Anfang gezeigt hat. Was aber viel schlimmer ist als eine nach einem Update komplett ausgewechselte Software, ist eine, die gleich aussieht - sich aber anders verhält. So geschehen bei fast allen Firefox Versionen an die ich mich erinnere - 1,2,3,3.5 und nun 3.6. Womit wir nun beim Thema wären.
Besonders meine ich, war es die Version 3. Ja, hier muss es wohl gewesen sein, als mir das Update das erste Mal auf die Nerven ging: Tabs - eine tolle Sache wenn ihr mich fragt - macht man auf und zu, schwuppdiwupp, klicksklacksschwupps, und sie öffnen einem das Internet in nur einem einzigen Windows-Fenster. Toll! Wenn einen eine Seite nervt, dann kann man sie zumachen. Wenn einen aber ganz viele Seiten nerven, was macht man dann? Man macht sie nacheinander zu. Im Firefox 2 konnte man das kleine [x] oben rechts mehrmals anklicken und der reihen nach wurden die einzelnen Nervtabs gekillt. Das ging dann ab Version 3 nicht mehr. Meine erste Reaktion: Himmel hilf, wer hat denn die [x]-Buttons an die Tabs selbst geklebt? Nach ein wenig suchen in der about:config fand ich heraus, dass man
browser.tabs.closeButtons verändert hatte. Durch setzen dieser Einstellung auf 3 kann man die Buttons wieder
in die obere rechte Ecke befördern. Wer Tabs durch anklicken schließen will: Einfach
mit der mittleren Maustaste draufklicken. BAM!!! Wer keine hat: Mausrad senkrecht
nach unten drücken - das ist die mittlere Taste, für alle die dies nicht wissen.
Genauso nervte mich gerade Firefox, Version 3.6. Ich hab den Internet Explorer immer ausgelacht, weil der neue Tabs direkt hinter dem gerade aktiven Tab aufmacht. Während ich mehrere Dokumente geöffnet habe, halte ich sehr gerne eine Reihenfolge ein, in der diese abgearbeitet werden. Alle anderen werden hinten angefügt und die Arbeit geht FIFO - first in - first out - weiter. Der Internet Explorer zerstört meine gedankliche Queue die ich dabei aufbaue, indem er neue Elemente in die Mitte der Queue einfügt. Genau wie das der neue Firefox 3.6 tut. BAM!! Mensch nervt mich das. Zum Glück gibt es weitere mit diesem Problem gepeitschte Nutzer, sonst hätte ich wohl wieder ne Ewigkeit Config blättern dürfen. Die Zeit, die ich dadurch gespart habe, habe ich übrigens investiert um diesen Artikel zu schreiben. Wie gewonnen, so geronnen, sagte der Vampir!
Das Problem lässt sich lösen, indem einfach
browser.tabs.insertRelatedAfterCurrent auf false gesetzt wird.
Danke für den Tipp an Perun von webwork-tools.de!
Ich bin mal gespannt, welche Überraschungen Firefox 4 für uns bereit hält. Manchmal glaube ich die einzige Konstante in meinem Leben ist der Nahostkonflikt...
Hier noch eine Zusammenfassung der Einstellungen:
browser.tabs.closeButtons = 3 : Verschiebt den "Schließen"-Knopf von den Tabs in die obere, rechte Ecke.
browser.tabs.insertRelatedAfterCurrent = false : Öffnet neue Tabs am Ende der bereits geöffneten.
IDEs compared to personal hygiene
Using a sophisticated IDE can be compared to terms of anal hygiene. Think about it for one minute:
There are actually people, that insist on building their apps in Vi or Vim. Those can be compared to desert nomads in terms of anal hygiene, as they are known of utilizing desert sand to wipe. For me, the imagination of doing projects with more than 3 files and 500+ lines of code in a white-on-black-no-mouse-vi-interface gives me the very same sensation as the idea of rubbing desert sand into my buttcrack. I guess that both feels pretty much the same. Also, I bet, both methods will leave a lot of dust and sand that will that will be a real pain when you are doing your business the next time.
During my studies we had to use Ultra Edit. That one can be compared to single layer American toilette paper. It is lightweight, cheap, rough, uncomfortable and you have to fold it several times until it suits your custom needs but it will eventually get the job done.
Some more layers applied will get you to Eclipse. Yeah, eclipse is a 4 layer super tissue with a very soft touch, a perfumed scent and a nice funny children's print on top, that will make it look adorable... It is nice to use, you need less time to do the same stuff, it offers you great, comfortable support for what you wanna do.
And now the ultimate superlative:
Imagine a toilette that shoots colored, scented rose-water up your butt, while tuning into your favorite radio station while you're doing business! The toilet itself is made out of diamonds, coated with pure, solid gold. When you enter the bathroom it greets you and opens up the lid for you. It is pre-warmed so your behind doesn't ever get cold. It is very comfortable to sit on, as its shape is optimized to perfectly fit your buttocks. It offers you perfect service. It detects when you are done pooping and raises the water level so your falling feces will not splash the toilette water up your butt. You don't need to actually wipe - the toilette does it for you. There is a very sleek and comfortable mechanism that cleans out the groove between your gluteal muscles. It also applies special cooling ointment that makes your bunghole smell really, really awesome making anyone ask "Wow, what smells so good in here" and you will blush heavily because you don't dare to tell them it is actually your rectum. The special toilette automatically detects the type of your poo and analyzes its consistency and content. It then tells you if you need more iron, copper, sulfide or other minerals and adjusts your diet to these needs by interacting with your fridge.
If you can imagine that toilette, than you basically know what it is like to use Visual Studio 2010 Ultimate.
Die unbeliebteste Programmiersprache aller Zeiten
Nein, obwohl ich sie nicht leiden kann, ist Java davon weit entfernt, die unbeliebteste Sprache zu sein. Java liegt dabei sogar hinter Visual Basic und COBOL, die doch wesentlich unbeliebter sind.
Ich schlage hiermit eine Sprache vor, die ekelhaft, unnütz und hirnlos ist, sich aber an der Java-Syntax orientiert.
Ich schlage vor, den folgenden Code:
public static void Main(String[] args)
{
}
...durch den folgenden, zwecks eines neuen Sprachdesigns von Java zu ersetzen:
pubic shitty futile Minor(Rogue[] wuargs)
{
}
Rette Deine Freiheit
Ich möchte darauf hinweisen, dass hier KEIN Artikel über "Rette deine Freiheit" steht. Das kennt ihr sowieso alle schon :D