pass relative url in signalR hub connection - angularjs

I am trying to implement signalR in angularJS,
I want to pass relative url to hub connection, but it's making current url (on which my angular application is hosted)
My API base url : http://localhost:81/NrsService/api/TestSignal
My angular application running at
http://localhost:81
Here is my signalR setup :
$.connection.hub.url = "/NrsService/api/TestSignal";
//Getting the connection object
connection = $.hubConnection();
Like it is sending request at http://localhost:81/signalr/negotiate? but I want it to be http://localhost:81/NrsService/api/TestSignal/negotiate?

You have to edit the generated JavaScript code where the client proxy is defined. As of SignalR 2.4.0 there is a createHubProxies function defined where you should find this line of code:
signalR.hub = $.hubConnection("/signalr", { useDefaultPath: false });
Change it to the following to prevent the "/signalr" ending in your requests:
signalR.hub = $.hubConnection("", { useDefaultPath: false });
After that, you can simply change the url which should be called the way you provided in your question, e.g.:
$.connection.hub.url = "/NrsService/api/TestSignal";
If you also want to change this Url dynamically, you can use the document.location properties. In my case, I did something like this:
var subPath = document.location.pathname.substr(0, document.location.pathname.lastIndexOf("/"));
$.connection.hub.url = subPath; // subpath equals to "/NrsService/api"
Hope this helps.

Related

window.open("url","_self") works well on localhost url, but not when I put it on a server

I have an angularJS frontend that I redirect to a third party payment gateway when a customer clicks on the Pay button.
I am currently using the following function to redirect to the payment gateway:
if(status.data.result == "true"){
var ref = window.open(url,'_self');
}
};
This code works well when I try it on localhost. But does not work when I put it on the ubuntu server.
Any ideas on why?
Regards,
Galeej
Try adding return false after var ref = window.open(url,'_self'); like
var ref = window.open(url,'_self');
return false;
return false will prevent from page submit and it may work this way to properly redirect on the same page.
May be your content security policy is blocking this url to load.
You can try using _blank instead of _self to see if that is the case.
Also it would be better to use $window, because angular.
Also are you using the fully qualified URL ?
The solution that worked for us was using window.location = url

I want my $http.get(..../Login) to be dynamically change as per environment and my project name should also be dynamic

$http.get('localhost:8080/myApp/Login).success(function() {.....
…..
…
});
I want to replace 'localhost:8080/myApp with the host where I will deploy my project with project name at that time …
What I am doing is that using angular I am mapping the URL to spring controller method . So I don't want it to be static I want 'localhost:8080/myApp to be taken dynamically where I am deploying my project with the project name …
While working
Projects name: myApp
Host:localhost:8080
When deployed
Projects name: myApplication
Host:www.xyz.com
So my
$http.get('www.xyz.com/myApplication/Login). success(function(){...
….
…});
Update:I also want my project name to be automatically taken in which my js and Java controller is there
The $location service does provide information from the url. So, you could construct something to then make the dynamic host:
// given URL http://example.com/#/some/path?foo=bar&baz=xoxo
var host = $location.host();
// => "example.com"
// given URL http://user:password#example.com:8080/#/some/path?foo=bar&baz=xoxo
host = $location.host();
// => "example.com"
host = location.host;
// => "example.com:8080"
First you need to declare $location in your service then you can extract dynamic location host port.
To know more about $location service please check angular api doc
Extract Project Name:
For Extract Projet name i make my custom method which get project name from $location.absUrl() use Following function:
var url= $location.absUrl();
url= getProjectName(url);
function getProjectName(url){
var index,int;
for (int = 0; int <3; int++) {
index = url.indexOf("/");
url= url.substring(index+1);
}
var index = url.indexOf("/");
url = url.slice(0, index);
return url;
}

App Engine Endpoint: HTTP method GET is not supported by this URL

