This is my jquery code to call web api
var request = {
RequestId: "123",
DeviceId: "ACU-B2-01-R1",
AccessType: "Unlock",
LoginId: "tester",
Password: "tester"
};
$.ajax({
url: 'http://localhost:55208/api/accesspanel',
type: 'POST',
data: JSON.stringify(request),
dataType: 'jsonp',
contentType: "application/json;charset=utf-8",
success: function (data) {
alert("success");
alert(JSON.stringify(data));
},
error: function (x, y, z) {
alert(x + '\n' + y + '\n' + z);
}
});
When I run this code, nothing happens. Neither the success nor error block gets fired. After checking in the debug console of chrome, this is the error record:
GET http://localhost:55208/api/accesspanel?callback=jQuery18203847100134007633_…22,%22LoginId%22:%22tester%22,%22Password%22:%22tester%22}&_=1364916423737 405 (Method Not Allowed)
send jquery.min.js:2
p.extend.ajax jquery.min.js:2
(anonymous function)
I am, however, able to call my web api method successfully using C# code, which looks like this:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:55208/");
var request = new DeviceAccessRequest
{
RequestId = Guid.NewGuid().ToString(),
DeviceId = "ACU/B2/01/R1",
AccessType ="Unlock",
LoginId = "tester",
Password = "tester" ,
};
var response = client.PostAsJsonAsync("api/accesspanel", request).Result;
if (response.IsSuccessStatusCode)
{
var deviceAccessResponse = response.Content.ReadAsAsync<DeviceAccessResponse>().Result;
}
}
And this is my web api method:
[HttpPost]
public HttpResponseMessage PostDeviceControl(DeviceAccessRequest deviceAccessRequest)
{
var deviceAccessResponse = new DeviceAccessResponse(deviceAccessRequest.RequestId)
{
Status = "OK"
};
var response = Request.CreateResponse<DeviceAccessResponse>(HttpStatusCode.OK, deviceAccessResponse);
return response;
}
The reason you are seeing a GET request in your console is because you specified dataType: 'jsonp' which works only with GET requests (the reason for that is because jQuery translates this to a <script> tag pointing to the url you specified). If you want to be doing cross domain AJAX with other verbs than GET you cannot use JSONP. If you need to use other verbs (such as POST in your case) you have 2 options:
CORS. Take a look at the following video which illustrates how you could enable CORS on your Web API. Obviously for this to work your client side browser need tio support it
Server side bridge on your domain. The idea here is to have some server side script on the domain hosting your javascript code that will send the HTTP request to the API and return the response to the client. Then you will send a regular AJAX POST request to your own domain
Related
Here's a strange thing: I'm trying to send data to a Coldfusion web service via HttpWebRequest (also tried with HttpClient), and I always get a "login page" as response.
BUT, if I do the same thing as Ajax Post, it works.
BUT², if I put the content-type as "application/json" in the Ajax call, it returns the login page as well.
The web service manager says that the service doesn't need login, since we're using VPN to call it. But if I try to access the webservice URI via browser, it opens the login page.
The code in C#:
[EDIT] Created the object using JsonConvert
var request = HttpWebRequest.Create("http://url.cfc");
var obj= new
{
method = "MethodName",
data1 = "123456",
data2 = "aaa"
};
string postData = JsonConvert.SerializeObject(obj);
request.Method = "POST";
//request.ContentType = "application/json"; (not using!!!)
using (var writer = new StreamWriter(request .GetRequestStream()))
{
writer.Write(postData );
writer.Flush();
writer.Close();
}
var response = (HttpWebResponse)request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
And the code in JS:
$.ajax({
type: "post",
url: "http://url.cfc",
data: ({
method: "MethodName",
data1: 123456,
data2: "aaa" }),
dataType: "json",
success: function (result) {
console.log(result); },
error: function (result) {
console.log(result); },
});
});
Is there some substantial difference between Ajax call and HttpWebRequest call that can "block" the request using C#? Or maybe I'm failing in put some important data in HttpWebRequest's Header? Moreover: some issue in Coldfusion's web service authorization configuration?
You may need to move the methodname to the url instead of passing as a post param.
In Javascript in your AJAX call:
url: "http://url.cfc?method=MethodName"
and in C#:
var request = HttpWebRequest.Create("http://url.cfc?method=MethodName");
I am trying to create a demo project which uses .Net ASP.Net WebAPI and KnockoutJs as the front end. I have created the controller methods that listen for the /token post, and validates a user, and returns a token. This is done from an Ajax Post from the Knockout View Model.
This code works. However, when I get 200 back (Success) from the webApi, I then redirect to a controller method, decorated with a [Authorize]. And that's where I hit a 401 - not authorised.
Login()
{
var data = {
username : this.login.emailAddress(),
password : this.login.password(),
RememberMe: this.login.rememberMe(),
grant_type: "password"
}
return $.ajax({
type: "POST",
data: data,
dataType: "json",
url: "/token",
contentType: "application/json"
}).done((reply) => {
window.location.href = "/Home/AnotherThing";
});
}
I think the issue is - I get a response back from my /token (login) call, but do nothing with it. I'm not sure what to do with the token. I stupidly thought that OAuth would somehow put the token into my headers, and they would be there magically. I was wrong.
So, I've been looking for an example, and then best I can find is Here
But this means I am going to have a LOT of repeated code, on each view model
Extract:
function ViewModel() {
var self = this;
var tokenKey = 'accessToken';
var RefTokenKey = 'refreshToken';
self.result = ko.observable();
self.user = ko.observable();
self.token = ko.observable();
self.refreshToken = ko.observable();
function showError(jqXHR) {
self.result(jqXHR.status + ': ' + jqXHR.statusText);
}
self.callApi = function () {
self.result('');
var token = sessionStorage.getItem(tokenKey);
var headers = {};
if (token) {
headers.Authorization = 'Bearer ' + token;
}
$.ajax({
type: 'GET',
url: '/api/values',
headers: headers
}).done(function (data) {
self.result(data);
}).fail(showError);
}
self.callToken = function () {
self.result('');
var loginData = {
grant_type: 'password',
username: self.loginEmail(),
password: self.loginPassword()
};
$.ajax({
type: 'POST',
url: '/Token',
data: loginData
}).done(function (data) {
self.user(data.userName);
// Cache the access token in session storage.
sessionStorage.setItem(tokenKey, data.access_token);
var tkn = sessionStorage.getItem(tokenKey);
$("#tknKey").val(tkn);
}).fail(showError);
}
}
var app = new ViewModel();
ko.applyBindings(app);
This seems to be part of what I am missing:
sessionStorage.setItem(tokenKey, data.access_token);
var tkn = sessionStorage.getItem(tokenKey);
$("#tknKey").val(tkn);
Would I need every view model to have the code that then goes to the sessionStorage, and get the token?
So, this:
var token = sessionStorage.getItem(tokenKey);
var headers = {};
if (token) {
headers.Authorization = 'Bearer ' + token;
}
$.ajax({
type: 'GET',
url: '/api/values',
headers: headers
}).done(function (data) {
self.result(data);
}).fail(showError);
}
It seems like a lot of code.. Is this the right way to go?
Ok, so what you could do is attach the bearer token to each of your HTTP requests. I assume you're using jQuery there? If that's the case you could leverage the beforeSend config param.
Extract a reusable method such as this:
function onBeforeSend(xhr, settings) {
var token = sessionStorage.getItem(tokenKey);
if (token) {
xhr.setRequestHeader('Authorization', 'Bearer ' + token );
}
}
And then simply attach that method to each of your $.ajax calls that require the token, like this:
$.ajax({
type: 'GET',
url: '/api/values',
headers: headers,
beforeSend: onBeforeSend
}).done(function (data) {
self.result(data);
}).fail(showError);
The onBeforeSend function obviously needs to be accessible by your ajax call (I'm not a knockout guy so I don't know if it has any constructs such as services, but if not, you could namespace it for example to avoid making it a global function, but your code organization is up to you).
This way you'll only have to add the beforeSend: onBeforeSend bit to each request that requires auth and it will avoid unnecessary code duplication.
I am creating a backend service that writes new articles into a database and, upon completion, publishes changes to listening client in the frontend layer. My problem is that these BE updates are not triggering my subscribers in the frontend layer.
Directly testing the TaskHub (signalR backend asp.net hub) layer seems to be working, but the UI is not being updated. Relevant code for the various layers follows (I might be missing a thing or two - ask if something is amiss).
The order of things :
starting off with calling this on the client side:
var taskHub = $.connection.taskHub;
$.connection.hub.start();
Then i have this Knockout function to do the update :
self.AddQuickNews = function() {
taskHub.server.AddAndUpdateQuickNews(self.newContent()); /just a string
}
On ASP.NET backend i have the following method to handle this :
public void AddAndUpdateQuickNews(string newContent)
{
ArticleServices.AddQuickNews(newContent); //add a new record
var quicknews = ArticleServices.GetQuickNews(); // get all records
Clients.All.updateQuickNews(quicknews); // pass back to clients
}
Handling the result on the Client :
taskHub.client.UpdateQuickNews = function (quicknews) {
quicknewsmodel.quicknews(quicknews);
console.log("SignalR -> " + quicknews);
//console.log("Fra UpdateMatches: " + matches);
}
XHR call to test the backend
self.AddQuickNews = function () {
var url = 'api/MainPage/AddQuickNews';
var params = "?content=" + self.newContent();
$.ajax({
url: url + params,
type: 'GET',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
success: function (data) {
taskHub.server.updateQuickNews(data);
},
error: function () {
//alert("error");
}
});
}
The way I see it, its because when You declare the listener in your Client Side, you are using Upper Case in the first letter. This behavior is not allowed in SignalR. You must change your listener becomes :
taskHub.client.updateQuickNews = function (quicknews) {
quicknewsmodel.quicknews(quicknews);
console.log("SignalR -> " + quicknews);
//console.log("Fra UpdateMatches: " + matches);
}
taskHub.server.addAndUpdateQuickNews(self.newContent()); /just a string
Hope this would help you
I wanted to call notify specific client from server using signalR but it was not working. my code was executed successfully but client dose not receive any call from server.
However this is working for all client.
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ProcessStatusNotifyHub>();
hubContext.Clients.All.notify("Got it!");
But this is not working for specific client
[Updated Code]
Following code written in chat.cshtml
$(function () {
// Reference the auto-generated proxy for the hub.
var chat = $.connection.processStatusNotifyHub;//chatHub;
chat.client.notify = function (msg) {
alert(msg);
}
// Start the connection.
$.connection.hub.start().done(function () {
var myClientId = $.connection.hub.id;
console.log('connected: ' + myClientId);
$('#sendmessageToClient').click(function () {
//chat.server.send('imdadhusen', 'This is test text');
$.ajax({
url: '#Url.Action("Send", "PushNotification")',
type: 'POST',
data: { 'clientID': myClientId },
dataType: 'json',
success: function (result) {
alert(result.status);
}
});
});
});
});
Following code is written in Controller
[HttpPost]
public ActionResult Send(string clientID)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ProcessStatusNotifyHub>();
//hubContext.Clients.All.notify("Got it!");
hubContext.Clients.User(clientID).notify("Got it!");
responseResult result = new responseResult();
result.status = "OK";
result.message = "Notification sent successfully";
return Json(result, JsonRequestBehavior.AllowGet);
}
I have tried debug the code it is showing correct value of client id on .cstml or controlloer. e.g. clientid : 0fdf6cad-b9c1-409e-8eb7-0a57c1cfb3be
Could you please help me to send notification to specific client from server.
The operation hubContext.Clients.User(string) is expecting the User ID of the current HTTP request, not SignalR's client ID.
If you want to use client ID use this operation:
hubContext.Clients.Client( )
If you want to access it by the current user, then you can get current user ID with this
string UserID=User.Identity.GetUserId()
This is not an answer to your specific question, but instead a suggestion on your code. Why not have the client use the Signalr connection to call the server? Just define a method in the hub that a client can call. Then in that method use the HubCallerContext to get the id.
This page shows you how to call the server from the client:
http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-javascript-client
Then in the hub's method:
Clients.User(Context.ConnectionId).notify("Got it!");
I have a button on a website which sends an AJAX request to a .NET HTTPHandler. In the ProcessRequest method of this handler, it then took the query string parameters, and created a new WebRequest to another URL and appended the parameters onto it.
However, the AJAX request has been changed to use POST rather than GET (amongst other things).
This is what I need to do:
public void ProcessRequest(HttpContext context)
{
// Take POST data from request and create a new request
// with it to another URL
}
What makes it more complicated is that the POST data contains multiple arrays all called 'points' which hold latitude, longitude and altitude. So I can't use context.Request["points"] as that doesn't return an array of my points arrays. And I've tried using context.Request.Form.GetValues("points") but that doesn't seem to work, presumably because the data wasn't submitted as a form.
Edit:
AJAX request is:
$.ajax({
url: self.stationsAllFile,
data: data,
type: 'POST',
success: function (response) {
// store stations as kml string
self.stationsCurrent = response;
self.showStations(self.stationsCurrent, false);
self.listStations(self.stationsCurrent, finishedLoading);
var stations = $.parseXML(response),
$stations = $(stations),
$placemarks = $stations.find('Placemark');
$placemarks.each(function () {
var $placemark = $(this),
latLng = $placemark.find('coordinates').text(),
latLngSplit = latLng.split(','),
lng = latLngSplit[0],
lat = latLngSplit[1];
excludePoints.push(lng + ',' + lat + ',0.000000');
});
// do we need to go again?
if(endPoint < route.length) {
self.processRoute(route, endPoint + 1, excludePoints);
}
},
error: function () {
self.showError(self.language.errors.unknownError);
},
dataType: 'text'
});
Example POST data (pasted from Fiddler):
points%5B%5D=-2.2349300000000003%2C53.48073%2C0.000000&points%5B%5D=-2.26805%2C53.559020000000004%2C0.000000&type=route