Custom WCF REST Service Hosted in SharePoint 2013 - wcf

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...

Related

POST two arrays in JSON to ASP.NET Core MVC 6

foreword: I have an application with MVC 4 and .NET v4.6.1 and is working like a charm. One editor template sends an AJAX request to the controller to get a list of things:
function showEffectiveRights(e) {
$.ajax({
contentType: "application/json",
data: JSON.stringify({
privileges: $("#AssignedPrivileges").getKendoMultiSelect().value(),
privilegeGroups: $("#AssignedGroups").getKendoMultiSelect().value()
}),
dataType: "json",
success: function (data) {
// Stuff
},
error: function (data) {
showResponseMessages(data);
},
type: "POST",
url: '#Url.Action("EffectiveRights", "User")'
});
}
The controller looks like this:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult EffectiveRights([DataSourceRequest] DataSourceRequest request, Guid[] privileges, Guid[] privilegeGroups)
{
// Stuff
}
The payload of the POST request is as follows:
{"privileges":["d72c1162-0c3d-e611-953e-00155d9e5c08","e32c1162-0c3d-e611-953e-00155d9e5c08"],"privilegeGroups":["bb2c1162-0c3d-e611-953e-00155d9e5c08"]}
Whenever the AJAX request is sent, the variables privileges and privilegeGroups have the information from the client. Yay!
Let's get to the problem. My new application should use MVC 6 and .NET Core. According to NuGet, every library I use is up-to-date. The JavaScript is exactly the same. The controller only got another attribute (it doesn't work with AcceptVerbs either):
[HttpPost]
public JsonResult EffectiveRights([DataSourceRequest] DataSourceRequest request, Guid[] privileges, Guid[] privilegeGroups)
{
// Stuff
}
The payload and the request headers of both applications are identical. But for whatever reason, the variables privileges and privilegeGroups never contain any elements.
I've tried to add [FromBody] but this did not help either.
https://stackoverflow.com/a/38493849/4944034 had a similar problem. But he sent only one object, I have two. And the suggest solution did not work for me.
What do I have to change to make this work?
EDIT
I have something similar on the very same page. The data are submitted by a component from Kendo. The content type is application/x-www-form-urlencoded and the payload looks like this:
profileId=8f96c1bb-5c68-4071-a423-ab2a7ba8234f&selectedPrivileges=1410335f-9e35-4454-a7e9-77c7d24bf5df&selectedGroups=60d0ec60-c820-47d7-acea-f4d57f221e5c
The controller is very well able to receive those two arrays:
[HttpPost]
public JsonResult PrivilegeListForUser([DataSourceRequest]DataSourceRequest request, Guid[] selectedPrivileges, Guid[] selectedGroups)
{
// Stuff
}
May this be due to the DefaultContractResolver I am setting in Startup.cs?
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
services.AddKendo();
}
Best regards,
Carsten
PS: You might have noticed that I am using Telerik's Kendo. Yes, I am using different versions in both applications. But I do not see, how Kendo should interfere here.
Create a model class and use it:
public class InputModel
{
public Guid[] privileges { get; set; }
public Guid[] privilegeGroups { get; set; }
}
[HttpPost]
public JsonResult EffectiveRights([DataSourceRequest] DataSourceRequest request, [FromBody]InputModel model)
{
// Stuff
}

WCF RESTful Service - HTTP POST Request

I developed a WCF Service with the following post method:
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "/InsertBearingData")]
bool InsertBearingData(String JSONString);
I am using Fiddler to formulate an HTTP POST Request for this method but, it is returning Status Code - 400 Bad Request. This is the request formulated:
Request Header:
Host: localhost:21468
Content-Length: 96
Content-Type: application/json
Request Body:
[{"start time":"29-03-2013 11:20:11.340","direction":"SW","end time":"29-03-2013 11:20:14.770"}]
Can you please tell me how to formulate a good request in order to get a succesful response?
There are a few issues in your code:
The data type of the parameter is string, but you're passing a JSON array to it; a string parameter requires a JSON string to be passed.
The body style of the operation is set to Wrapped, which means that the parameter should be wrapped in an object whose key is the parameter name, something like {"JSONString":<the actual parameter value>}
To receive a request like the one you're sending, you need to have an operation like the following:
[ServiceContract]
public interface ITest
{
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/InsertBearingData")]
bool InsertBearingData(MyType[] param);
}
[DataContract]
public class MyType
{
[DataMember(Name = "start time")]
public string StartTime { get; set; }
[DataMember(Name = "end time")]
public string EndTime { get; set; }
[DataMember(Name = "direction")]
public string Direction { get; set; }
}

knockout.mapping as JSON back to Api controller not converting back to object

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;

WCF Restful service parse enum within json cause error

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; }

Calling wcf rest service from jquery doesn't work

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.