wcf - customizing service document - wcf

I am new to WCF programming and its syndication classes etc. I am trying to produce a AtomPub service document programmatically when someone tries to access root address of my service. I am able to do that only to some extent using ServiceDocument, ResourceCollectionInfo, Workspace classes -
<service>
<app:workspace>
<a10:title type="text">Sample Repository</a10:title>
<app:collection href="http://some_url">
<a10:title type="text">Root Collection</a10:title>
</app:collection>
</app:workspace>
</service>
However I would like to add some custom attributes and elements as can be seen below. Please notice the xmlns attributes, cmisra elements etc.
<service
xmlns:cmis="http://docs.oasis-open.org/ns/cmis/core/200908/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:app="http://www.w3.org/2007/app"
xmlns:cmisra="http://docs.oasis-open.org/ns/cmis/restatom/200908/">
<workspace>
<atom:title>Sample Repository</atom:title>
<collection href="http://some_url">
<atom:title>Root Collection</atom:title>
<cmisra:collectionType>root</cmisra:collectionType>
</collection>
</workspace>
</service>
There is nothing much available on msdn website or elsewhere. Any inputs on how to achieve this would be appreciated.
Thanks!
Thanks.

The ResourceCollectionInfo class has two properties ElementExtensions and AttributeExtensions which can be used to customize the serialization of a ResourceCollectionInfo. Please note that most of the syndication elements have those extensions, which work with both the RSS and Atom formatters.

Related

Multiple services on the same URL

Currently I'm building a central place to do some common tasks, which a bunch of projects should use and it would be extremely nice to have this (mostly for convenience, I'll admit):
I just can't seem to do that. I've tried implementing multiple interfaces, but that doesn't work the way I want. It exposes too much in the same place.
I have a pseudo thing going on right now where I have three services, but on three different addresses (/ReportingService.svc, /QueueService.scv, and /TicketService.svc) and thus the above doesn't happen (ideally just one /Services.svc).
The way the dialog is constructed seems to support this way of exposing services (namely it says "1 service(s) found at[...]"), but I just can't find a way to do it.
I suppose this could be helpful, but I haven't been able to make it work either. I just get a 404 when trying to open the listenUri.
Does anyone have any good ideas?
You need to create your methods in the interface (and obviously implement those methods in your service class).
[ServiceContract]
public interface IService
{
[OperationContract]
string method1(int code);
[OperationContract]
int method2(String id);
}
So, now you have just one service and multiple methods inside of it.
Or if you want to keep your methods organized, you could include services in your Web.config file.
<services>
<service name="ServicioWeb.IService1">
<endpoint binding="webHttpBinding"
contract="ServicioWeb.IService1"
behaviorConfiguration="WebBehavior"
/>
</service>
<service name="ServicioWeb.IService2">
<endpoint binding="webHttpBinding"
contract="ServicioWeb.IService2"
behaviorConfiguration="WebBehavior"
/>
</service>
</services>
And your URL would change a bit. Something like this:
http://localhost:8080/Service1.svc/...
http://localhost:8080/Service2.svc/...

Sending licensekey from client to server via WCF in header

I want to authenticate app server against my central with license key and I do not want to include it as a parameter in my calls.
I have implemented custom ServiceAuthorizationManager but I am not sure what is the best way and place to set this licenseKey on client side.
I tried below (I might be completely wrong with approach)
<endpoint address="http://127.0.0.1:8000/ApiDataService" binding="wsHttpBinding"
contract="MyInterfaces.IApiDataService">
<headers>
<LicenseKey>MyLicenseKey</LicenseKey>
</headers>
</endpoint>
then in ServiceAuthorizationManager.CheckAccessCore(OperationContext operationContext)
I have managed to find a header's index via:
operationContext.RequestContext.RequestMessage.Headers.FindHeader("LicenseKey", "")
but retrieved HeaderInfo does not have my content any more.
I am sure it is in operationContext.RequestContext so I should be able to capture it somehow.
Any idead how?
Is there any nicer/standard way of doing this?
Should I use <identity\> element?
I am more than happy to use operationContext.ServiceSecurityContext but I do not know how to set it up on client side and where to place my custom license key.
I have multiple endpoints so I would prefer to have a single place for my license key. Setting it from code would be even better.
Thanks
This article here Globalization Patterns in WCF (WS-I18N implementation) may be of interest to you as it demonstrate how to use WCF out-of-band mechanisms to send and receive custom information. It's about globalization, not licensing of course, but the problem seems somewhat similar.

Applying [WebInvoke(ResponseFormat = WebMessageFormat.Json)] in config file