Following is my App Engine Endpoint. I annotate it as ApiMethod.HttpMethod.GET because I want to be able to make a get call through the browser. The class itself has a few dozen methods understandably. Some of them using POST. But getItems is annotated with GET. When I try to call the url through a browser, I get a 405 error
Error: HTTP method GET is not supported by this URL
The code:
#Api(name = "myserver",
namespace = #ApiNamespace(ownerDomain = "thecompany.com", ownerName = "thecompany", packagePath = ""),
version = "1", description = "thecompany myserver", defaultVersion = AnnotationBoolean.TRUE

 )

 public class myserver {
#ApiMethod(name = "getItems", httpMethod = ApiMethod.HttpMethod.GET)
public CollectionResponse<Item> getItems(#Named("paramId") Long paramId) {
…
return CollectionResponse.<Item>builder().setItems(ItemList).build();
}
}
This is not for localhost, it’s for the real server. Perhaps I am forming the url incorrectly. I have tried a few urls such as
https://thecompanymyserver.appspot.com/_ah/spi/com.thecompany.myserver.endpoint.myserver.getItems/v1/paramId=542246400
https://thecompanymyserver.appspot.com/_ah/spi/myserver/NewsForVideo/v1/542246400
The proper path for this is /_ah/api/myserver/1/getItems. /_ah/spi refers to the backend path, which only takes POST requests of a different format.
Side note: API versions are typical "vX" instead of just "X".
You can use the api explorer to find out whether you're using the correct url. Go to
https://yourprojectid.appspot.com/_ah/api/explorer
this works on the devserver as well:
http://localhost:8080/_ah/api/explorer
Also if you're not planning to use the google javascript api client you should add path="..." to your #ApiMethods, so you are sure about what the path actually is.

Dynamic content Single Page Application SEO

I am new to SEO and just want to get the idea about how it works for Single Page Application with dynamic content.
In my case, I have a single page application (powered by AngularJS, using router to show different state) that provides some location-based search functionalities, similar to Zillow, Redfin, or Yelp. On mt site, user can type in a location name, and the site will return some results based on the location.
I am trying to figure out a way to make it work well with Google. For example, if I type in "Apartment San Francisco" in Google, the results will be:
And when user click on these links, the sites will display the correct result. I am thinking about having similar SEO like these for my site.
The question is, the page content is purely depending on user's query. User can search by city name, state name, zip code, etc, to show different results, and it's not possible to put them all into sitemap. How google can crawl the content for these kind of dynamic page results?
I don't have experience with SEO and not sure how to do it for my site. Please share some experience or pointers to help me get started. Thanks a lot!
===========
Follow up question:
I saw Googlebot can now run Javascript. I want to understand a bit more of this. When a specific url of my SPA app is opened, it will do some network query (XHR request) for a few seconds and then the page content will be displayed. In this case, will GoogleBot wait for the http response?
I saw some tutorial says we need to prepare static html specifically for Search Engines. If I only want to deal with Google, does it mean I don't have to serve static html anymore because Google can run Javascript?
Thanks again.
If a search engine should come across your JavaScript application then we have the permission to redirect the search engine to another URL that serves the fully rendered version of the page.
For this job
You can either use this tool by Thomas Davis available on github
SEOSERVER
Or
you can use the code below which does the same job as above this code is also available here
Implementation using Phantom.js
We can setup a node.js server that given a URL, it will fully render the page content. Then we will redirect bots to this server to retrieve the correct content.
We will need to install node.js and phantom.js onto a box. Then start up this server below. There are two files, one which is the web server and the other is a phantomjs script that renders the page.
// web.js
// Express is our web server that can handle request
var express = require('express');
var app = express();
var getContent = function(url, callback) {
var content = '';
// Here we spawn a phantom.js process, the first element of the
// array is our phantomjs script and the second element is our url
var phantom = require('child_process').spawn('phantomjs',['phantom-server.js', url]);
phantom.stdout.setEncoding('utf8');
// Our phantom.js script is simply logging the output and
// we access it here through stdout
phantom.stdout.on('data', function(data) {
content += data.toString();
});
phantom.on('exit', function(code) {
if (code !== 0) {
console.log('We have an error');
} else {
// once our phantom.js script exits, let's call out call back
// which outputs the contents to the page
callback(content);
}
});
};
var respond = function (req, res) {
// Because we use [P] in htaccess we have access to this header
url = 'http://' + req.headers['x-forwarded-host'] + req.params[0];
getContent(url, function (content) {
res.send(content);
});
}
app.get(/(.*)/, respond);
app.listen(3000);
The script below is phantom-server.js and will be in charge of fully rendering the content. We don't return the content until the page is fully rendered. We hook into the resources listener to do this.
var page = require('webpage').create();
var system = require('system');
var lastReceived = new Date().getTime();
var requestCount = 0;
var responseCount = 0;
var requestIds = [];
var startTime = new Date().getTime();
page.onResourceReceived = function (response) {
if(requestIds.indexOf(response.id) !== -1) {
lastReceived = new Date().getTime();
responseCount++;
requestIds[requestIds.indexOf(response.id)] = null;
}
};
page.onResourceRequested = function (request) {
if(requestIds.indexOf(request.id) === -1) {
requestIds.push(request.id);
requestCount++;
}
};
// Open the page
page.open(system.args[1], function () {});
var checkComplete = function () {
// We don't allow it to take longer than 5 seconds but
// don't return until all requests are finished
if((new Date().getTime() - lastReceived > 300 && requestCount === responseCount) || new Date().getTime() - startTime > 5000) {
clearInterval(checkCompleteInterval);
console.log(page.content);
phantom.exit();
}
}
// Let us check to see if the page is finished rendering
var checkCompleteInterval = setInterval(checkComplete, 1);
Once we have this server up and running we just redirect bots to the server in our client's web server configuration.
Redirecting bots
If you are using apache we can edit out .htaccess such that Google requests are proxied to our middle man phantom.js server.
RewriteEngine on
RewriteCond %{QUERY_STRING} ^_escaped_fragment_=(.*)$
RewriteRule (.*) http://webserver:3000/%1? [P]
We could also include other RewriteCond, such as user agent to redirect other search engines we wish to be indexed on.
Though Google won't use _escaped_fragment_ unless we tell it to by either including a meta tag; <meta name="fragment" content="!">or using #! URLs in our links.
You will most likely have to use both.
This has been tested with Google Webmasters fetch tool. Make sure you include #! on your URLs when using the fetch tool.

