2 separate controllers for the same end point in html and json or a single one? - rest

I have the end points "/customers" and "/api/v1/customers", in html and json respectively for a list of customers. Do I have to create 2 different controllers and thus actions for them? Or can I return html or json from a single controller and action depending a requested format: html or json? Note that for "/api/v1/customers" I need authentication via an Api Key.

You can have one controller and action for both endpoints, but I would advise against it.
You mentioned that those controllers need to do different stuff, so instead of adding stuff like "if json then check api key" make two separate controllers and extract common code of getting all the customers.
There is a great talk about untangling business logic from http interface: http://www.elixirconf.eu/elixirconf2016/lance-halvorsen Getting a list of customers might be out of your controllers, so at the end you will have two controllers like this:
defmodule MyApp.Api.CustomersController do
plug MaApp.ApiAuth #plug for checking api key
def index(conn, params) do
...
customers = ActualLogic.get_customers()
...
end
end
def MyApp.CustomersController do
plug MyApp.UserAuth #for example checks if user is logged in
def index(conn, params) do
...
customers = ActualLogic.get_customers()
...
end
end
At the end your controller does not perform any logic, it calls something else to do the job and action is responsible only for web stuff like parsing params, authentication via api key, session cookies and translating end result to json/html.

Related

Handle views using Phoenix as a RESTful API app

I'm having trouble understanding how to handle phoenix views properly.
Let's say that a client is calling "login_user" api.
My controller will handle the request, checking if user is enabled to access or not. As response, i need to send user's data to the caller.
Now, after controller's login logic, what's the proper way to respond?
1) fetch all data needed by the caller inside the controller:
user_data = function_that_fetch_user_data()
conn
|> put_resp_header("content-type", "application/json")
|> put_status(:ok)
|> send_resp(Status.code(:ok), user_data)
2) render the view
conn
|> put_resp_header("content-type", "application/json")
|> put_status(:ok)
|> render(login.json, user)
and fetch data inside it
defmodule CryptomonitorWeb.UserView do
use MyAppWeb, :view
def render("login.json", user) do
token = generate_user_token(user.mail)
%{
token: token,
email: user.mail,
group: "operators"
}
end
end
Getting data and building my response directly in the controller is more clear and clean to me, but i want to know what's the "right" way to handle api responses. My app will usually just return json to the client and sometimes render some html page.
Views are representation of a data you want to send to as a response. So first you need to have some data, second you have to present it in the way it have to be presented in some API or an HTML page.
You are definitely don't want to go into a database from a view, but if you don't go there, then the border is more subtle.
Still, generate_user_token looks like a data emitter, not like a data presenter. And who knows, maybe one day someone will change the function, so it'll hit a database to generate token.
To avoid related troubles, better to have data generators out of a view. Place them into controller, then send results to a view.
So, I'm voting for option 1.

Passing data between form views in Pyramid

I'm making a project in pyramid framework, so i have a view which have a form in it with 2 input type texts and a submit button.
The form is a post method, so im getting them with a POST request.
I want to send them to a new view and display them on the screen.
meaning:
on 0.0.0.0:6543 is the form on first view.
I want to display the values the user insert in the input on 0.0.0.0:6543/here
I tried with HTTPfound but i guess im missing an understanding on how to really pass the variables.
Please help me...
The easiest way to accomplish is to use sessions.
You need a session backend which stores your data on a server (see pyramid_redis_session). There are also cookie-based session solutions where all data is stored on the client side.
The first view writes all passed over data to a session:
request.session["mydata"] = value
The second view reads data from the session
print(request.session["mydata"])
Another way to pass the data from one view to another is via the URL. This does not require server-side support, unlike sessions. Also, it's RESTful ;)
return HTTPFound('/here?greeting=Hello&subject=World')
In your second view you then simply get the variables from request.GET:
greeting = request.GET.get('greeting', '')
subject = request.GET.get('subject', '')
# pass the data to the template
return {
"greeting": greeting,
"subject": subject
}
Regarding your comment: You can't use HTTPFound with POST. You can, however, directly submit your form to /here using <form method="post" action="/here" ...>. In this case you'll be able to access the data using request.POST.get('greeting').

Restful API for Templating