I'm writing what I'm referring to as a POJ (Plain Old JSON) WCF web service - one that takes and emits standard JSON with none of the crap that ASP.NET Ajax likes to add to it.
It seems that there are three steps to accomplish this:
Change "enableWebScript" to "webHttp" in the endpoint's tag
Decorate the method with [WebInvoke(ResponseFormat = WebMessageFormat.Json)]
Add an incantation of [AspNetCompatibilityRequirements(RequirementsMode
= AspNetCompatibilityRequirementsMode.Allowed)] to the service contract
This is all working OK for me - I can pass in and am being returned nice plain JSON.
If I remove the WebInvoke attribute, then I get XML returned instead, so it is certainly doing what it is supposed to do. But it strikes me as odd that the option to specify JSON output appears here and not in the configuration file. Say I wanted to expose my method as an XML endpoint too - how would I do this? Currently the only way I can see would be to have a second method that does exactly the same thing but does not have WebMethodFormat.Json specified. Then rinse and repeat for every method in my service? Yuck.
Specifying that the output should be serialized to JSON in the attribute seems to be completely contrary to the philosophy of WCF, where the service is implemented is a transport and encoding agnostic manner, leaving the nasty details of how the data will be moved around to the configuration file.
Is there a better way of doing what I want to do? Or are we stuck with this awkward attribute? Or do I not understand WCF deeply enough?
I haven't fully tested this out yet, BUT, I took a look at WebMessageFormat in reflector and where it was used in code.
There is a attribute of the webHttp element called defaultOutgoingResponseFormat that can be set to "Json" or "Xml".
<behaviors>
<endpointBehaviors>
<behavior name="ServicesJSONEndpointBehavior">
<webHttp defaultOutgoingResponseFormat="Json"/>
</behavior>
</behaviors>
I've run into the same issue and typically crufted work-arounds after searching online without much info.
I'll give it a shot with multiple configured endpointBehaviors and report back.
UPDATE 6/5/2011
FYI -- I've ditched vanilla WCF with all its hair-pulling scenarios like this that should be simple, in favor of ServiceStack ( http://servicestack.net/ ). If you're looking to put together a standard REST style / document-oriented service over HTTP that out-of-the-box supports JSON / XML / CSV (and in the future protocol buffers) and that allows you to put together clean MVC-style routes with ease, give ServiceStack a hard look. There are a number of things that ServiceStack will handle rather easily and cleanly that always turn out to be a major PITA due to some wonky configuration issue or similar in standard WCF. ServiceStack uses it's own JSON serializer, which as an added bonus, outperforms DataContractJsonSerializer and JSON.NET as Demis mentions below.

Multi Tenant Service API for Business entities and Business logic

