I have written a really simple wcf rest service which seems to work fine when I make requests to it through fiddler but I cannot get it to work when calling it from JQuery.
Service:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "customers/{regionId}",
ResponseFormat = WebMessageFormat.Json
)]
Customer[] GetCustomers(String regionId);
}
[DataContract]
public class Customer
{
[DataMember]
public Guid Id { get; private set; }
[DataMember]
public String Name { get; private set; }
public Customer(Guid id, String name)
{
Id = id;
Name = name;
}
}
public class Service1 : IService1
{
public Customer[] GetCustomers(String regionId)
{
return new[]
{
new Customer(Guid.NewGuid(), "john"),
new Customer(Guid.NewGuid(), "pete"),
new Customer(Guid.NewGuid(), "ralph")
};
}
}
I can make requests to this service via fiddler and it returns the expected json. However, when I try and call it with JQuery ajax via the firebug console it always fails. Here is the call:
$.ajax({
type: "POST",
data: "{}",
url: "http://127.0.0.1:8081/json/customers/1",
contentType: "application/json; charset=utf-8",
dataType: "json",
success:function(res)
{
alert('success');
},
error: function(xhr)
{
alert('failed: '+xhr.responseText);
}
});
I always get the failed alert and the responseText is always blank. Any ideas would be greatly appreciated.
When you say "via Fiddler" do you mean "using Fiddler's request builder" or do you mean "with Fiddler running?"
Question: What URL is your website running on? You cannot generally make XHR requests to different servers (or ports, in FF) using XHR.
Related
I followed this wonderful post
SharePoint 2013: Create a Custom WCF REST Service Hosted in SharePoint
Now when I call a "GET" request everything work as expected. My problem with "POST" request, it always return 404 not found error response!
[OperationContract]
[WebInvoke( Method = "POST",
UriTemplate = "Approve",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json)]
bool Approve(PendingMeRequest pendingRequest);
Approve implementation is simple and return true
public bool Approve(PendingMeRequest pendingRequest)
{
return true;
//SPUser currentuser = SPContext.Current.Web.CurrentUser;
//return Take_approval_action(pendingRequest, "approve", currentuser.LoginName);
}
PendingMeRequest Model is simple with 3 string fields
public class PendingMeRequest
{
public string Key { set; get; }
public string RequestedBy { set; get; }
public string RequestTitle { set; get; }
}
When I call this method with jquery I get 404 not found error
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + '/_vti_bin/UnifiedWorklistService.svc/Approve/',
method: 'POST',
dataType: 'JSON',
data: JSON.stringify({pendingRequest: obj}),
contentType: 'application/json; charset=utf-8',
success: function (data) {
alert(data);
}
});
I tried to call it from POSTMAN but I got the same error
I think i miss something with Sharepoint configuration but I cannot figure it out
UPDATE:
After few hours of searching finally I found the problem, it is the trailing slash in the ajax call /_vti_bin/UnifiedWorklistService.svc/Approve/ should be removed!!
I cannot believe the time I wasted to figure it out...
I have the following WCF REST web service interface :
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(
UriTemplate = "foobar/",
Method = "POST",
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare),
]
void PostFoobar(Foobar foobar);
}
The implementation :
public class Service : IService
{
public void PostFoobar(Foobar foobar)
{
try
{
Log.MonitoringLogger.Info("foo" + foobar.foo);
Log.MonitoringLogger.Info("bar" + foobar.bar);
}
catch (Exception ex)
{
if (Log.ExceptionLogger.IsErrorEnabled) Log.ExceptionLogger.Error(ex);
}
}
}
My Foobar class:
[DataContract]
public class Foobar
{
[DataMember]
public string foo { get; set; }
[DataMember]
public string bar { get; set; }
}
But it seems my Foobar object in parameter is always NULL when I call it from a client. I tried to implement the following method :
void PostFoobar(String foo, String bar);
And this time it works ! So my question is : Why, when I send a JSON Foobar object, it does not deserialized it ?
Here is a capture made with Wireshark to see if my client sent really the JSON object I expected :
So it seems the client send the object as expected : { "foo": "foo text", "bar": "bar text" }
I found something interesting: It works with the chrome plugin Simple REST client. I sent the json data:
{ "foo": "foo text", "bar" : "bar text" }
and it has worked.
So I have analysed the logs of my IIS server, and I found a difference : No element is catched from my client. The following object (in the logs)
<root type="object" xmlns="">
<foo type="string">foo chrome</foo>
<bar type="string">bar chrome</bar>
</root>
Appears only when I call my service from the chrome client.
I am trying to get the knockout.js library and knockout.mapping.js library to work in my MVC4 application. I am using a controller to generate the view. The razor view is converting the model to a JSON string and then I am using the mapping plugin to get my JSON model in my MV.
That part all works fine. If I add some values to the model in the controller they are showing up in my view. The problem I am having is sending back to me WebApi controller. Once I get it there It will not convert back into my serializable model. Here is what I have:
<script type="text/javascript" src="/Scripts/knockout-2.1.0.debug.js"></script>
<script type="text/javascript" src="/Scripts/knockout.mapping-latest.debug.js"></script>
<script type="text/javascript">
function SearchModel() {
var self = this;
var baseUri = '/Api/searchsubscriber/FindSubscriber';
self.search = function (formElement) {
debugger;
var myJSONString = JSON.stringify(ko.mapping.toJS(formElement));
alert(myJSONString);
$.ajax({
type: "POST",
url: baseUri,
data: myJSONString
}).done(updateSearchResults);
};
updateSearchResults = function (data) {
debugger;
var jsonString = JSON.stringify(data);
alert(jsonString);
};
};
$(function () {
debugger;
//in my actualy view it looks like this
//var jsonModel = '#Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(this.Model))';
var jsonModel = '{"SubscriberNum":null,"PersonCode":null,"ClientCode":null,"LastName":"TEST","FirstName":"H","MI":null,"DOB":null,"StartDt":null,"EndDt":null,"GroupNum":null}';
var mvcModel = ko.mapping.fromJSON(jsonModel);
var myViewModel = new SearchModel();
var g = ko.mapping.fromJS(myViewModel, mvcModel);
ko.applyBindings(g);
});
</script><code>
This is my model
[Serializable]
public class SearchSubscriberFields //: SearchSubscriberResults
{
//public List<SearchSubscriberResults> Results { get; set; }
public string SubscriberNum { get; set; }
public string PersonCode { get; set; }
public string ClientCode { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string MI { get; set; }
public string DOB { get; set; }
public string StartDt { get; set; }
public string EndDt { get; set; }
public string GroupNum { get; set; }
}
This is my ApiController
[Authorize]
public class SearchSubscriberController : ApiController
{
MyService _service = new MyService();
[HttpPost]
public SearchSubscriberFields FindSubscriber(SearchSubscriberFields search)
{
if (search != null)
{
SearchSubscribersRequest request = new SearchSubscribersRequest();
request.Credentials = new Credentials() { Username = User.Identity.Name };
request.SubscriberNum = Utility.FormatString(search.SubscriberNum).ToUpper();
request.PersonCode = Utility.FormatString(search.PersonCode).ToUpper();
request.ClientCode = Utility.FormatString(search.ClientCode).ToUpper();
request.LastName = Utility.FormatString(search.LastName).ToUpper();
request.FirstName = Utility.FormatString(search.FirstName).ToUpper();
request.MI = Utility.FormatString(search.MI).ToUpper();
SearchSubscribersResponse response = _service.SearchSubscribers(request);
if (response.Errors.Count < 1)
{
return search;
}
}
return search;
}
}
So, from every example that I have looked at this should work. If I change the object in the api controller to FindSubscriber(JObject search) I can see that I am getting a JSON string. Fiddler show it is sending a JSON string.
From Fiddler:
POST http://localhost:60248/Api/searchsubscriber/FindSubscriber HTTP/1.1
Accept: */*
Content-Type: application/x-www-form-urlencoded
X-Requested-With: XMLHttpRequest
Referer: http://localhost:60248/Subscriber/Search
Accept-Language: en-US,es-DO;q=0.5
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
Host: localhost:60248
Content-Length: 157
Connection: Keep-Alive
Pragma: no-cache
{"SubscriberNum":null,"PersonCode":null,"ClientCode":null,"LastName":"TEST","FirstName":"H","MI":null,"DOB":null,"StartDt":null,"EndDt":null,"GroupNum":null}
I just am not getting a result in my controller that I can get back into a SearchSubscriberFields object.
Any ideas are greatly appreciated.
You should use ko.mapping.toJSON also you need to configure jQuery.Ajax correctly, now it will post it like a html form, your MVC Controller wont understand that.
Something like this should work
MyApp.utils = {
post: function (url, data, success) {
$.ajax({
url: url,
type: "POST",
dataType: "JSON",
contentType: "application/json; charset=UTF-8",
data: ko.mapping.toJSON(data),
success: success
});
}
};
I found that my problem was that my server model was had the [Serializable] attribute so that when I serialized it in my razor view it was also serializing the backing fields. That should have been obvious but I missed it. So once I posted back my model it was also posting back those binding fields and thus making it impossible to mapped to my model.
I added this to the Global.asmx
//This sets the JSON serializer to ignore the backing fields
JsonSerializerSettings jSettings = new Newtonsoft.Json.JsonSerializerSettings();
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = jSettings;
I'm trying to POST JSON data to a Web Api method but the JSON data is not binding to the model.
Here's my model:
[DataContract]
public class RegisterDataModel
{
[DataMember(IsRequired = true)]
public String SiteKey { get; set; }
[DataMember(IsRequired = true)]
public String UserId { get; set; }
[DataMember(IsRequired = true)]
public String UserName { get; set; }
}
Here's my Web Api action:
public class RegisterController : ApiController
{
public Guid Post([ModelBinder] RegisterDataModel registerDataModel)
{
if (!ModelState.IsValid)
{
throw new ModelStateApiException(ModelState);
}
var userProfileDataContract = userProfileBusinessLibrary.GetNewOne();
userProfileDataContract.UserId = registerDataModel.UserId;
userProfileDataContract.UserName = registerDataModel.UserName;
var userKey = userProfileBusinessLibrary.Register(registerDataModel.SiteKey, userProfileDataContract);
return userKey;
}
}
Before I added [ModelBinder], registerDataModel was null. After adding [ModelBinder], registerDataModel is a RegisterDataModel instance, but all of the property values are null.
Here's my Request via Fiddler:
http://local.testwebsite.com/api/register
Request Headers:
User-Agent: Fiddler
Host: local.testwebsite.com
Content-Length: 89
Content-Type: application/json; charset=utf-8:
Request Body:
{
"SiteKey":"qwerty",
"UserId": "12345qwerty",
"UserName":"john q"
}
What am I missing to make my post data bind to the RegisterDataModel properties? Thanks for your help.
Not related to the OP's problem, but the title of the question
led me here when I used (public) fields instead of properties
in the Model class (i.e. no {get; set;}).
It turned out that this also causes the binding to fail.
Maybe helps someone.
How are you creating the JSON request? Through Fiddler request builder? Try just the following in the request body.
{
"SiteKey":"qwerty",
"UserId": "12345qwerty",
"UserName":"john q"
}
I'm guessing 'Request Body:' is also part of your request body. Remove that and check.
In my case, app's requests are passed through a middleware called "API Manager" for authentication / authorization before forwarding to my .NET Web API. POST parameter isn't binded because, for some reason I'm no idea why, the "Content-Length" is emitted from the Headers.The reason is because, the default JsonMediaTypeFormatter always check requests' Content-Length before doing model binding, and if the Content-Length is not presented it will set the parameter to NULL.
I have a restful service based on WCF like below:
(The FeedbackInfo class has only one enum member - ServiceCode.)
[OperationContract]
[WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public List<FeedbackInfo> GetFeedbackInfoList(ServiceCode code)
{
return ALLDAO.GetFeedbackInfoList(code);
}
[OperationContract]
[WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.WrappedRequest, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
public int? CreateFeedback(FeedbackInfo feedback)
{
return ALLDAO.CreateFeedback(feedback);
}
I will use jquery ajax to invoke these two method like below:
$.ajax({
type: "GET",
url: "/Service/ALL.svc/GetFeedbackInfoList",
datatype: "text/json",
data: { "code": "Info"},
});
var feedbackInfo = { feedback: {
ServiceCode: "Info"
}};
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/Service/ALL.svc/CreateFeedback",
datatype: "text/json",
data: JSON.stringify(feedbackInfo),
});
The first calling will be excuted succesfully, whereas the second one give me an error: The value "Info" cannot be parsed as the type 'Int64'. I'm wondering why the same one enum can not be parsed in the second calling? Just because of the enum type being used as a member of class?
EDIT:
The FeedbackInfo and ServiceCode are looked like below:
public class FeedbackInfo
{
public int ID { get; set; }
public string Title { get; set; }
public ServiceCode ServiceCode { get; set; }
}
[DataContract]
public enum ServiceCode
{
[EnumMember]
Info,
[EnumMember]
Error
}
I have put together a better solution that uses the Newtonsoft.Json library. It fixes the enum issue and also makes the error handling much better. It's quite a lot of code, so you can find it on GitHub here: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs
You have to add some entries to your Web.config to get it to work, you can see an example file here:
https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config
Enums are serialized as integers, so you'd need to use ServiceCode: 1 (or whatever), or alternatively, add a custom property in the FeedbackInfo class to deserialize the enum from a given string value. Ie, something like this:
public string ServiceCode {
get {
return ServiceCodeEnum.ToString();
}
set {
MyEnum enumVal;
if (Enum.TryParse<MyEnum>(value, out enumVal))
ServiceCodeEnum = enumVal;
else
ServiceCodeEnum = default(MyEnum);
}
}
private MyEnum ServiceCodeEnum { get; set; }