Does Luracast Restler support multi-part URIs? - rest

I've recently started a project using Luracast Restler. It seems a very simple and effective way to set up a REST API. With very little code, I was able to provide CRUD services for my Category and Product resources.
My GET methods look like this:
class Categories
{
function get($id=NULL) {
if (isset($id))
{
// return category details for $id.
}
else
{
// return all categories.
}
}
}
class Products
{
function get($id=NULL) {
if (isset($id))
{
// return product details for $id.
}
else
{
// return all products.
}
}
}
Clients can get the details of the "books" category using:
http:api/categories/books
or all categories using:
http:api/categories
Same for products. One product:
http:api/products/123
All products:
http:api/products
So far so good.
Now I want to progress to something slightly more involved. I want to give my clients access to the products in a category.
I want my URI to be:
http:api/categories//products
E.g.
http:api/categories/books/products
and from there, I want to offer:
http:api/categories//products/
E.g.
http:api/categories/books/products/123
This gives my client the ability to transfer from one resource to another using a progressive series of links, which I see as a core principle of REST.
But I can't see a way of achieving this with Restler. I've seen some mention of JavaDoc comments being used to specify URI mapping, so I tried this:
class Products
{
/**
* url GET /categories/:catId/products/:prodId
*/
function get($catId=NULL, $prodId=NULL) {
// Get product($prodId) of category($catId)
}
}
But this doesn’t work. Restler doesn’t seem to take any information from the comment; it implicitly creates the URI route based on class name and function name.
Can anyone help? Am I missing something? Any advice would be much appreciated.

Everything is fine in the example above and what you are trying to achieve except one simple mistake that stopped it from working!
Your PHPDoc comment is missing #
Change your code as follows
<?php
class Products
{
/**
* #url GET /categories/:catId/products/:prodId
*/
function get($catId=NULL, $prodId=NULL) {
// Get product($prodId) of category($catId)
}
}
Also take a look at the related question
How do you organize Luracast Restler classes to create related route endpoints?

Related

Is splitting an index action into multiple ones a restful-friendly approach?

I need to display two different index pages to two different user groups. For example, a regular user should see one page, and a privileged user - another one. I see two ways of approaching this issue:
One index action with conditionals:
public function index()
{
// view for privileged users
if(request()->user()->hasRole('privileged')){
return view('index_privileged');
}
// view for regular users
if(request()->user()->hasRole('regular')){
return view('index_regular');
}
return redirect('/');
}
Multiple actions:
public function index_privileged()
{
return view('index_privileged');
}
public function index_regular()
{
return view('index_regular');
}
Which approach is more "restful-friendly" and generally better?
I'm a big fan of light controllers. This might be a little overboard for a simple problem but if something like this pops up again, you'd already have everything all setup.
With that said, it might be best to create a PrivilegedUser class and a RegularUser class and give them both an index method which returns their respective views. Code them both to an interface UserInterface and make sure they both implement that.
Here is what those looked like in my test.
class RegularUser implements UserInterface
{
public function index()
{
return view('index_regular');
}
}
class PrivilegedUser implements UserInterface
{
public function index()
{
return view('index_privileged');
}
}
interface UserInterface
{
public function index();
}
Then you can add a listener which should run for the event Illuminate\Auth\Events\Login. Laravel will fire this event for you automatically when someone logs in. This goes into the file EventServiceProvider.php.
protected $listen = [
'Illuminate\Auth\Events\Login' => [
'App\Listeners\AuthLoginListener',
],
];
Now you can run php artisan event:generate to generate the new listener. Here is what my listener looks like, it should work for you.
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Foundation\Application;
class AuthLoginListener
{
/**
* Create the event listener.
*
* #param Application $app
*/
public function __construct(Application $app)
{
$this->app = $app;
}
/**
* Handle the event.
*
* #param Login $event
* #return void
*/
public function handle(Login $event)
{
if ($event->user->hasRole('privileged')) {
$this->app->bind('App\Repositories\UserInterface', 'App\Repositories\PrivilegedUser');
} else if ($event->user->hasRole('regular')) {
$this->app->bind('App\Repositories\UserInterface', 'App\Repositories\RegularUser');
}
}
}
Essentially what this is doing is telling Laravel to load up a certain class based on the type of user that just logged in. The User instance is available through the Login object which was automatically passed in by Laravel.
Now that everything is setup, we barely have to do anything in our controller and if you need to do more things that are different depending on the user, just add them to the RegularUser or PrivilegedUser class. If you get more types of users, simply write a new class for them that implements the interface, add an additional else if to your AuthLoginListener and you should be good to go.
To use this, in your controller, you'd do something like the following...
// Have Laravel make our user class
$userRepository = App::make('App\Repositories\UserInterface');
return $userRepository->index()->with('someData', $data);
Or even better, inject it as a dependency.
use App\Repositories\UserInterface;
class YourController extends Controller
{
public function index(UserInterface $user)
{
return $user->index();
}
}
Edit:
I just realized I forgot the part where you wanted to return redirect('/'); if no condition was met. You could create a new class GuestUser (I know this sounds like an oxymoron) which implements UserInterface but instead of using the AuthLoginListener, I'd bind it in a service provider when Laravel boots. This way Laravel will always have something to return when it needs an implementation of UserInterface in the event it needs this class if no one is logged in.
Well, its more like a refactoring "issue" than a rest-friendly issue. Check this guideline and you can see that most of the things that makes an api friendly is concerned to the url.
But, lets answer what you are asking. The thing you wanna do is a refactoring method but it is not only the move method but something like the extract variable.
The second option would make the code more readable, either ways are right but the second is more developer friendly. It enhances the code readability from any developer. I would recommend using the second option.
Refactoring is never enough, but read something like this, it will help you a lot writing more readable codes.

