NextJS Dynamic Routing - Is it possible to set array of route names? - javascript

I'm using dynamic routing in my app. Here is the folder structure I'm working with
-pages
-department
-[slug].js
Now I want to show the page if only the slug value is cse, eee, swe, english e.t.c otherwise I want to show the 404 Not Found page. Is there any way to set the array of route names ? Then, if slug value(which I can get using getServerSideProps) is included in that array, I will serve the page otherwise serve the 404 Not Found page.
Why I need that ?
In getServerSideProps function, I'm getting data from api end-point. In this case if slug value is not included in the api-endpoint, server throws Request failed with status code 501 error!
So if slug value is cse, the api-endpoint is available and I can get the data. If slug value is anything or xyasdfs, the api-endpoint is not available and I can't get the data and the server throws the following error.

i would check in my component for a property in my response body.. like, if all the departments have a name,, then check for it, if it doesn't exist, then return a 404.

It should be something like this to render from server side
Page.getInitialProps = async ({query}) => {
if(query.slug == 'cse') {
//codes
}
}
or you can use next Router hook
https://nextjs.org/docs/routing/dynamic-routes

Related

How to prevent user from accessing JSON data from query parameters?

I am developing a really simple webapp that searches for a company's stocks.
Here is the JS code (uses AJAX to fetch the company's stock from the server):
document.getElementById("requestQuoteBtn").addEventListener("click", function createQuoteRequest(){
var quoteSymbol = document.getElementById("requestedSymbol").value;
var quoteRequest = createAJAX();
quoteRequest.open('GET', '/quote?sym='+quoteSymbol);
quoteRequest.send();
quoteRequest.onload = function getQuoteRequest(){
if(quoteRequest.status == 200){ // SUCCESSFUL
displayQuoteData(false, JSON.parse(quoteRequest.response)); // basically shows a hidden div with the data
}
else // NO COMPANY W/ THIS SYMBOL FOUND
displayQuoteData(true, null);
};
});
Here is the Flask code:
#app.route("/quote", methods=["GET"])
#login_required
def quote():
requestedSymbol = request.args.get("sym")
if not requestedSymbol:
return "no symbol"
quoteData = lookup(requestedSymbol) # USES AN API TO FETCH COMPANY'S STOCK
if quoteData is None:
return "NONE", 404
else:
return quoteData
The issue is that if the user accesses, for example, this URL:
www.mywebsite.com/quote?sym=AAPL
It will literally show a raw HTML with JSON's data, instead of my website with the data:
{"name":"Apple, Inc.","price":"$245.18","symbol":"AAPL"}
How can I prevent this?
If you simply want to make sure that users do not accidentally access your api endpoint when trying to access your website (aka this is about user experience and your not concerned with adding auth to your API endpoint)
The easiest way is to create separate routes for your api and your client routing
Update:
#app.route("/api/quote", methods=["GET"])
Likewise update:
quoteRequest.open('GET', '/api/quote?sym='+quoteSymbol);
Your client routing will still be:
#app.route("/quote", methods=["GET"])
If you want to make sure that nobody can access your api endpoint then you need to add some sort of authorization to your endpoint.
If you do not secure your API with some authorization then anyone can access the data you return from your server API simply by visiting the route.
Either way setting up separate routes for your API endpoints and client side routes should solve the problem of showing API data instead of your client template when visiting:
mywebsite.com/quote?sym=AAPL

How to clear the search parameter that caused the error?

Let's say I have a page that renders search results depending on the parameters in the URL like so:
https://www.someurl.com/categories/somecategory?brands=brand1,brand2,brand3
Which results in the page showing only brand1, brand2, and brand3 listings. I also have a filter section on the side like so:
[o] Brand 1
[ ] Brand 2
[o] Brand 3
[o] Brand 4
By ticking the items, the URL will get updated with the corresponding parameters. Basically, what happens is that I am fetching data from an API by passing the URL parameters as arguments, which then the server side endpoint takes in to return to me the matching data.
Now the problem is that, if a user types into the URL an invalid parameter e.g.
https://www.someurl.com/categories/somecategory?brands=somegibberish
The server will return an error (which I then display on the page).
However, when I tick one or more of the filters, since what it does is merely append into the URL more parameters, the server will always return an error as the errant parameter is still being sent over:
https://www.someurl.com/categories/somecategory?brands=somegibberish,brand1,brand2
To solve this, currently, when someone clicks a filter and error is not null, I just clear the parameters like so:
componentDidUpdate(prevProps)
if (prevProps.location.search !== location.search) {
if (
someobject.error &&
!someobject.list.length
) {
this.props.history.replace("categories", null);
this.props.resetError();
}
}
}
Which results in the path becoming:
https://www.someurl.com/categories/
But the UX of that isn't smooth because when I click a filter (even if there was an error), I expect it to do a filter and not to clear everything. Means if I have this path previously (has an error param):
https://www.someurl.com/categories/somecategory?brands=somegibberish,brand1
..and I click on brand2 in my filters, the path should become:
https://www.someurl.com/categories/somecategory?brands=brand1,brand2
But am quite stumped as to how to know which of the parameters has to be removed. Any ideas on how to achieve it? Should the server return to me the 'id' that it cannot recognize then I do a filter? Currently, all the server returns to me is an error message.
I agree with SrThompson's comment to not support typing of brands in the app since anything outside of your list results in an error anyway.
Expose an interface with the possible brands for the user to make a selection from.
With that said, here's how you can go about filtering the brands in the request URL.
Convert the URLstring to a URL object and retrieve the value for "brands" query parameter from its search params.
const url = new URL(
"https://www.someurl.com/categories/somecategory?brands=somegibberish,brand1,brand2")
const brands = url.searchParams.get("brands")
Filter brands that are not included in the filter list
const BRAND_FILTER = ['brand1', 'brand2']
const allowedBrands = brands.split(',')
.filter(brand => BRAND_FILTER.includes(brand))
.join(',')
Update the brand query parameter value
url.searchParams.set("brands", allowedBrands)
Then get the URL to be used for the request.
const requestURL = url.toString();

