1

When registering two handlers for the same type, but with different URIs, the handler selection algorithm doesn't seem to check the uri when it determines which handler to use.

If you run the program below, you'll notice that only HandlerOne will be invoked (twice). It does not matter if I call for "/one" or "/two", the latter supposed to be handled by HandlerTwo.

Am I doing something wrong or is this something to be fixed in OpenRasta? (I'm using 2.0.3.0 btw)

class Program
{
    static void Main(string[] args)
    {
        using (InMemoryHost host = new InMemoryHost(new Configuration()))
        {
            host.ProcessRequest(new InMemoryRequest
            {
                HttpMethod = "GET",
                Uri = new Uri("http://x/one")
            });
            host.ProcessRequest(new InMemoryRequest
            {
                HttpMethod = "GET",
                Uri = new Uri("http://x/two")
            });
        }
    }
}
class Configuration : IConfigurationSource
{
    public void Configure()
    {
        using (OpenRastaConfiguration.Manual)
        {
            ResourceSpace.Has.ResourcesOfType(typeof(object))
                .AtUri("/one").HandledBy(typeof(HandlerOne));
            ResourceSpace.Has.ResourcesOfType(typeof(object))
                .AtUri("/two").HandledBy(typeof(HandlerTwo));
        }
    }
}
class HandlerOne
{
    public object Get() { return "returned from HandlerOne.Get"; }
}
class HandlerTwo
{
    public object Get() { return "returned from HandlerTwo.Get"; }
}

Update I have a feeling that I could accomplish what I want similar using UriNameHandlerMethodSelector as described on http://trac.caffeine-it.com/openrasta/wiki/Doc/Handlers/MethodSelection, but then I'd have to annotate each handler methods and also do AtUri().Named(), which looks like boilerplate to me and I'd like to avoid that. Isn't AtUri(X).HandledBy(Y) making the connection between X and Y clear?

Evgeniy Berezovsky
  • 18,571
  • 13
  • 82
  • 156

3 Answers3

5

Eugene,

You should never have multiple registrations like that on the same resource type, and you probably never need to have ResourcesOfType<object> ever associated with URIs, that'll completely screw with the resolution algorithms used in OpenRasta.

If you're mapping two different things, create two resource classes. Handlers and URIs are only associate by resource class, and if you fail at designing your resources OpenRasta will not be able to match the two, and this is by design.

If you want to persist down that route, and I really don't think you should, then you can register various URIs to have a name, and hint on each of your methods that the name ought to be handled using HttpOperation(ForUriName=blah). That piece of functionality is only there for those very, very rare scenarios where you do need to opt-out of the automatic method resolution.

Finally, as OpenRasta is a compsable framework, you shouldnt have to go and hack around existing classes, you ought to plug yourself into the framework to ensure you override the components you don't want and replace them by things you code yourself. In this case, you could simply write a contributor that replaces the handler selection with your own moel if you don't like the defaults and want an MVC-style selection system. Alternatively, if you want certain methods to be selected rather than others, you can remove the existing operation selectors and replace them (or complement them with) your own. That way you will rely on published APIs to extend OpenRasta and your code won't be broken in the future. I can't give that guarantee if you forked and hacked existing code.

SerialSeb
  • 6,701
  • 24
  • 28
  • From a conceptual standpoint, I don't see why different REST resources must have different types. I wouldn't see a problem having 2 different resources, representing 2 different things, of the same type. But in my specific case, I'm not using static classes for my resources, but dynamic objects created from/to JSON, so there's gonna be only one type for all of my resources ever. So I'm not doing - what you said you want to avoid in your comment to my answer - RPC style. I don't want to hack OpenRasta, especially not "against" its spirit, so on occasion I'll try to do a custom HandlerSelection. – Evgeniy Berezovsky Oct 25 '11 at 08:50
0

As Seb explained, when you register multiple handlers with the same resource type OpenRasta treats the handlers as one large concatenated class. It therefore guesses (best way to describe it) which potential GET (or other HTTP verb) method to execute, which ever it thinks is most appropriate. This isn't going to be acceptable from the developers prospective and must be resolved.

I have in my use of OpenRasta needed to be able to register the same resource type with multiple handlers. When retrieving data from a well normalised relational database you are bound to get the same type response from multiple requests. This happens when creating multiple queries (in Linq) to retrieve data from either side of the one-to-many relation, which of course is important to the whole structure of the database.

Taking advice from Seb, and hoping I've implemented his suggestion correctly, I have taken the database model class, and built a derived class from it in a resources namespace for each instance of when a duplicating resource type might have been introduced.

ResourceSpace.Has.ResourcesOfType<IList<Client>>()
                .AtUri("/clients").And
                .AtUri("/client/{clientid}").HandledBy<ClientsHandler>().AsJsonDataContract();

ResourceSpace.Has.ResourcesOfType<IList<AgencyClient>>()
                .AtUri("/agencyclients").And
                .AtUri("/agencyclients/{agencyid}").HandledBy<AgencyClientsHandler>().AsJsonDataContract();

Client is my Model class which I have then derived AgencyClient from.

namespace ProductName.Resources
{
    public class AgencyClient: Client { }
}

You don't even need to cast the base class received from your Linq-SQL data access layer into your derived class. The Linq cast method isn't intended for that kind of thing anyway, and although this code will compile it is WRONG and you will receive a runtime exception 'LINQ to Entities only supports casting Entity Data Model primitive types.'

Context.Set<Client>().Cast<AgencyClient>().ToList();  //will receive a runtime error

More conventional casts like (AgencyClient) won't work as conversion to a SubClass isn't easily possible in C#. Convert base class to derived class

Using the AS operator will again compile and will even run, but will give a null value in the returned lists and therefore won't retrieve the data intended.

Context.Set<Client>().ToList() as IEnumerable<AgencyClient>; //will compile and run but will return null

I still don't understand how OpenRasta handles the differing return class from the handler to the ResourceType but it does, so let's take advantage of it. Perhaps Seb might be able to elaborate?

OpenRasta therefore treats these classes separately and the right handler methods are executed for the URIs.

Community
  • 1
  • 1
Matthew R
  • 1,038
  • 11
  • 15
-2

I patched OpenRasta to make it work. These are the files I touched:

OpenRasta/Configuration/MetaModel/Handlers/HandlerMetaModelHandler.cs
OpenRasta/Handlers/HandlerRepository.cs
OpenRasta/Handlers/IHandlerRepository.cs
OpenRasta/Pipeline/Contributors/HandlerResolverContributor.cs

The main change is that now the handler repository gets the registered URIs in the initializing call to AddResourceHandler, so when GetHandlerTypesFor is called later on during handler selection, it can also check the URI. Interface-wise, I changed this:

public interface IHandlerRepository
{
    void AddResourceHandler(object resourceKey, IType handlerType);
    IEnumerable<IType> GetHandlerTypesFor(object resourceKey);

to that:

public interface IHandlerRepository
{
    void AddResourceHandler(object resourceKey, IList<UriModel> resourceUris, IType handlerType);
    IEnumerable<IType> GetHandlerTypesFor(object resourceKey, UriRegistration selectedResource);

I'll omit the implementation for brevity.

This change also means that OpenRasta won't waste time on further checking of handlers (their method signatures etc.) that are not relevant to the request at hand.

I'd still like to get other opinions on this issue, if possible. Maybe I just missed something.

Evgeniy Berezovsky
  • 18,571
  • 13
  • 82
  • 156
  • That's not a patch I will be taking. There is a strict principle in OpenRasta, you associate one or more URIs and one or more handlers to *one* resource type. Thef act that the URI is not used for handler selection is done on purpose, to prevent you from trying to shove multiple resources into one and focusing back on your uri - handler matching, which invariably leads to MVC-style action-based RPC code. – SerialSeb Oct 25 '11 at 08:07
  • @serialseb - Why not design the configuration APIs so that they accept only information they are going to pay attention to? Why are URIs specified in the configuration for a resource? This misleads people into assuming that all the information it contains has a purpose. As you point out, a lot of it is irrelevant. – Daniel Earwicker Jan 25 '12 at 11:27
  • Daniel, the configuration is designed to associate a resource to a uri, a handler and various codecs, which is exactly what people should find relevant when designing restful systems. If you try and shoehorn RPC or MVC semantics on top of OpenRasta you're on your own, as the framework is not designed for those scenarios. – SerialSeb Jan 25 '12 at 16:09
  • 1
    @serialseb I do agree with your point, but I see a problem in the syntax. To me, it is suggestive of something that it does not do. I did not invest any time into thinking about this, but I have a feeling the syntax could be changed to remove this misleading suggestion. – Evgeniy Berezovsky Jan 26 '12 at 01:14
  • I'm not sure what is miselading. You map a resource type to things, not a uri to a handler, not a handler to a codec. The code you propose maps a URI to a handler, which is specifically what I refuse to add to the framework, as traditional MVC frameworks do that at their own RPC perils. – SerialSeb Jan 30 '12 at 12:03