Grails Rest URL mapping how to use other 'id' then the database 'id'?

A question about rest url mappings.. I would like to use a different ​id​ in the rest calls then the database id.
Lets say i have an url mapping like this:
"/books"(resources:"book")
Then the show action would be..
def show(Book book) {
if(book == null) {
render status:404
}
else {
return [book: book]
}
}
This would load the book belonging the the id that was passed in via the rest call like: http://url/books/1
If i want to use a different property of the class Book how would i get the automatic databind working? Assume that my book also has a uuid.
How do i get http://url/books/4860ca28-9a76-480f-b898-fffedda1888f to work in the same way as the database id?

How do I correctly map versioned resources in Grails?

I have grails running a REST API and using version numbers in the URL like so: https://api.mycompany.com/v2/metadata. I need to change the parameters of one of the endpoints, so I'm bumping the version up to v3. Only one controller is affected, so I would like to delegate the remaining calls back to the controllers for v2 without having to copy/paste everything again. Here's the relevant section of my UrlMappings.groovy:
class UrlMappings {
static mappings = {
"/v3/widget"(controller: "v3widget")
"/v3/$otherResource" {
// does not work, but illustrates what I want to happen
uri = { "/v2/" + params.otherResource }
}
// rest of my file...
"/v2/metadata"(controller: 'metadata')
...
What's the correct way to do this? I'm using grails 2.2.5
I would use a variable in the uri path, and instead of your example you would have the following mappings:
class UrlMappings {
static mappings = {
"/$apiVersion/widget"(controller: "v3widget")
"/$apiVersion/otherResource"(controller: "otherResource")
// rest of my file...
"/$apiVersion/metadata"(controller: 'metadata')
...
And then you could check for the value in controller:
class OtherResourceController {
def index(){
if(params.apiVersion == 'v2') {
...
} else {
...
}
}
}
The example here is checking for the string value, but you could go a bit deeper and actually convert the string value into internal api version enum representation which might be easier to manage. You could also do that in filter.
This way you can increment the logic changes and api will have a nice fallback, will delegate to default version.
But it gets really curly when you have couple of api versions layered one on the other.
The solution I found that works relies on the fact that wildcard mappings can also accept other regular expressions:
class UrlMappings {
static mappings = {
// v3 specific
"/v3/widget"(controller: "v3widget")
// v3/v2 common
"/v[23]/$otherResource" {
// normal mappings go here
}
// v2 specific
"/v2/metadata"(controller: 'v2metadata')
...
This solution works well since I don't have to repeat any mappings, and it's clear what's different between v2 and v3 resources.

ASP.NET Web API REST Querystring - How does a client know available parameters and options?

When exposing querystring parameters using GET I have the following base URL:
https://school.service.com/api/students
This will return the first 25 students.
What if I want to return a list of students based on ONE of the following criteria:
* have accepted a job
* have received a job offer
* have no job offers
The three above choices are essentially an enum.
Therefore, the query request for students who have no job offers I assume would look like:
https://school.service.com/api/students?jobOfferStatus=3
However, I'm wondering if jobOfferStatus=3 is the proper way to handle this. If so, how would I publish/provide to the clients a list of available options for that jobOfferStatus query parameter? What about other possible query parameters and their valid options? We'll have many possible query parameters like this.
I'd love to see an example of how this should be done properly. What are the best practices?
There are two main options: documenting it, or making it discoverable. A lot of APIs have documentation where they list all of the resources and parameters for reference. Otherwise, the client won't know.
You could also make it discoverable in some way by including the options in a response. For conventions on this, search for HATEOAS if you haven't already. (I'm not really knowledgeable enough about HATEOAS myself to make a suggestion.)
I will mention that "3" is not a very meaningful value for jobOfferStatus, and there's no need for the client to know that number. You can make it anything you want -- jobOfferStatus=none or even jobOffer=none. Your controller can do the work of matching that value to your enumeration. Try to design your interface to be intuitive for developers (and, of course, write good documentation).
To handle multiple query parameters, you can use optional parameters in your function:
public HttpResponseMessage GetStudents(string jobOffer = "",
string other1 = "",
string other2 = "")
{
if (jobOffer == "accepted" && other2 == "whatever") {
// return a response
}
else {
// return a different response
}
}
When the client uses parameters by those names, you can tailor your response appropriately.
You have some options to do this, let's try to help:
1) Configure a generic route to asp.net web api knows how to solve another action's name different from Get to a get method, on the App_Start\WebConfigApi.cs class, try to add this:
config.Routes.MapHttpRoute("DefaultApiWithActionAndId",
"api/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
Using it, you can have diferent methods on the api controller:
// request: get
// url: api/Students/GetStudents
public HttpResponseMessage GetStudents()
{
return Request.CreateResponse(...);
}
// request: get
// url: api/Students/GetStudentsWithJobOffer
public HttpResponseMessage GetStudentsWithJobOffer()
{
return Request.CreateResponse(...);
}
// request: get
// url: api/Students/GetStudentsAcceptedJob
public HttpResponseMessage GetStudentsAcceptedJob()
{
return Request.CreateResponse(...);
}
2) Use a simple parameter on the Get method:
// request: get
// url: api/Students?jobOfferStatus=1
public HttpResponseMessage GetStudents(int jobOfferStatus)
{
// use jobOfferStatus parameter to fill some list
return Request.CreateResponse(...);
}
3) Use a simple method with a parameter named id, to get a default friendly url by asp.net mvc web api.
// request: get
// url: api/Students/1
public HttpResponseMessage GetStudents(int id)
{
// use the id parameter to fill some list
return Request.CreateResponse(...);
}

Dynamic SEO Friendly URLs in Yii

I want to use custom dynamic seo friendly urls in yii.
I read all kind of articles and they all say the same thing.
This is what i managed to find so far, and it does not suit my needs:
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',
or
array(
'<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>'=>'<_c>/<_a>',
'<_c:(post|comment)>/<id:\d+>'=>'<_c>/view',
'<_c:(post|comment)>s/*'=>'<_c>/list',
)
I dont need urls like: domain.com/a/b/c/d
I need: domain.com/here-goes-the-article-title-ACTION-ID
I need a expression that can identify the article title.
This is how one of my urls looks like: http://www.linkbook.ro/concurs-castiga-o-invitatie-de-trei-zile-de-festival-la-bestfest-2012-detailsU-2-882.html
where concurs-castiga-o-invitatie-de-trei-zile-de-festival-la-bestfest-2012 is the article title
detailsU is the action
2 is the database id
882 is the article id
Let me formulate my comment as an answer. You still have to implement some stuff yourself, but this should get you started:
class MyRule extends CBaseUrlRule
{
public function parseUrl($oManager, $oRequest, $sPathInfo, $sRawPathInfo)
{
// Extract database Id and article Id from $sPathInfo and perhaps put it in $_REQUEST
if ("url isn't SEO thingy")
return FALSE:
return 'articles/index';
}
public function createUrl($oManager, $sRoute, $aParameters, $sAmpersand)
{
if ("i have an SEO item to show")
return "/however you want to assemble your URL";
return FALSE;
}
}
The above example assumes that you route everything through the articles controller (action index).
Adding to the config is a matter of adding the following to your rules:
'urlManager'=>array(
'urlFormat'=>'path',
'rules'=>array(
array('class' => 'MyRule'),
'<controller:\w+>/<id:\d+>'=>'<controller>/view',
'<controller:\w+>/<action:\w+>/<id:\d+>'=>'<controller>/<action>',
'<controller:\w+>/<action:\w+>'=>'<controller>/<action>',