Google App Engine Channel API with custom domains

In my GAE app (Python) I have implemented multitenancy and multi-site support based on the host part of the request object.
For example, www.foo.com/index.html and www.bar.com/index.html are both handled by the same app (e.g. myapp.appspot.com). The app reads the host-value and then decides which namespace and site-configuration to use. This works great as long as the app receives the request directly from the user-agent.
However, I would like to use the Channel API, but there is an issue because requests to /_ah/channel/connected/ and /_ah/channel/disconnected/ are not originating from the original user-agent. Instead, the request has Host: myapp.appspot.com and the parameter to=myapp.appspot.com. (The from parameter is the token I expect. Also www.foo.com/_ah/channel/jsapi is redirected to a talkgadget server which is not documented but seems to be as expected.)
I assume, the problem is caused by the code in channel.js which doesn't call my app using the original host, e.g. www.foo.com/_ah/channel/connected. Instead it uses a talkgadget.google.com host which (as far as I can tell) will then call my app, but using myapp.appspot.com, ignoring the original host and so I cannot use the request's host value for my purpose.
As a workaround, I can figure out a way of including the host information into the channel token, so when my connected and disconnected handlers receive the token, they can use the token instead.
However, I would like to know if there is a better approach, where I could still get the original host name (e.g. www.foo.com) requests to /_ah/channel/connected/ and /_ah/channel/disconnected/. Any ideas?
This is what I have tried so far (with-out any success):
Adding the custom domain host name to the JS src attribute:
<script type="text/javascript" src="//www.foo.com/_ah/channel/jsapi"></script>
I also tried to manually override the base-url of the channel socket, suggested here: https://stackoverflow.com/questions/16558776/google-app-engine-channel-api-same-origin-policy
<script type="text/javascript">
onOpened = function() {
// TODO
};
onMessage = function() {
// TODO
};
onError = function() {
// TODO
};
onClose = function() {
// TODO
};
goog.appengine.Socket.BASE_URL = "https://www.foo.com/_ah/channel/";
channel = new goog.appengine.Channel('{{channelToken}}');
socket = channel.open();
socket.onopen = onOpened;
socket.onmessage = onMessage;
socket.onerror = onError;
socket.onclose = onClose;
</script>
I couldn't find any official documentation for channel.js and I don't want to implement something that is going to break easily with the next update by Google.
Short of a proxy, I don't see a better way than including the information in-band. The problem is that the library/infrastructure (can't be certain without looking deeper) is stripping the HTTP-layer information (the Host header), and indeed you don't have any control of the HTTP layer to pass custom headers, etc. So, you either need to have the info at a lower layer (TCP doesn't even provide a means to do this, and since the entrypoint of your code is through the browser running channel.js, rather than a system-level process running on the bare network interface, this is out of the picture decisively), or at a higher layer, ie. within the channel.

Resources