We have an application where we wish to expose an large number of database entities and some business logic. Each entity will require the ability to Read , Add, and Update. at this point we do not expect to allow deletion.
the software we build is used in a wide range of business, so of which are multi tenanted operations Bureau services, also some of our clients use this approach to have separate databases for financial reasons.
We wish to be able to minimize the number of endpoints that need to be maintained. At the moment there are only 3 tables be exposed as WCF interfaces each with 6 attached methods. this is manageable but if operation has 50 databases that suddenly becomes 150 endpoints. worse if we have 50 tables exposed that becomes 2500 endpoints.
Does anyone have a suggestion on how we could design out system that we still have a simple entity model of Job.add (var1) or iList jobs = Job.GetSelected("sql type read").
without all these endpoints
WCF Data Services allows you to expose your data in a RESTful manner using the Open Data protocal (OData). This was formally called ADO.Net data services and before that Astoria. Any IQueryable collection can be exposed. The way shown in most of the examples is to use the Entity Framework, however there are examples showing usage with NHibernate and other Data Access technologies. OData is a self describing API based on Atom-Pub with some custom extensions. With a minimal amount of code you can expose you're entire database in a well defined format. That's the easy part.
In order to implement multi-tenency, you can create query interceptors in the WCF Data Services application to implement that logic. The number of interceptors and the complexity of the code you write will depend upon your security model and requirements. Looking at something like T4 templates or CodeSmith to generate the interceptor methods based on your database schema may be a way to prevent lots of repetitive manual coding.
The link I provided has a lot of information and tutorials on WCF Data Services and would provide a good place to start to see if it would meet your needs. I have been looking at WCF Data Services for a similar problem (Multi-tenancy), and would love to hear how you evently implement your solution.
It seems like you could pass the "identity" to every query and take that into account. This would mean that every record on your "Job" table would need to have a reference to the owner "identity" but that should not be much of a problem.
Just make sure that every query validates the "identity", and you should be OK.
If I understand your question correctly, I think you need unique endpoints but you can have a single service behavior that your end points reference.
Create a default endpoint:
<behaviors>
<serviceBehaviors>
<behavior name="MyService.DefaultBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
Set your default binding:
<bindings>
<wsHttpBinding>
<binding name="DefaultBinding">
<security mode="None">
<transport clientCredentialType="None"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
Have all service reference point to the default behavior and binding:
<service behaviorConfiguration="MyService.DefaultBehavior"
name="MyService.Customer">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="DefaultBinding"
contract="MyService.ICustomer">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
Each time you add a service, its a simple config entry.
With Apache you can use a fairly simple set of URL rewriting rules to map an arbitrary set of DB table tables and their corresponding endpoints to a single endpoint with a parameter.
For example, to map $ROOT/table_name/column_name to $ROOT/index.php?tn=table_name&cn=column_name, you could add a rule like this to $ROOT/.htaccess:
RewriteRule ^([a-zA-Z0-9_]+)/([a-zA-Z0-9_]+)/?$ index.php?tn=$1&cn=$2 [QSA,L]
Then you only need to maintain $ROOT/index.php (which of course can generate the appropriate HTTP status codes for nonexistent tables and/or columns).
Providing Multi-Tenancy, Without A Bazillion End Points
One way is to go with a REST-style WCF service that can use username/passwords to distinguish which client you are working with, and thus be able to select internally which DB to connect to. WCF gives you the the UriTemplate which allows you to map part's of the URL to the param's in your web methods:
HTTP GET Request: http://www.mysite.com/table1/(row Id)
HTTP PUT Request: http://www.mysite.com/table1/(row Id)/(field1)/(field2)
HTTP POST Request: http://www.mysite.com/table1/(row Id)/(field1)/(field2)
HTTP DELETE Request: http://www.mysite.com/table1/(row Id)
You can add other Uri Templates for more tasks as well, such as the following:
HTTP GET Request: http://www.mysite.com/table1/recentitems/(number of most recent items)
HTTP GET Request: http://www.mysite.com/table1/cancelPendingOrders/(user Id)
Who's Using My Service?
By requiring clients to supply a username and password, you can map that to specific DB. And by using the UriTemplate of /{tableName}/{operation}/{params...} you could then use code in your web service to execute the DB procedures given the table, operation, and params.
Wrapping It Up
Your web configuration wouldn't need to be altered much at all even. The following web article series is a great place to learn about REST-style web services, which I believe fits what you need: http://www.robbagby.com/rest/rest-in-wcf-blog-series-index/

WCF CustomBinding Configuration

I've written a custom binding class that inherits from CustomBinding.
My custom class overrides the BuildChannelFactory method and uses a custom ChannelFactory to create a custom channel.
I'm having difficulties using the custom binding class in the WCF client.
I am able to use my custom binding class if I configure it in code:
Binding myCustomBinding = new MyCustomBinding();
ChannelFactory<ICustomerService> factory =
new ChannelFactory<ICustomerService>(myCustomBinding,
new EndpointAddress("http://localhost:8001/MyWcfServices/CustomerService/"));
ICustomerService serviceProxy = factory.CreateChannel();
serviceProxy.GetData(5);
My problem is I don't know how to configure it in the App.config file.
Is it a customBinding element, or a bindingExtension element? Is it something else?
When you created your custom binding in code, did you also implement a "YourBindingElement" (deriving from StandardBindingElement) and a "YourBindingCollectionElement" (deriving from StandardBindingCollectionElement) along with it?
If so - use that to configure your custom binding, as if it were any other binding.
The first step is to register your binding in the app.config or web.config file in the extensions section of <system.serviceModel>
<extensions>
<bindingExtensions>
<add name="yourBindingName"
type="YourBinding.YourBindingCollectionElement, YourBindingAssembly" />
</bindingExtensions>
</extensions>
Now, your new binding is registered as a "normal" available binding in WCF. Specify your specifics in the bindings section as for other bindings, too:
<bindings>
<yourBinding>
<binding name="yourBindingConfig"
proxyAddress="...." useDefaultWebProxy="false" />
</yourBinding>
</bindings>
Specify other parameters here, as defined in your "...BindingElement" class.
Lastly, use your binding like a normal binding in your services and/or client sections in system.serviceModel:
<client>
<endpoint
address="....your url here......"
binding="yourBinding"
bindingConfiguration="yourBindingConfig"
contract="IYourContract"
name="YourClientEndpointName" />
</client>
I couldn't really find a lot of resources on how to write your own binding in code on the web - Microsoft has a WCF/WPF/WF sample kit which includes a few samples from which I basically learned enough to figure it out :-)
There's one really good article by Michele Leroux Bustamante on creating your custom bindings - part 2 of a series, but part 1 is not available publicly :-(
Here's a sample custom binding in code for which you can download the complete source code: ClearUserNameBinding.
Marc
If you want to use this custom binding via configuration, you must extend the BindingCollectionElement abstract base and define bindingExtensions element in web.config.