-
Notifications
You must be signed in to change notification settings - Fork 0
Configuration How Tos
On this page you’ll find a number of examples of registering resources using the fluent root ResourceSpace.Has
, specifying some basic representations and with corresponding handler examples. We’ll be concentrating on using codecs other than the WebForms Codec, as this means we only have a resource registration and a handler (no view!) to write for each example.
ResourceSpace.Has.ResourcesOfType<Customers>()
.AtUri("/customers")
.HandledBy<CustomersHandler>()
.AsXmlDataContract();
This registers a Customers
collection type at the URI /customers
and specifies that it’ll be handled by a CustomerHandler
. Here’s a (partial) example handler which only handles the GETting of customers (and assumes some kind of available repository implementation):
public class CustomersHandler
{
public Customers Get()
{
return CustomerRepository.GetAll();
}
}
Registering a Customer
resource to a URI template with integer ID and an XmlDataContract
representation
This example allows us to retrieve a representation for a specific customer using their ID. This example introduces the concept of URI Templates – anything you see in curly braces can be thought of as a parameter – which by convention will usually have the same name as a parameter in a handler method.
ResourceSpace.Has.ResourcesOfType<Customer>()
.AtUri("/customer/{id}")
.HandledBy<CustomerHandler>()
.AsXmlDataContract();
Example handler:
public class CustomerHandler
{
public Customer GetById(int id)
{
return CustomerRepository.GetById(id);
}
}
Note that OpenRasta will select the
GetById
method automatically when it receives an HTTP Get
request on the /customers/{id}
URI. Any method starting with Get
will be matched. For a fuller discussion of how methods are selected by convention, see Handler Method Selection.
Here we’ll extend our previous Customers
example and add another means of accessing lists of customers by region. Our resource registration now looks like this:
ResourceSpace.Has.ResourcesOfType<Customers>()
.AtUri("/customers")
.And.AtUri("/customers/region/{region}")
.HandledBy<CustomersHandler>()
.AsXmlDataContract();
Our handler will now be extended by one method and will look like this:
public class CustomersHandler
{
public Customers Get()
{
return CustomerRepository.GetAll();
}
public Customers GetByRegion(string region)
{
return CustomerRepository.GetByRegionName(region);
}
}
Note that when we add more URIs that return resources of type
Customers
, we normally do so on the same resource registration we used previously. Occasionally we might want to serve Customers
from a different handler. This is possible, but is not “usual” behaviour. ’’’Care must be taken not to register the same resource type with the same handler twice! ‘’’ This will cause an exception when the application starts and the fluent configuration is executed.
Serving a JSON representation in addition to an XML representation for all resources of type Customer
We simply add another codec registration to our existing Customers
resource registration. Joy of joys – ‘’our handler does not need to change’’. We just got JSON support for free!
ResourceSpace.Has.ResourcesOfType<Customers>()
.AtUri("/customers")
.And.AtUri("/customers/byRegion/{region}")
.HandledBy<CustomersHandler>()
.AsXmlDataContract()
.And.AsJsonDataContract();
WithoutUri
is a special type of modifier for a resource declaration. It states that we sometimes serve resources which are not directly accessible from a URI. Why would we want to do this? In the case of errors, for example, you might want to define your own Error
class – much like you’d define your own Exception
classes – and serve the error as a resource when something went wrong with retrieving or updating a Customer
. Let’s say, for example, we’d like to have a TicketedError
class so that if a user gets an error he gets served a ticket number he can use when calling support:
ResourceSpace.Has.ResourcesOfType<TicketedError>()
.WithoutUri
.AsXmlDataContract()
.And.AsJsonDataContract();
Note that we haven’t declared a handler here either. Well, why declare one for something we’re not going to serve directly? We do, however, have multiple representations for it. Because it’s ’’’cool’’’. Let’s define our
TicketedError
class now:
public class TicketedError
{
public int TicketNumber { get; set; }
public string Message { get; set; }
}
Now let’s modify the
CustomerHandler
to give us the error in some circumstance – let’s say when the customer can’t be found. This will depend on some implementation of TicketRepository.CreateTicketedError
which, when given an exception, returns a new TicketedError
instance with its TicketNumber
filled in. Note that we have to alter our Customers
-returning method to return an OperationResult
:
public class CustomerHandler
{
public OperationResult GetById(int id)
{
try
{
return new OperationResult.OK { ResponseResource = CustomerRepository.GetById(id) };
}
catch (NotFoundException ex)
{
return new OperationResult.NotFound
{
ResponseResource = TicketRepository.CreateTicketedError(ex);
}
}
}
}
From an exception handling perspective, this is definitely ’’’not’’’ the best way to deal with errors! However, it does show – without too much darting around different topics – why resources without URIs could be served, why we’re not bound to return what the handler normally returns, and how we do it.
Assuming you’ve written your own codec for a format OpenRasta doesn’t support, you’ll need to tell OpenRasta via configuration that you’ll be using this codec to assist with dealing with a particular format. Let’s say you’ve written an RdfCodec
to supply representations of resources in RDF format. We’ll add this representation to the two we’re already using from our previous examples:
ResourceSpace.Has.ResourcesOfType<Customers>()
.AtUri("/customers")
.And.AtUri("/customers/byRegion/{region}")
.HandledBy<CustomersHandler>()
.AsXmlDataContract()
.And.AsJsonDataContract()
.And.TranscodedBy<RdfCodec>(null);
Note that the
null
part is configuration that the codec can choose not to use. Our imaginary RdfCodec
doesn’t have any configuration, so we leave it null
. We could pass in any object we like, though – the codec will get to use whatever configuration object
we pass in.