I am struggling with a design aspect of my restful api for templating collections of resources.
The endpoint calls for a json with the name to a particular template and a collections of tokens. The API will then create entries into numerous tables and use the tokens where appropriate.
A very simple example is:
*{
'template': 'DeviceTemplate'
'tokens': [
'customer': 1234,
'serial_number': '12312RF3242a',
'ip_address': '1.1.1.1'
]
}*
This creates a new device for the customer with that ip address along with several other objects, for instance interfaces, device users etc etc. I use the tokens in various places where needed.
I'm not sure how to make this endpoint restful.
The endpoint for /device is already taken if you want to create this resource individually. The endpoint I need is for creating everything via the template.
I want to reserve the POST /template endpoint for creating the actual template itself and not for implementing it with the tokens to create the various objects.
I want to know how to call the endpoint without using a verbs.
I also want to know if its a good idea to structure a POST with a nested JSON.
I'd suggest that you create an action on the template object itself. So right now if you do /templates/<id> you are given an object. You should include in that object a url endpoint for instantiating an instance of that template. Github follows a scheme that I like a lot [1] where within an object there will be a key pointing to another url with a suffix _url. So for instance, your response could be something like:
{
"template": "DeviceTemplate",
"id": "127",
...
"create_url": "https://yourdomain.com/templates/127/create",
...
}
Then this way you treat a POST to that endpoint the same as if this template (DeviceTemplate) was its own resource.
The way to conceptualize this is you're calling a method on an object instead of calling a function.
[1] - For example https://developer.github.com/v3/#failed-login-limit

Sail.js - routing to methods, custom policies & PATCH method

I have a few questions that I couldn't find answers anywhere online.
Does sails.js framework support HTTP PATCH method? If not - does anyone know if there is a planned feature in the future?
By default if I create method in a controller it is accessible with GET request is it the routes.js file where I need to specify that method is accessible only via POST or other type of methods?
How would you create a policy that would allow to change protected fields on entity only for specific rights having users. I.e: user that created entity can change "name", "description" fields but would not be able to change "comments" array unless user is ADMIN?
How would you add a custom header to "find" method which specifies how many items there are in database? I.e.: I have /api/posts/ and I do query for finding specific items {skip: 20; limit: 20} I would like to get response with those items and total count of items that would match query without SKIP and LIMIT modifiers. One thing that comes to my mind is that a policy that adds that that custom header would be a good choice but maybe there is a better one.
Is there any way to write a middle-ware that would be executed just before sending response to the client. I.e.: I just want to filter output JSON not to containt some values or add my own without touching the controller method.
Thank you in advance
I can help with 2 and 5. In my own experience, here is what I have done:
2) I usually just check req.method in the controller. If it's not a method I want to support, I respond with a 404 page. For example:
module.exports = {
myAction: function(req, res){
if (req.method != 'POST')
return res.notFound();
// Desired controller action logic here
}
}
5) I create services in api/services when I want to do this. You define functions in a service that accept callbacks as arguments so that you can then send your response from the controller after the service function finishes executing. You can access any service by the name of the file. For example, if I had MyService.js in api/services, and I needed it to work with the request body, I would add a function to it like this:
exports.myServiceFunction = function(requestBody, callback){
// Work with the request body and data access here to create
// data to give back to the controller
callback(data);
};
Then, I can use this service from the controller like so:
module.exports = {
myAction: function(req, res){
MyService.myServiceFunction(req.body, function(data){
res.json(data);
});
}
}
In your case, the data that the service sends back to the controller through the callback would be the filtered JSON.
I'm sorry I can't answer your other questions, but I hope this helps a bit. I'm still new to Sails.js and am constantly learning new things, so others might have better suggestions. Still, I hope I have answered two of your questions.

How to implement a POST-REDIRECT-GET in Play Framework

Let's say I have two controller methods: Users.preInsert and Users.insert. The preInsert method is the one used to display the user entry form (GET), while the insert method is responsible for the actual insertion (POST) or calling the 'insert' service.
This is how the routes looks like:
GET /users/add controllers.Users.preInsert(...)
POST /users/add controllers.Users.insert(...)
So how do I redirect a request (POST to GET) without losing the parameters like error messages returned from the insert service and the values inputed by the client so that they can be accessed and displayed in the entry form. The parameters may involve some complex objects. I have implemented it using the Caching API but I would like to know if there are any better ways of doing it.
That's the exact purpose of the Form objects (http://www.playframework.com/documentation/2.1.1/ScalaForms).
And I think there is a an error in your routes, it could look like:
GET /users/add controllers.Users.preInsert(...)
POST /users/add controllers.Users.insert(...)
You should definitively take a look at the form sample.
You don't need to redirect it back to the preInsert action, instead at the beginning of the insert check if form has errors and it it has display your view containing form (the same which you used in preInsert). It's described in the doc mentioned by nico_ekito in section Handling binding failure