I create a wcf rest service .as you can see the details of that :
[ServiceContract]
public interface INewsRepository
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/AddNews", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
bool Add(News entity);
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/DeleteNews/{id}", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
bool Remove(string id);
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "/EditNews", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
bool Edit(News entity);
}
I call this service in my client like this :
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Web;
using System.Web.Script.Serialization;
using CMSManagement.Domain.Entity;
using UPTO.UI.Infrastructure.Configuration;
using System.Text;
namespace UPTO.UI.Services
{
public class NewsRepository : IServiceRepository<News>
{
private WebClient ClientRequest;
private MemoryStream ms;
private DataContractJsonSerializer serializerToUplaod;
public string ServiceHostName = "http://cmsmanagement"+ ServiceServer.Address;
public NewsRepository()
{
ClientRequest = new WebClient();
ClientRequest.Headers["Content-type"] = "application/json";
ms= new MemoryStream();
}
public bool Add(News data)
{
serializerToUplaod = new DataContractJsonSerializer(typeof(News));
serializerToUplaod.WriteObject(ms, data);
string Result = System.Text.Encoding.UTF8.GetString(ClientRequest.UploadData(ServiceHostName+"/NewsRepository.svc/AddNews", "POST", ms.ToArray()));
return Result.ToLower().Equals("true");
}
public bool Delete(string id)
{
byte[] data = ClientRequest.UploadData(ServiceHostName+"/NewsRepository.svc/DeleteNews/"+id, "POST", ms.ToArray());
string Result = System.Text.Encoding.UTF8.GetString(data);
return Result.ToLower().Equals("true");
}
public bool Edit(News data)
{
serializerToUplaod = new DataContractJsonSerializer(typeof(News));
serializerToUplaod.WriteObject(ms, data);
string Result = System.Text.Encoding.UTF8.GetString(ClientRequest.UploadData("http://localhost:47026/NewsRepository.svc/EditNews", "POST", ms.ToArray()));
return Result.ToLower().Equals("true");
}
}
}
The problem is the edit part of my service :
When i call my edit method i get this error in my wcf log :
The incoming message has an unexpected message format 'Raw'. The expected message formats for the operation are 'Xml', 'Json'. This can be because a WebContentTypeMapper has not been configured on the binding. See the documentation of WebContentTypeMapper for more details.
Finally i found that i should move
ClientRequest.Headers["Content-type"] = "application/json";
from constructor to my Edit method .Why ?
Related
The contract
[OperationContract]
[WebGet(UriTemplate = "Filter/{paramName:paramValue}/"),
RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
string[] Filter(string paramNameAndparamValue);
The implementation
public string Filter(string paramNameAndparamValue)
{
string[] tmp = paramNameAndparamValue.split(':');
// do something ...
}
Is there any why to pass this restful method a parameter that will be use as json object and avoid of using the string.split?
You can try like below
Iservice.cs
[OperationContract]
[WebGet(UriTemplate = "Filter/{paramName}/{paramValue}"),
RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
string[] Filter(string paramName,string paramValue);
service .cs
public string[] Filter(string paramName,string paramValue);
{
//your code;
}
I have an Apex Class (a class in SalesForce) that calls out to a REST web service.
public class WebServiceCallout
{
#future (callout=true)
public static void sendNotification(String aStr)
{
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http http = new Http();
req.setEndpoint('http://xx.xxx.xxx.xx:41000/TestService/web/test');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(aStr); // I want to read this in the web service
try
{
res = http.send(req);
}
catch(System.CalloutException e)
{
System.debug('Callout error: '+ e);
System.debug(res.toString());
}
}
}
The REST web service (C#, WCF) looks like so:
public interface ITestService
{
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/test")]
string Test(string aStr);
}
The Test() method does primitive operations.
When I run
WebServiceCallout.sendNotification("a test message")
the POST gets to the web service but how can I read what was set in the body of the HttpRequest req that was set back in the sendNotification() method - req.setBody(aStr);?
That is, what should the parameter of string Test(string aStr); be?
Do I need to specify anything else such as any configs/attributes in my WebInvoke or the App.config (e.g. the binding)?
If you want to read the raw body of the incoming request, you should define the type of the parameter as a Stream, not string. The code below shows one way to implement your scenario, and the post at http://blogs.msdn.com/b/carlosfigueira/archive/2008/04/17/wcf-raw-programming-model-receiving-arbitrary-data.aspx has more information on this "raw" mode.
public class StackOverflow_25377059
{
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebInvoke(Method = "POST",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/test")]
string Test(Stream body);
}
public class Service : ITestService
{
public string Test(Stream body)
{
return new StreamReader(body).ReadToEnd();
}
}
class RawMapper : WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
return WebContentFormat.Raw;
}
}
public static void Test()
{
var baseAddress = "http://" + Environment.MachineName + ":8000/Service";
var host = new ServiceHost(typeof(Service), new Uri(baseAddress));
var binding = new WebHttpBinding { ContentTypeMapper = new RawMapper() };
host.AddServiceEndpoint(typeof(ITestService), binding, "").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");
var req = (HttpWebRequest)HttpWebRequest.Create(baseAddress + "/test");
req.Method = "POST";
req.ContentType = "application/json";
var reqStream = req.GetRequestStream();
var body = "a test message";
var bodyBytes = new UTF8Encoding(false).GetBytes(body);
reqStream.Write(bodyBytes, 0, bodyBytes.Length);
reqStream.Close();
var resp = (HttpWebResponse)req.GetResponse();
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
foreach (var header in resp.Headers.AllKeys)
{
Console.WriteLine("{0}: {1}", header, resp.Headers[header]);
}
Console.WriteLine();
Console.WriteLine(new StreamReader(resp.GetResponseStream()).ReadToEnd());
Console.WriteLine();
}
}
By the way, your incoming request is not technically correct - you say (via Content-Type) that you're sending JSON, but the request body (a test message) is not a valid JSON string (it should be wrapped in quotes - "a test message" to be a JSON string instead).
I have a RESTful WCF Service function, however it is not serializing properly via the RestSharp Client.
[ServiceContract]
public interface IRestDragon
{
[OperationContract(Name = "getconfig")]
[WebInvoke(Method = "GET", UriTemplate = "getconfig/{id}", BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json)]
DragonConfig GetConfig(string id);
}
public class RestDragon : IRestDragon
{
public DragonConfig GetConfig(string id)
{
var config = new DragonConfig {Name = "Test", Data = "DAtaaaaaa"};
return config;
}
}
And here is how I consume the service:
static void Main(string[] args)
{
Console.ReadLine();
var client = new RestClient("http://localhost:5463/RESTDragon.svc");
client.AddDefaultHeader("ContentType", "application/json");
var request = new RestRequest("/getconfig/11123") {Method = Method.GET, RequestFormat = DataFormat.Json};
var response = client.Execute<DragonConfig>(request);
Console.WriteLine("Response: " + response.Content);
Console.ReadLine();
}
However it is returning:
Response: {"getconfigResult":{"Data":"DAtaaaaaa","Name":"Test"}}
I am unable to access the De-Serialized Data via response.Data.*. It comes back as null, and the data is shown in Content however, in the JSON Format, with the strange getconfigResult identifier.
Setting BodyStyle = WebMessageBodyStyle.Bare corrected the issue.
You need to access response.Data instead of response.Content to get the deserialized object.
I am using WCF .NET 4.0 hosting inside WebServiceHost. Normaly all works until i use my own defined class array inside class.
Server side function
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, UriTemplate = "foo")]
[OperationContract]
void Foo(FooQuery query);
Classes
[DataContract(Namespace="")]
public class FooQuery
{
[DataMember]
public MyFoo[] FooArray;
}
[DataContract(Namespace = "")]
public class MyFoo
{
[DataMember]
public string[] items;
}
Client side:
//create object
FooQuery myOriginalFoo = new FooQuery();
MyFoo _myFoo = new MyFoo();
_myFoo.items = new string[] { "one", "two" };
myOriginalFoo.FooArray = new MyFoo[] { _myFoo };
//serialize
var json = new JavaScriptSerializer().Serialize(myOriginalFoo);
string _text = json.ToString();
//output:
// {"FooArray":[{"items":["one","two"]}]}
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://localhost:2213/foo");
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
streamWriter.Write(_text);
streamWriter.Flush();
streamWriter.Close();
}
//here server give back: 400 Bad Request.
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
I have also tried manipulate my class with System.Runtime.Serialization.Json.DataContractJsonSerializer - all fine until i send to server and webinvoke give back error 400. Why webInvoke don't know how deserialize it or there any other error?
I found magic attribute called CollectionDataContract what makes a trick.
Adding new collection class:
[CollectionDataContract(Namespace = "")]
public class MyFooCollection : List<MyFoo>
{
}
Changed Query class
[DataContract(Namespace="")]
public class FooQuery
{
[DataMember]
public /*MyFoo[]*/MyFooCollection FooArray;
}
client code change:
MyFooCollection _collection = new MyFooCollection();
_collection.Add(_myFoo);
myOriginalFoo.FooArray = _collection; //new MyFoo[] { _myFoo };
All variables serialized now right :) yeah..takes many hours to figure out.
Since it is a web request, try GET instead:
[WebGet(ResponseFormat = WebMessageFormat.Json)]
Set the WebMessageBodyStyle to wrappedRequest as below for the WCF service to expect a wrapped JSON string. by default it expects a plain String.
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
I have quite a few RESTful (GET and POST) methods implemented in WCF 4.0. All these work over SSL.
An example of some of the methods:
[OperationContract]
[WebInvoke(UriTemplate = "Login?", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
LoginResponse Login(LoginRequest request);
[OperationContract]
[WebInvoke(UriTemplate = "UpdateDetails?", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
UpdateUserDetailResponse UpdateDetails(UpdateUserDetailRequest request);
[OperationContract]
[WebInvoke(UriTemplate = "GetDetails?", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
UserDetailResponse GetDetails(UserDetailRequest request);
I have looked through so many blogs and forums and I still cannot find something that meets
my requirements. I need to implement basic authentication on some of the methods but not all. If you look at the examples above I require a username and password to be sent through for the UpdateDetails and GetDetails method, but not for the Login method. The username and password is then authenticated against a database. Is it possible to do something like this?
As a side note: these REST methods are called by many different mobile devices.
I have looked at the following sites and they all implement basic authentication over REST but they cover all the methods mentioned above.
http://msdn.microsoft.com/en-us/library/aa702565.aspx
Adding basic HTTP auth to a WCF REST service
http://custombasicauth.codeplex.com/ (links at the bottom don't work
anymore)
Is it possible to do what I want to do?
I created a BasicAuthenticationInvoker class which you attribute on the methods you would like to authenticate as follows:
[OperationContract]
[BasicAuthenticationInvoker] // this is the auth attribute!
[WebInvoke(UriTemplate = "QuickQuote?", Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
QuickQuoteResponse QuickQuote(QuickQuoteRequest request);
The actual class looks as follows:
public class BasicAuthenticationInvoker : Attribute, IOperationBehavior, IOperationInvoker
{
#region Private Fields
private IOperationInvoker _invoker;
#endregion
#region IOperationBehavior Members
public void ApplyDispatchBehavior(OperationDescription operationDescription,
DispatchOperation dispatchOperation)
{
_invoker = dispatchOperation.Invoker;
dispatchOperation.Invoker = this;
}
public void ApplyClientBehavior(OperationDescription operationDescription,
ClientOperation clientOperation)
{
}
public void AddBindingParameters(OperationDescription operationDescription,
BindingParameterCollection bindingParameters)
{
}
public void Validate(OperationDescription operationDescription)
{
}
#endregion
#region IOperationInvoker Members
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
if (Authenticate("Client Name here"))
return _invoker.Invoke(instance, inputs, out outputs);
else
{
outputs = null;
return null;
}
}
public object[] AllocateInputs()
{
return _invoker.AllocateInputs();
}
public IAsyncResult InvokeBegin(object instance, object[] inputs,
AsyncCallback callback, object state)
{
throw new NotSupportedException();
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
throw new NotSupportedException();
}
public bool IsSynchronous
{
get
{
return true;
}
}
#endregion
private bool Authenticate(string realm)
{
string[] credentials = GetCredentials(WebOperationContext.Current.IncomingRequest.Headers);
if (credentials != null && credentials.Length == 2)
{
// do auth here
var username = credentials[0];
var password = credentials[1];
// validate the username and password against whatever auth logic you have
return true; // if successful
}
WebOperationContext.Current.OutgoingResponse.Headers["WWW-Authenticate"] = string.Format("Basic realm=\"{0}\"", realm);
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
return false;
}
private string[] GetCredentials(WebHeaderCollection headers)
{
string credentials = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
if (credentials != null)
credentials = credentials.Trim();
if (!string.IsNullOrEmpty(credentials))
{
try
{
string[] credentialParts = credentials.Split(new[] {' '});
if (credentialParts.Length == 2 && credentialParts[0].Equals("basic", StringComparison.OrdinalIgnoreCase))
{
credentials = Encoding.ASCII.GetString(Convert.FromBase64String(credentialParts[1]));
credentialParts = credentials.Split(new[] {':'});
if (credentialParts.Length == 2)
return credentialParts;
}
}
catch
{
}
}
return null;
}
}