In our NextJS application we have a URL that is fed with various query strings.
With some query strings, however, we have the problem that they are displayed in encoded form. For example like this:
http://localhost:8080/my-app/test-app?project=project%3Aone&project=project%3Atwo
As you can see, the colons are replaced with %CA.
I know that this may be a default behavior, but I need the colons in the URL.
Is there any way I can get this? So I need to URL above like:
http://localhost:8080/my-app/test-app?project=project:one&project=project:two
We are using URLSearchParams() like this:
const constructQueryString = (params: any) => {
const searchParams = new URLSearchParams();
const projects = params.project.split(',');
projects.forEach((p) => {
urlSearchParams.append('project', p);
});
return searchParams.toString();
};
These are escape codes for special characters in the URL that are necessary and cant be avoided. Also, there's no need for you to use URLSearchParams. Just use router.query will give you query and router.pathname for the path (/my-app/test-app) in nextJS
Use the decodeURIComponent global function in Javascript
const decodedPath = decodeURIComponent("http://localhost:8080/my-app/test-app?project=project%3Aone&project=project%3Atwo")
The Result is what you want as below:
http://localhost:8080/my-app/test-app?project=project:one&project=project:two
Related
Before I go on, let me say that I've looked through a number of threads already and can't find an answer that works for me.
Basically, I've built a custom link shortener and I'm using URLSearchParams to pull the URL to be shortened and the custom slug from the URL search query as follows:
var e = window.location.search;
const urlParams = new URLSearchParams(e);
const url = urlParams.get("url");
const slug = urlParams.get("slug");
Where the format for a query is: ?url=https://google.com&slug=customslug
After the parameters are handled, the URL string is treated with trim() to remove any whitespace. The final output is encoded with encodeURIComponent() when the API I'm using (https://short.io) is called.
However, I want to be able to pass URLs with &, like so: ?url=https://google.com/&testing&slug=customslug. My ideal solution would simply treat any & that isn't part of the &slug parameter as a part of the URL contained within the &url parameter. Currently, the & character is ignored if it isn't attached to a valid parameter (url or slug).
I have tried encoding the query input using encodeURIComponent(), but that results in a failure to pick up on either defined parameter. I have also tried splitting the input using split("&slug",1), but that results in an array and I cannot pass arrays to the Short.io API.
Any suggestions?
You should use the URL Encoded ampersand symbol %26.
var e = "?url=https://google.com/%26testing&slug=customslug";
const urlParams = new URLSearchParams(e);
const url = urlParams.get("url");
const slug = urlParams.get("slug");
console.log(url);
console.log(slug);
I solved my issue by building off of #CherryDT's comment about using window.location.hash to get my URL string. Ultimately, I chose to forgo the idea of a slug in the address bar, since it would cause further problems with my script.
While this solution is only applicable for my purposes, I'm detailing the solution because it functions as a workaround for the issue of not being able to encode a passed URL string from the address bar. Might be useful to someone, someday.
var e = window.location.href.replace(window.location.hash, '');
if (e.endsWith("?") === true) {
var url = window.location.hash.substr(1);
if (url === "") {
// Error code
} else {
console.log("A URL to be shortened was provided via hash.");
// Pass url to rest of script
}
}
For example i have a string hello check this out https://example.com?myparam=asd it is cool right?
and I want to get the https://example.com?myparam=asd part but the ?myparam=asd part can change like ?myparam=newerthing
Sorry if its a bit messy, I don't know how to describe it better.
You could run your text through a regular expression that captures URL patterns. I just went with a popular one. There are a ton of URL patterns floating around on the web.
For each match, you can pass it to the URL constructor which then gets passed to an IIFE. Once you have the URL object, you can obtain information about the it such as origin and searchParams. The searchParams object should already be a URLSearchParams object, so you can take the entries() and pass them to Object.fromEntries to get a key-value pair object.
This is rudimentary at best, but it's a start.
// Via: https://stackoverflow.com/a/29288898/1762224
const URL_REGEX = /(?:(?:https?|ftp|file):\/\/|www\.|ftp\.)(?:\([-A-Z0-9+&##\/%=~_|$?!:,.]*\)|[-A-Z0-9+&##\/%=~_|$?!:,.])*(?:\([-A-Z0-9+&##\/%=~_|$?!:,.]*\)|[A-Z0-9+&##\/%=~_|$])/igm;
const extractUrls = text => text.match(URL_REGEX).map(matched => (url => ({
url: url,
origin: url.origin,
searchParams: Object.fromEntries(url.searchParams.entries())
}))(new URL(matched)));
const urls = extractUrls("hello check this out https://example.com?myparam=asd it is cool right");
console.log(urls);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Feel free to use URLSearchParams
You'll able to parse and get params keys and values.
Quick example:
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
urlParams.get('myparam')
I need more details, but maybe u need somethinng like this
const str = 'hello check this out https://example.com?myparam=asd it is cool right?'
const regExUrl = /(myparam=)[^ \&]+/
console.log(str.replace(regExUrl, '$1' + 'neverthing'))
So here is a description of the problem that I've been talked to solve:
We need some logic that extracts the variable parts of a url into a hash. The keys
of the extract hash will be the "names" of the variable parts of a url, and the
values of the hash will be the values. We will be supplied with:
A url format string, which describes the format of a url. A url format string
can contain constant parts and variable parts, in any order, where "parts"
of a url are separated with "/". All variable parts begin with a colon. Here is
an example of such a url format string:
'/:version/api/:collection/:id'
A particular url instance that is guaranteed to have the format given by
the url format string. It may also contain url parameters. For example,
given the example url format string above, the url instance might be:
'/6/api/listings/3?sort=desc&limit=10'
Given this example url format string and url instance, the hash we want that
maps all the variable parts of the url instance to their values would look like this:
{
version: 6,
collection: 'listings',
id: 3,
sort: 'desc',
limit: 10
}
So I technically have a semi-working solution to this but, my questions are:
Am I understanding the task correctly? I'm not sure if I'm supposed to be dealing with two inputs (URL format string and URL instance) or if I'm just supposed to be working with one URL as a whole. (my solution takes two separate inputs)
In my solution, I keep reusing the split() method to chunk the array/s down and it feels a little repetitive. Is there a better way to do this?
If anyone can help me understand this challenge better and/or help me clean up my solution, it would be greatly appreciated!
Here is my JS:
const obj = {};
function parseUrl(str1, str2) {
const keyArr = [];
const valArr = [];
const splitStr1 = str1.split("/");
const splitStr2 = str2.split("?");
let val1 = splitStr2[0].split("/");
let val2 = splitStr2[1].split("&");
splitStr1.forEach((i) => {
keyArr.push(i);
});
val1.forEach((i) => {
valArr.push(i);
});
val2.forEach((i) => {
keyArr.push(i.split("=")[0]);
valArr.push(i.split("=")[1]);
});
for (let i = 0; i < keyArr.length; i++) {
if (keyArr[i] !== "" && valArr[i] !== "") {
obj[keyArr[i]] = valArr[i];
}
}
return obj;
};
console.log(parseUrl('/:version/api/:collection/:id', '/6/api/listings/3?sort=desc&limit=10'));
And here is a link to my codepen so you can see my output in the console:
https://codepen.io/TOOTCODER/pen/yLabpBo?editors=0012
Am I understanding the task correctly? I'm not sure if I'm supposed to
be dealing with two inputs (URL format string and URL instance) or if
I'm just supposed to be working with one URL as a whole. (my solution
takes two separate inputs)
Yes, your understanding of the problem seems correct to me. What this task seems to be asking you to do is implement a route parameter and a query string parser. These often come up when you want to extract data from part of the URL on the server-side (although you don't usually need to implement this logic your self). Do keep in mind though, you only want to get the path parameters if they have a : in front of them (currently you're retrieving all values for all), not all parameters (eg: api in your answer should be excluded from the object (ie: hash)).
In my solution, I keep reusing the split() method to chunk the array/s
down and it feels a little repetitive. Is there a better way to do
this?
The number of .split() methods that you have may seem like a lot, but each of them is serving its own purpose of extracting the data required. You can, however, change your code to make use of other array methods such as .map(), .filter() etc. to cut your code down a little. The below code also considers the case when no query string (ie: ?key=value) is provided:
function parseQuery(queryString) {
return queryString.split("&").map(qParam => qParam.split("="));
}
function parseUrl(str1, str2) {
const keys = str1.split("/")
.map((key, idx) => [key.replace(":", ""), idx, key.charAt(0) === ":"])
.filter(([,,keep]) => keep);
const [path, query = ""] = str2.split("?");
const pathParts = path.split("/");
const entries = keys.map(([key, idx]) => [key, pathParts[idx]]);
return Object.fromEntries(query ? [...entries, ...parseQuery(query)] : entries);
}
console.log(parseUrl('/:version/api/:collection/:id', '/6/api/listings/3?sort=desc&limit=10'));
It would be even better if you don't have to re-invent the wheel, and instead make use of the URL constructor, which will allow you to extract the required information from your URLs more easily, such as the search parameters, this, however, requires that both strings are valid URLs:
function parseUrl(str1, str2) {
const {pathname, searchParams} = new URL(str2);
const keys = new URL(str1).pathname.split("/")
.map((key, idx) => [key.replace(":", ""), idx, key.startsWith(":")])
.filter(([,,keep]) => keep);
const pathParts = pathname.split("/");
const entries = keys.map(([key, idx]) => [key, pathParts[idx]]);
return Object.fromEntries([...entries, ...searchParams]);
}
console.log(parseUrl('https://www.example.com/:version/api/:collection/:id', 'https://www.example.com/6/api/listings/3?sort=desc&limit=10'));
Above, we still need to write our own custom logic to obtain the URL parameters, however, we don't need to write any logic to extract the query string data as this is done for us by using URLSearchParams. We're also able to lower the number of .split()s used as we can obtain use the URL constructor to give us an object with a parsed URL already. If you end up using a library (such as express), you will get the above functionality out-of-the-box.
I am trying to use URLSearchParams to extract the url parameters whose values are AES encrypted. But I noticed that if the value contains '+' sign, URLSearchParams is not giving proper value.
For example, if url is 'https://www.google.com/?data=a+b', URLSearcParams is giving data as 'a b'. I am using below code.
var url = new URL('https://www.google.com/?data=a+b')
url.searchParams.get('data')
Can anyone please let me know, if there is any other way to extract the url parameter values.
You must use searchParams.append() in order to set querystring params properly. I made a simple object with all keys + values and then a helper function to append the contents of the objec properly:
const url = new URL('https://www.google.com/')
const params = {
'data': 'a+b',
'moarData': 'c+d'
};
Object.entries(params).forEach(([key, val]) => url.searchParams.append(key, val));
console.log(url.searchParams.get('data'));
console.log(url.searchParams.get('moarData'));
I am trying to insert value to URL which I want to visit. I use this (for example):
const idp = '10'
cy.visit('http://test.com/aaa/bbb?id=${idp}')
but when I run this, it will ends on this http://test.com/aaa/bbb?id=$%7Bidp%7D
instead of id=10.
Also I am interested how can I get value from URL to variable.
For example I have URL http://test.com/aaa/bbb?id=5 and I want to create variable idc which will have value 5.
I think you are using the wrong quotes, you need to use backticks to use Template Literals:
cy.visit(`http://test.com/aaa/bbb?id=${idp}`)
You can then use cy.url() to get the current URL as a string and use JavaScript to parse the string as normal.
For the first part of your question you need to use backticks(also called grave accents) as outlined in the Template Literals docs instead of regular quotes. Your line would then look like -
cy.visit(`http://test.com/aaa/bbb?id=${idp}`)
For the second part of your question, you can use URLSearchParams (Note this DOES NOT work in IE). An example would be -
var url = new URL("http://test.com/aaa/bbb?id=5");
var searchParams = new URLSearchParams(url.search);
const urlParams = new URLSearchParams(searchParams );
const myParam = urlParams .get('id');