WCF Web API UriTemplate Elements Found in Multiple Methods
Asked Answered
G

1

6

Let's say I am using the new WCF Web API to build a RESTful service and, in my service, I have a section of the URI that will describe the target resource, but is used on (nearly) all methods of the contract. For example, if I have a User service that deals with eCommerce and may look like:

[ServiceContract]
public class MyUserService
{
    private MyUserRepository _UserRepo;
    private MyOrganizationRepository _OrgRepo;

    [WebGet (UriTemplate = "{OrganizationName}/Users")]
    public IEnumerable<User> GetUsers (string OrganizationName)
    {
        IEnumerable<User> Users = null;
        var Organization = _OrgRepo.GetOrgByName (OrganizationName);

        if (Organization != null)
        {
            Users = Organization.GetUsers ();
        }
        else
        {
            throw new WebFaultException<string> ("Organization not found.", HttpStatusCode.NotFound);
        }

        return Users;
    }

    [WebInvoke (UriTemplate = "{OrganizationName}/Users", /*yada...yada...yada*/)]
    public User AddNewUser (string OrganizationName, User User)
    {
        // Find the organization, like above, and throw if null.
    }
}

If I have to continually load the organization and test for null, this will bog down my code and is not very DRY. (So tempted to spell out DRY...) What I would like to do is load up a property in the MyUserService class that is populated when {OrganizationName} is included in the URI and throw a WebFaultException otherwise. Because this is apart of the URI, what would be the best way to accomplish this?

EDIT:

For those that may be interested, here is an example of the HttpOperationHandler I came up with. There doesn't seem to be a whole lot of information out there covering this. I also found more information about Processors that will be coming with the WCF Web Api suite and it looks like they will handle this sort of thing better replace HttpOperationHandlers and it seems they may be easier to use. (This is just a for-instance to cover some things I found hard to find. I wrote it up a bit differently in my application.)

using Microsoft.ApplicationServer.Http.Dispatcher;   // For HttpOperationHandler
using Microsoft.ApplicationServer.Http.Description;  // For HttpOperationHandlerFactory

public class OrganizationHandler : HttpOperationHandler<string, Organization>
{
    private Repository<Organization> _OrganizationRepository;

    public OrganizationHandler (UnitOfWork Work)
        : base ("OrganizationName")
    {
        _OrganizationRepository = Work.Organizations;
    }

    public override Organization OnHandle (string OrganizationName)
    {
        var Result = _OrganizationRepository
                        .Get (O => O.UrlSafeName.Equals (OrganizationName,
                                                StringComparison.InvariantCultureIgnoreCase));

        if (Result == null)
        {
            throw new WebFaultException<string> ("Organization not found.");
        }

        return Result;
    }
}

public class OrganizationHandlerFactory : HttpOperationHandlerFactory
{
    private UnitOfWork _Work;

    public OrganizationHandlerFactory (UnitOfWork Work)
    {
        _Work = Work;
    }

    protected override Collection<HttpOperationHandler> OnCreateRequestHandlers
        (ServiceEndpoint endpoint, HttpOperationDescription operation)
    {
        var Collection = base.OnCreateRequestHandlers (endpoint, operation);

        if (operation.InputParameters.Any (IP => IP.Type.Equals (typeof (Organization))))
        {
            var Binding = endpoint.Binding as HttpBinding;

            if (Binding != null)
            {
                Collection.Add (new OrganizationHandler (_Work));
            }
        }

        return Collection;
    }
}

And then to wire it up in Global.asax (I am using Ninject for IoC):

// Add this reference to get the MapServiceRoute<T> extension
using Microsoft.ApplicationServer.Http.Activation;

public class Global : HttpApplication
{
    protected void Application_Start (object sender, EventArgs e)
    {
        var Kernel = BuildKernel ();

        var Config = HttpHostConfiguration.Create ()
            .SetOperationHandlerFactory
                (Kernel.Get (typeof (OrganizationHandlerFactory)) as OrganizationHandlerFactory)
            .SetResourceFactory (new NinjectResourceFactory (Kernel));


        RouteTable.Routes.MapServiceRoute<OrganizationService> ("Organizations", Config);
    }

    protected IKernel BuildKernel ()
    {
        IKernel Kernel = new Ninject.StandardKernel ();

        // Load up the Kernel

        return Kernel;
    }
}

public class NinjectResourceFactory : IResourceFactory
{
    private readonly IKernel _Kernel;

    public NinjectResourceFactory (IKernel Kernel)
    {
        _Kernel = Kernel;
    }

    public object GetInstance (Type serviceType, InstanceContext instanceContext, HttpRequestMessage request)
    {
        return Resolve (serviceType);
    }

    public void ReleaseInstance (InstanceContext instanceContext, object service)
    {
        throw new NotImplementedException ();
    }

    private object Resolve (Type type)
    {
        return _Kernel.Get (type);
    }
}

And here it is in my Service:

[ServiceContract]
[ServiceBehavior (InstanceContextMode = InstanceContextMode.PerCall)]
public class OrganizationService
{
    [WebGet (UriTemplate = "{OrganizationName}/Products")]
    public IEnumerable<Product> GetProducts (Organization Organization)
    {
        return Organization.Products;
    }
}
Goahead answered 20/5, 2011 at 0:9 Comment(5)
Processor is just the old name for HttpOperationHandlerRhinarium
@Darrel Miller - thanks! I am trying to keep up with all the changes and the lack of information (understandable) that also mixes in with other "WCF REST" links makes it pretty tough. Thanks for catching this!Relinquish
Nice job on the OperationHandler!Rhinarium
The project has been out in the open since very early days, so there is lots of out of date documentation and blog posts. Now that the library is starting to settle down things should get better.Rhinarium
Thanks! I agree it should get better with time. That's why I wanted to post the solution I came up with just to add to the confusion! =D (Thanks, too, for fixing the tags.)Relinquish
R
2

This is exactly what OperationHandlers are for. You create a single OperationHandler that converts the URI parameter into a strongly typed object that you can just accept as a parameter on the operation.

Rhinarium answered 20/5, 2011 at 1:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.