AngularJs bookmarkable url and query parameters

I'm trying to fix one mistake which one of the previous developer has did. Currently in project we have half-bookmarkable pages. Why half? Because if token has expired user will be redirect to resourse where he has to provide his credentials and on success he will be redirect to previous resource which he has used before. Sounds good for now. The problem here is next, some of resources have server side filtering and highly complicated logic which comes in the end to the one object like this:
param = {
limit: 10,
offset: 0,
orderBy: 'value',
query: 'string query can include 10 more filters'
};
then thru the service it sends a request to the end point
var custom = {};
function data(param) {
custom.params = param;
return $http.get('/data_endpoint', custom)
.then(function (response) {
return response.data;
});
From this part request works fine and response is correct but url isn't. User cannot store current url with search params because url doesn't have it and if user will try to get back to previous page with applied filters he won't be able to do that.
Also there is interceptor in this application, which add one more query param to every api request, because every api request requires some specific validation. This param is always the same.
I have tried to add $location.search(param) exactly to this data service but it breaks the app with infinity loop of login / logout looping, because interceptor stops working and all other request are sending without special query param
Then I thought to add this params inside interceptor like this:
config.params.hasOwnProperty('query') ? $location.search(config.params) : $location.search();
But it still breaks app to infinity loop.
This method adds query to url but doesn't allow to modify it, so if user applied additional filtering, it doesn't send new modified request but sends old one.
if (config.params.hasOwnProperty('query')){
for (var key in config.params){
$location.search(key, config.params[key])
}
}
That's why I decided to ask question here, probably somebody gives an advice how to solve this problem. Thanks in advance.

How do I generate a unique link that will load a session with certain docs available to the client?

Sorry for the bad phrasing.
Essentially, I want to be able to generate a link to a page, which will load a session of certain docs.
For example, Links.find() returns to Client A Links.find({clientName:"A"}). Now Client A wants to send this series of elements to his friend, and wants to do so by sending him a link which loads a client instance that can see Links.find({clientName"A"}).
Any input at all would be greatly appreciated.
Add Iron Router to your project. Then create a route that puts the relevant query into the URL, for example (in a client-loaded JavaScript file):
Router.map(function () {
this.route('client', {
path: '/client/:_clientName',
before: function () {
this.subscribe('client', this.params._clientName).wait();
}
}
}
Then a URI like http://yourapp.com/client/A would cause the client template to render (by default it uses the same name as the route name, unless you specify a different name) subscribing to the client subscription using "A" as the subscription parameter. This would be paired on the server side with:
Meteor.publish('client', function (clientName) {
// Clients is a Meteor collection
return Clients.find({clientName: clientName});
});
So that's how to process links after they've been generated. As for creating them, just work backwards: what query parameters are you passing to your subscription (that in turn get put into the find() call to MongoDB)? Identify each of them and write some code that adds them to an appropriate URI—in this case, your function would simply concatenate "http://yourapp.com/client/" with clientName, in this case "A". Obviously much-more-complicated routes/URIs and queries are possible, for example http://yourapp.com/events/2012-01-01/2012-12-31 with an Iron Router route path of /events/:_fromDate/:_toDate and so on.

Struggling to build a JS/PHP validation function for my app

I have a web service that returns a JSON object when the web service is queried and a match is found, an example of a successful return is below:
{"terms":[{"term":{"termName":"Focus Puller","definition":"A focus puller or 1st assistant camera..."}}]}
If the query does not produce a match it returns:
Errant query: SELECT termName, definition FROM terms WHERE termID = xxx
Now, when I access this through my Win 8 Metro app I parson the JSON notation object using the following code to get a JS object:
var searchTerm = JSON.parse(Result.responseText)
I then have code that processes searchTerm and binds the returned values to the app page control. If I enter in a successful query that finds match in the DB everything works great.
What I can't work out is a way of validating a bad query. I want to test the value that is returned by var searchTerm = JSON.parse(Result.responseText) and continue doing what I'm doing now if it is a successful result, but then handle the result differently on failure. What check should I make to test this? I am happy to implement additional validation either in my app or in the web service, any advice is appreciated.
Thanks!
There are a couple of different ways to approach this.
One approach would be to utilize the HTTP response headers to relay information about the query (i.e. HTTP 200 status for a found record, 404 for a record that is not found, 400 for a bad request, etc.). You could then inspect the response code to determine what you need to do. The pro of this approach is that this would not require any change to the response message format. The con might be that you then have to modify the headers being returned. This is more typical of the approach used with true RESTful services.
Another approach might be to return success/error messaging as part of the structured JSON response. Such that your JSON might look like:
{
"result":"found",
"message":
{
"terms":[{"term":{"termName":"Focus Puller","definition":"A focus puller or 1st assistant camera..."}}]}
}
}
You could obviously change the value of result in the data to return an error and place the error message in message.
The pros here is that you don't have to worry about header modification, and that your returned data would always be parse-able via JSON.parse(). The con is that now you have extra verbosity in your response messaging.

Categories