there is a video content type field in which there is a link slug, and when a new video is created, in the get request we get a null slug. tell me what's the matter. didn't install slugify
docs
getting stuck at getting {slug : null} after api call in strapi?
ok, this is what I did
I made a variable before POST request based on one of my form fields (eg:name field)
my formValues is an object with values of form fields like this
formValues = {name:"whatever" , decsription:"whatever"}
make a variable:
const slug = formValues.name.split(" ").join("-") + "-" + Math.random();
now we might have same names, so that's why I used a random value (you might want to use uuid or something like that)
then you send it like this
const res = await axios.post(`${API_URL}/api/events`,{...formValues, slug });
// you may not need to send an object with the shape like this
// but the point is you concat your custom slug to the object you want to send
notice I'm adding a custom slug from frontend which is somehow random but based off of one of the fields, but it doesn't really matter, right now strapi does not have any documentation about this common problem, it seems like the best solution might be usingstrapi-plugin-slugify but if that didn't work for you feel free to use my solution
Related
I want to implement a mechanism to obfuscate the id fields in my application . Right now all the id fields are integers. I want to use some sort of reversible hashing to create random strings corresponding to the id fields. Also, I am trying to accomplish this with minimal changes to the overall project.
One thing that came to my mind was to write a middleware to intercept every request and response object and check for the presence of id field. If the request contains id field and it is an obfuscated version, decode the string and replace the request parameter with the integer id.
If the response contains the integer id, run the encode function on it to send the obfuscated id to the client.
The problem I am facing is with modifying the req object. The id field can be present in req.body or req.params or res.query. However, in the middleware, I cannot see the id field when it is present in req.params.
I tried using policies. But the problem I am facing there is even after changing the req.params, the changes are lost when the control reaches the controller. What would be the recommended way of solving this problem ?
Here is the sample code:
In the policy:
module.exports = function (req, res, next) {
req.params.id = '12345';
req.query.pageSize = 30;
req.body = {};
sails.log.info('req.params', req.params);
sails.log.info('req.query', req.query);
sails.log.info('req.body', req.body);
return next();
};
I am just modifying values of req.params, req.query and req.body.
When I try to access these values in the controller, the values of req.query and req.body are the modified values as changed in the policy. However, req.params changes back to what was sent by the client and the changes made in the policy are lost
I think you are confusing policy and middleware? Is your code above in api/policies? If so, you still need to define which controller(s) this policy is applied to in config/policies.
So config/policies.js should look like:
modue.exports.policies = {
// If you really want this policy for every controller and action
'*': 'policyName.js',
// If you want it for a specific controller. '*' denotes every action in controller
specificController: {
'*': 'policyName.js'
},
// If you want it for a specific action on a specific controller
specificController: {
'specificAction': 'policyName.js'
}
};
Also I'd like to add. Policies are generally meant for authorization of controllers but this seems like a decent use case. Since every single request is not going to have these fields this should be a policy. Policies are great when applying something to a handful of controllers/actions. Middleware is great when you need to apply to every single action that comes into your app.
http://sailsjs.org/documentation/concepts/policies
http://sailsjs.org/documentation/concepts/middleware
Gitter response:
sgress454 #sgress454 10:45
#mandeepm91
In the policy, if I change req.body or req.query, the changes persist in the next policy or controller. However, changes made to req.params are lost.
This is one of the main use cases for req.options. This object is intended to be used to store request data that can be mutated by one handler before being passed on to the next. The req.params object is meant to provide each handler with a copy of the original request parameters.
How you approach this really depends on what your goal is. If you really need each request handler (that is, policies and controller actions) to see an encoded version of the ID, then a good approach would be to encode the ID in a policy as #S-Stephen suggested, store that value in req.options.id and refer to that in your other request handlers. If, on the other hand, you're really only concerned with the response having the encoded ID, the suggested practice is to use the res.ok() response in your handlers (rather than res.send() or res.json), and adjust the code for that response in api/responses/ok.js to encode the ID before sending. You can also use a custom response if this is only required for certain requests. See custom responses for more info.
Hi #sadlerw, you should be able to modify the code for res.ok() in your api/responses/ok.js file to have it always return JSON if that's what you want for every response. If it's something you only want for certain actions, you could create a custom response instead and use that where appropriate.
I'm doing a small project that pulls data from tmdb's API.
Right now I have a /tv view that takes an id and request the TV show associated with that id. It results in a url like example.com/tv/23521. Looking at tmdb's own site their URL structure seems to something like "id-slug-title". Regardless of what comes after the ID it still redirects you to the right page.
How is that done? It would seem that it takes in the URL, splits it at "-" and uses the first parameter as ID. I am not sure how to do that in Flask though. I was thinking of using before and after request methods, but I'm worried that will just make unnecessary API calls. In order to get the slug title, I would have to make at least one call with the ID to get the title and then slugify that title.
The route accepts both an id and a slug, where the slug is optional:
#app.route('/tv/<int:id>', defaults={'slug': None})
#app.route('/tv/<int:id>-<slug>')
def tv(id, slug):
# ...
Note that you don't have to do any splitting yourself; the route matches if there is an integer number followed by a dash and some more text, or if it is just a number.
Only the id parameter is needed to find the right page. The slug is simply checked against the 'canonical' and you are redirected if it doesn't match:
page = load_page(id)
if slug != page.slug:
return redirect(url_for('tv', id=id, slug=page.slug))
Don't recalculate the slug each time, just store that in the database. You'll have to load the page info anyway for you to be able to serve it.
You could put that behaviour in a decorator and pass in the loaded page data into the view:
#app.route('/tv/<int:id>', defaults={'slug': None})
#app.route('/tv/<int:id>-<slug>')
#tv_page
def tv(page):
# ...
with tv_page then handling the parameters:
from functools import wraps
def tv_page(view_func):
#wraps(view_func)
def wrapper(id, slug):
page = load_page(id)
if slug != page.slug:
return redirect(url_for('tv', id=id, slug=page.slug))
return view_func(page)
return wrapper
I'd like to display Instagram photos matching particular tags on my site. I don't see a way to search for all tags over all time, so I am implementing a timer that periodically checks /tags/tag-name/media/recent for my desired tags. Then I am caching the .id attribute of any as-yet-unseen media, so I can still have access to that item if and when /tags/tag-name/media/recent no longer returns that item.
Now I am ready to embed the images on my site, but I think saving .id is mistaken. The second available embed endpoint - /p/shortcode/media - looks close. It issues a redirect to the image, which will suffice for my task, but it wants a 'shortcode', not an id.
How do I get this shortcode? There is no .shortcode attribute on the media objects returned from /tags/tag-name/media/recent. Should I use a regex to parse the .link attribute, assuming that the link will take the form http://instagr.am/p/shortcode/? Or is there a better technique to remember and later display images that match my desired tags?
Preferring regex solution over String.split, this is what I ended up doing:
//expecting http://instagr.am/p/BWl6P/
var linkrx = /\/p\/([^\/]+)\/$/;
// find /p/, then 1 or more non-slash as capture group 1, then / and EOL
if(igPic.link.match(linkrx) !== null){
var shortcode = igPic.link.match(linkrx)[1];
};
No other way, just use .split() from the link attribute. This will give you the shortcode:
link.split("/")[4]
/p/([^/]+)(/.)*$
this pattern match also links like:
https://instagram.com/p/6H_CiIrdKn/?taken-by=7imet4
https://instagram.com/p/6H_CiIrdKn
This will always return the last component of a path:
'http://instagr.am/p/D/'.replace(/\/$/i, '').split("/").pop()
So http://instagr.am/p/D/ becomes D
tl;dr
Remove any trailing slash (//$/i, a/b/c/ → a/b/c)
Split by / (a/b/c → [a,b,c])
Get the last item in the [a,b,c] array (.pop(), [a,b,c] → c)
I'm writing a simple API, and building a simple web application on top of this API.
Because I want to "consume my own API" directly, I first Googled and found this answer on StackOverflow which answers my initial question perfectly: Consuming my own Laravel API
Now, this works great, I'm able to access my API by doing something like:
$request = Request::create('/api/cars/'.$id, 'GET');
$instance = json_decode(Route::dispatch($request)->getContent());
This is great! But, my API also allows you to add an optional fields parameter to the GET query string to specify specific attributes that should be returned, such as this:
http://cars.com/api/cars/1?fields=id,color
Now the way I actually handle this in the API is something along the lines of this:
public function show(Car $car)
{
if(Input::has('fields'))
{
//Here I do some logic and basically return only fields requested
....
...
}
I would assume that I could do something similar as I did with the query string parameter-less approach before, something like this:
$request = Request::create('/api/cars/' . $id . '?fields=id,color', 'GET');
$instance = json_decode(Route::dispatch($request)->getContent());
BUT, it doesn't seem so. Long story short, after stepping through the code it seems that the Request object is correctly created (and it correctly pulls out the fields parameter and assigns id,color to it), and the Route seems to be dispatched OK, but within my API controller itself I do not know how to access the field parameter. Using Input::get('fields') (which is what I use for "normal" requests) returns nothing, and I'm fairly certain that's because the static Input is referencing or scoping to the initial request the came in, NOT the new request I dispatched "manually" from within the app itself.
So, my question is really how should I be doing this? Am I doing something wrong? Ideally I'd like to avoid doing anything ugly or special in my API controller, I'd like to be able to use Input::get for the internally dispatched requests and not have to make a second check , etc.
You are correct in that using Input is actually referencing the current request and not your newly created request. Your input will be available on the request instance itself that you instantiate with Request::create().
If you were using (as you should be) Illuminate\Http\Request to instantiate your request then you can use $request->input('key') or $request->query('key') to get parameters from the query string.
Now, the problem here is that you might not have your Illuminate\Http\Request instance available to you in the route. A solution here (so that you can continue using the Input facade) is to physically replace the input on the current request, then switch it back.
// Store the original input of the request and then replace the input with your request instances input.
$originalInput = Request::input();
Request::replace($request->input());
// Dispatch your request instance with the router.
$response = Route::dispatch($request);
// Replace the input again with the original request input.
Request::replace($originalInput);
This should work (in theory) and you should still be able to use your original request input before and after your internal API request is made.
I was also just facing this issue and thanks to Jason's great answers I was able to make it work.
Just wanted to add that I found out that the Route also needs to be replaced. Otherwise Route::currentRouteName() will return the dispatched route later in the script.
More details to this can be found on my blog post.
I also did some tests for the stacking issue and called internal API methods repeatedly from within each other with this approach. It worked out just fine! All requests and routes have been set correctly.
If you want to invoke an internal API and pass parameters via an array (instead of query string), you can do like this:
$request = Request::create("/api/cars", "GET", array(
"id" => $id,
"fields" => array("id","color")
));
$originalInput = Request::input();//backup original input
Request::replace($request->input());
$car = json_decode(Route::dispatch($request)->getContent());//invoke API
Request::replace($originalInput);//restore orginal input
Ref: Laravel : calling your own API
This is essentially a continuation of the question here: Nodejs Passport display username.
app.get('/hello', function(req, res) {
res.render('index.jade', { name: req.user.username });
});
So users log in via PassportJS, and goes to index.jade, which contains #{name} in the body, which will be replaced by the value of req.user.username.
Question: Is it possible to use the value of req.user.username in index.jade's JavaScript? I tried assigning its value to a variable but it doesn't work.
I have been using the trick of having a hidden input with #{name} as value:
input(type='hidden', id='variableName', value='#{name}')
Then JavaScript can access this value using:
$("#variableName").val()
This works. But does it have any potential downside like security issues? What is the right way to do this?
You have a few options. One of them is what you did and put the value inside you html. You can also solve it by doing:
script
window.name = #{name};
This will create an inline script that sets the variable. The other option you have is using ajax. That means you probably need to make an extra route to reply to that request.