ThatRendle/Simple.Web

Generic arguments in UriTemplates

ThatRendle opened this issue · 10 comments

Allow generic type arguments to be included as part of the UriTemplate, so you can do things like this:

[UriTemplate("/{TModel}/{Id}")]
[ValidTypes(typeof(Album), typeof(Artist), typeof(Label))]
public class GetModel : IGet, IOutput<TModel>
{
    public Status Get()
    {
        var db = Database.Open();
        Output = db[typeof(TModel).Name].Get(Id);
        return Output != null ? 200 : 404;
    }

    public int Id { get; set; }
    public TModel Output { get; private set; }
}

It's perhaps a bit cleaner instead of restricting the model types through the ValidTypesAttribute to enforce it using an interface? E.g.:

[UriTemplate("/{TModel}/{Id}")]
public class GetModel : IGet, IOutput<TModel> where TModel: IMyDomainGettable

+1 for using generic constraints over ValidTypesAttribute.

I'll have to spike that to check it's going to work properly, I think. The main issue is that you end up modifying the model type with an interface that exists purely for Simple.Web, which is not in keeping with the "keeping out of the way" motif.

That's true. My primary concern is that if you don't have these generic constraints in place you may end up with code that compiles fine but blows up at runtime. Perhaps it would make sense to have checks at startup that "somehow" do sanity checks. No idea how you'd tackle that, though.

The generic constraint still wouldn't blow up at compile time, because the argument is coming from the URI at runtime.

One of the ideas I've got in mind soon is a "startup" page that you browse to which (a) pre-compiles and warms-up, and (b) gives you a full report on the system status, including showing any exceptions that happened during the initialisation.

Well, if you'd call a method that doesn't exist on the IMyDomainGettable it would fail to compile, though? But having a startup page would certainly be very useful, regardless.

I too would expect the compile to fail?!

Perhaps I'm getting the wrong end of the originally intentioned stick but I would have thought IMyDomainGettable is your domain object (and generic constraint) opposed to Simple.Web's - thus not getting in your way? Wouldn't Simple.Web just take the generic constraint into account when building it's object/type graph?

It would be nice if Simple.Web could continue to have implied conventions opposed to be explicitly declared (aka Fubu); it is already tied to the method interfaces for 'glue' so that aspect is already "in your way" so to speak. I would have thought to just build on that accepted trade-off for the time-being; especially as the frameworks that claim opposite have a massive learning-curve due to that bespokeable convention-based compositional glue.

Status page would be very cool, given it's simple :)

@ianbattersby : Yes, that was what I meant. The IMyDomainGettable would just be an interface declared on your domain that exposes, say, some kind of Get method that you can then use in your handler. Perhaps instead of IMyDomainGettable it would've been clearer had I used something like IRepository or something.

Having started work on this, I think having an abstract TypeResolverAttributeBase that returns a Type from a string, with a couple of stock implementations (like a NamespaceTypeResolverAttribute) is going to be the only way forward on this. Reason being, your average RESTful API uses plural lowercase names, e.g. /issues/18, which would need to be resolved to a singular Pascal class name, e.g. Issue.

Right, I've done this, and gone both ways: there is a base attribute, GenericResolverAttribute, which can be used for fine-grained control over types, plus a Regex (matching against the type name) and an Explicit (specify a list of types) implementation of that base. But it also uses constraint matching, either against the types returned by the attribute, or against all loaded types. Hurrah for that.