Simple WCF POST with Uri Template - wcf

I thought this would be incredibly simple, but I must be missing something. I am trying to make a simple WCF POST request in conjunction with a UriTemplate. I have read numerous examples where people use a stream paramater as the last paramater, and this is supposed to pick up the POST body. I can only get this to work if the stream is the only paramater.
I've gone back to basics with a simple Hello World service.
Here is my code on the client
static string Test()
{
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://localhost:1884/MyAPI/Service.svc/HelloWorld");
req.Method = "POST";
req.ContentType = "text/plain";
Stream reqStream = req.GetRequestStream();
byte[] fileToSend = System.Text.UTF8Encoding.UTF8.GetBytes("sometext");
reqStream.Write(fileToSend, 0, fileToSend.Length);
reqStream.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
var sr = new StreamReader(resp.GetResponseStream());
return sr.ReadToEnd();
}
And this is the code on the service
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "HelloWorld")]
Stream HelloWorld(Stream content);
}
public Stream HelloWorld(Stream content)
{
var sr = new StreamReader(content);
string text = sr.ReadToEnd();
return new System.IO.MemoryStream(Encoding.UTF8.GetBytes("Hello World! " + text));
}
This all works fine. Then I make this change:
[ServiceContract]
public interface IService
{
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "HelloWorld/test/{test}")]
Stream HelloWorld(string test, Stream content);
}
public Stream HelloWorld(string test, Stream content)
{
var sr = new StreamReader(content);
string text = sr.ReadToEnd();
return new System.IO.MemoryStream(Encoding.UTF8.GetBytes("Hello World! " + text + test));
}
And change the client code to hit HelloWorld/test/sometext
I get a 500 Internal Server Error. I've tried about 10 different variations including using a ?key=value type UriTemplate, returning strings instead of streams etc, and no luck.
Feels like I'm missing some tiny little thing that is going to make this work, as I have seen countless examples of exactly this all over the web. Theirs works, mine doesn't.
Any ideas?

I am not sure what went wrong, but after trying everything, I resolved this by creating a new project and copying all the code over. Never worked out what the differences were, maybe something got corrupted
Edit: in the end we discovered we had to specify WebServiceHostFactory in the Service.svc. This was there by default in the new project

When streaming, the Stream must be the only parameter: http://msdn.microsoft.com/en-us/library/ms789010.aspx
You may be able to use message headers: Add filename and length parameter to WCF stream when Transfermode = Stream

You can use new single file WCF model to configure and adjust endpoint behaviour. I combined your contract and service class into one file to show you how to do this.
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text;
namespace StreamService
{
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class MergedEndpoint
{
[WebGet(RequestFormat = WebMessageFormat.Xml, UriTemplate = "Data/{someid}",
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public string GetData(string someid)
{
return string.Format("You entered: {0}", someid);
}
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, UriTemplate = "HelloWorld",
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public Stream HelloWorld1(Stream content)
{
var sr = new StreamReader(content);
string text = sr.ReadToEnd();
return new System.IO.MemoryStream(Encoding.UTF8.GetBytes("Hello World from single file! " + text));
}
[WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, UriTemplate = "HelloWorld/test/{testparam}",
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public Stream HelloWorld2(string testparam, Stream content)
{
var sr = new StreamReader(content);
string text = sr.ReadToEnd();
return new System.IO.MemoryStream(Encoding.UTF8.GetBytes("Hello World from single file! " + testparam+ text));
}
}
}
Input parameters need to be same name as method params. Their type is also string. You need to do the conversion if you want different input param.
You need to create WCf project and add Global.asax file with routing info for this file. You may need to add reference to System.ServiceModel.Activation to setup routing.
Example:
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes();
}
private void RegisterRoutes()
{
RouteTable.Routes.Add(new ServiceRoute("MergedEndpoint", new WebServiceHostFactory(), typeof(MergedEndpoint)));
}
Your Client code has one change to content type.
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://localhost:55166/MergedEndpoint/HelloWorld/test/234");
req.Method = "POST";
//req.ContentType = "text/plain";
req.MediaType = "HTTP/1.1";
req.ContentType = "application/json; charset=utf-8";
Stream reqStream = req.GetRequestStream();
byte[] fileToSend = System.Text.UTF8Encoding.UTF8.GetBytes("sometext");
reqStream.Write(fileToSend, 0, fileToSend.Length);
reqStream.Close();
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
var sr = new StreamReader(resp.GetResponseStream());
string outp = sr.ReadToEnd();
Console.WriteLine("Response:"+outp);
You can still read the raw content even if it is set to Json type.

Related

Dealing with WebMessageBodyStyle.Wrapped when making a GET request with RestSharp

I have a simple object:
public class Country
{
public int Id { get; set; }
public string Name { get; set; }
}
that is being returned from a WCF service with the following Operational Contract:
[OperationContract]
[WebInvoke(Method = "GET", BodyStyle = WebMessageBodyStyle.Wrapped, UriTemplate = "/Countries/{aId}")]
[return: MessageParameter(Name = "Country")]
Country Country(string aId);
Basically the object is wrapped inside a Country:{} block which is what I want. On the client side I do the following:
private void RequestPrepare(out RestClient aRestClient, out RestRequest aRestRequest, string aRequestUri, Method aRequestMethod = Method.GET)
{
aRestClient = new RestClient(BASE_URL);
aRestRequest = new RestRequest(aRequestUri, aRequestMethod);
aRestRequest.AddHeader("Accept", "application/json");
}
and call like so:
RestClient restClient;
RestRequest restRequest;
RequestPrepare(out restClient, out restRequest, "Countries/{aId}");
restRequest.AddUrlSegment("aId", "1"); // replaces matching token in request.Resource
var restResponse = restClient.Execute<Country>(restRequest);
List<Country> listCountry = new List<Country>();
listCountry.Add(new Country {Id = restResponse.Data.Id, Name = restResponse.Data.Name});
return listCountry;
the JSON response is:
Content = "{\"Country\":{\"Id\":1,\"Name\":\"Australia\"}}"
so basically restResponse.Data.Id and restResponse.Data.Name do not contain valid data because of the "Country" wrapping.
1) So how do I configure RestSharp to deal with the Country{} wrap?
2) As a secondary question how do I make the Restsharp call so that it will automatically deserialise List

WCF to WCF communication 401, HttpClient

I have a WCF REST service that needs to communicate with another WCF REST service.
There are three websites:
Default Web Site
Website1
Website2
If I set up both services in Default Web Site and connect to the other (using HttpClient) using the URI http://localhost/service then everything is okay.
The desired set-up is to move these two services to separate websites and rather than using the URI http://localhost/service, accessing the service via http://website1.domain.com/service still using HttpClient.
I received the exception:
System.ArgumentOutOfRangeException: Unauthorized (401) is not one of
the following: OK (200), Created (201), Accepted (202),
NonAuthoritativeInformation (203), NoContent (204), ResetContent
(205), PartialContent (206)
I can see this is a 401, but what is going on here?
Thanks
I think this is related to your setup for webservice. It is best if you just create GET,POST,Put,DELETE heartbeat calls for new services and then check those from fiddler. If you get 401, it may mean your app pool identity could not access something.
Steps to fix that:
Give user read/write/modify/execute/..similar rights at your WCF publish folder
Create app pool for this site in .net 4 integrated
Set this user to application pool identity, enable anonymous mode
Enable PUt,Delete verbs as well
Part of a heartbeat class in your service to test calls:
[DataContract]
public class StatusInfo
{
[DataMember]
public string MachineName { get; set; }
[DataMember]
public string IpAddress{ get; set; }
[DataMember]
public string Methodname { get; set; }
public override string ToString()
{
return "Machinename:" + MachineName + " ;IP:" + IpAddress + "; Method:" + Methodname;
}
}
private void ResolveStatus(StatusInfo statusInfo,string methodname)
{
try
{
var context = System.ServiceModel.OperationContext.Current;
RemoteEndpointMessageProperty property =
(RemoteEndpointMessageProperty)
context.IncomingMessageProperties[RemoteEndpointMessageProperty.Name];
statusInfo.IpAddress = property.Address;
statusInfo.MachineName = Environment.MachineName;
statusInfo.Methodname = methodname;
}catch(Exception ex)
{
}
}
/// <summary>
/// create task
/// </summary>
/// <param name="taskwrapped"></param>
[WebInvoke(Method = "POST", UriTemplate = "", RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public StatusInfo postcall()
{
StatusInfo statusInfo = new StatusInfo();
logger.Trace(Tagname + "postcall");
ResolveStatus(statusInfo, "POST");
return statusInfo;
}
/// <summary>
/// edit task
/// </summary>
[WebInvoke(Method = "PUT", UriTemplate = "", RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public StatusInfo Edit()
{
StatusInfo statusInfo = new StatusInfo();
logger.Trace(Tagname + "Edit");
ResolveStatus(statusInfo, "PUT");
return statusInfo;
}
//delete request with taskid
[WebInvoke(Method = "DELETE", UriTemplate = "", RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public StatusInfo DeleteCall()
{
StatusInfo statusInfo = new StatusInfo();
logger.Trace(Tagname + "Edit");
ResolveStatus(statusInfo, "DELETE");
return statusInfo;
}
//delete request with taskid
[WebInvoke(Method = "DELETE", UriTemplate = "/{recordid}", RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.WrappedRequest)]
public StatusInfo DeleteCallWithParam(string recordid)
{
StatusInfo statusInfo = new StatusInfo();
logger.Trace(Tagname + "Edit");
ResolveStatus(statusInfo, "DELETE/"+recordid);
return statusInfo;
}
enter code here
I received the exception:
Who is "I"? One of the web services or some other client?
If I'm understanding things correctly, it's the receiving end that seems to be expecting a range of responses, 401 not being one of them. It maybe some error checking code that expects "this range" of responses and does X (and 401 isn't one of these, or there is no "default" method to account for x response?).
That said, 401, is an authorization error so check on possible ServiceAuthorizationManager and/or similar settings in place that isn't being met by "I" causing the 401 response in the first place....
Hth...
My guess is you are missing authorizaton headers or credentials.
Check this out :
Consume RESt API from .NET
How to authenticate with Rest-client based on HttpClient and .net4

Wcf Client: Passing XML string in the WCF REST service using WebInvoke

With out parameter for Display method it is working in browser i.e http://localhost:2617/UserService.svc/test
When i add one parameter i am unable to browse it also.
I have the following contract.
[ServiceContract]
public interface IUserService
{
[OperationContract]
[WebInvoke(Method="PUT",UriTemplate = "/tes/{name}",
BodyStyle=WebMessageBodyStyle.WrappedRequest)]
string Display(string name);
}
public string Display(string name)
{
return "Hello, your test data is ready"+name;
}
I am trying to call using the following code
string url = "http://localhost:2617/UserService.svc/test"; //newuser
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
string xmlDoc1 = "<Display xmlns=\"\"><name>shiva</name></Display>";
req.Method = "POST";
req.ContentType = "application/xml";
byte[] bytes = Encoding.UTF8.GetBytes(xmlDoc1);
req.GetRequestStream().Write(bytes, 0, bytes.Length);
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
Stream responseStream = response.GetResponseStream();
var streamReader = new StreamReader(responseStream);
var soapResonseXmlDocument = new XmlDocument();
soapResonseXmlDocument.LoadXml(streamReader.ReadToEnd());
I am unable to get output for that.please help me on this.
There are a few things that are not quite right in your code.
Client
On the client you need to specify the namespace to be tempuri, since you have not declared an explicit one, so your client code would need to be this:
string url = "http://localhost:2617/UserService.svc/test"; //newuser
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
string xmlDoc1 = "<Display xmlns=\"http://tempuri.org/\"><name>shiva</name></Display>";
req.Method = "POST";
req.ContentType = "application/xml";
byte[] bytes = Encoding.UTF8.GetBytes(xmlDoc1);
req.GetRequestStream().Write(bytes, 0, bytes.Length);
HttpWebResponse response = (HttpWebResponse)req.GetResponse();
Stream responseStream = response.GetResponseStream();
var streamReader = new StreamReader(responseStream);
var soapResonseXmlDocument = new XmlDocument();
soapResonseXmlDocument.LoadXml(streamReader.ReadToEnd());
Service
On the service the UriTemplate is not quite right - you are specifying /tes/{name} so that will be expecting a URL like http://localhost:2617/UserService.svc/tes/shiva but you are wanting to post XML data to it in the body so you should change that to UriTemplate = "/test" (I am assuming you meant test and not tes as in your question).
Also, the method should be POST if you are wanting to POST data to it (the client needs to match the service and I am assuming what you have on the client is what you want).
So, in conclusion, your IUserService should look like this:
[ServiceContract]
public interface IUserService
{
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "/test",
BodyStyle = WebMessageBodyStyle.WrappedRequest)]
string Display(string name);
}
You still need to create a class
public class Test
{
public string name { get; set; }
}
You can also use fiddler to check if {name:999} could be passed as a parameter.

Return a WCF EF4 Entity as JSON

My service interface is:
[ServiceContract]
public interface IMyService
{
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "HelloJSON/{name}")]
string HelloJSON(string name);
[OperationContract]
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare, UriTemplate = "GetEmployees")]
List<Employee> GetEmployees();
}
My implementation is:
public class MyService : IMyService
{
public string HelloJSON(string name)
{
return string.Format("Hello {0} in JSON", name);
}
public List<Employee> GetEmployees()
{
using (DBEntities ctx = new DBEntities())
{
List<Employee> emp = new List<Employee>();
emp = (from e in ctx.Employee select e).ToList();
return emp;
}
}
}
When I call the first method I get something like "Hello pepe in JSON", that's ok.
When I call the second method and set a breakpoint on line "return emp;" I get the list of the employees(there are 6 records from the database), but in IE I get this:
Internet Explorer cannot display the webpage
and testing in Firefox all I get is a blank page with a blank body, no HTML, no data and no errors.
I think WCF can't serialize my default EF4 entities.
EDIT:
My final solution was something(not exactly) like this:
static string SerializeJSON<T>(T obj) {
JavaScriptSerializer serializer = new JavaScriptSerializer();
return serializer.Serialize(obj); }
EF entities cannot be serialized by default you must add code generation to them.
Refer to this article on how to create Serializable entities.
called Self Tracking entities
The best way would be to enable RIA Services and expose JSON Endpoint, it does everything correctly.
http://blogs.msdn.com/b/davrous/archive/2010/12/14/how-to-open-a-wcf-ria-services-application-to-other-type-of-clients-the-json-endpoint-4-5.aspx
http://channel9.msdn.com/Shows/SilverlightTV/Silverlight-TV-26-Exposing-SOAP-OData-and-JSON-Endpoints-for-RIA-Services

WCF WebApi's Self-Hosting not accepting PUT verbs

I have put together a HTTP driven API using the WCF WebAPI that uses the PUT verb. When hosted inside of an MVC3 project that is hosted upon IIS Express, everything works as designed.
However, when I unit-test I'm occasionally wanting to test the transport aspects rather than just against my own resources. My unit-tests fail with a 405 - MethodNotAllowed. Again, exactly the same service hosted in IIS works (where I enabled the PUT and DELETE verbs in the configuration file).
How can I get the 'self-hosted' service, as used in my testing, to accept these verbs too?
The almost identical 'get' tests work, so I'm not expecting the concept of the following to be at fault.. hopefully...
[Test]
public void PutNewMachine()
{
// Create new record to add
var machine = new Machine
{
ID = 1,
Name = "One",
Description = "Machine #1",
Location = 1
};
using (var client = new HttpClient())
{
using (var request = new HttpRequestMessage(
HttpMethod.Put,
HOST + "/1"))
{
request.Content = new ObjectContent<Machine>(machine);
using (var response = client.Send(request))
{
Assert.AreEqual(
HttpStatusCode.Created,
response.StatusCode,
"New record put should have been acknowledged "
+ "with a status code of 'Created'");
}
}
}
}
In the setup to the test, I'm preparing the end-points using the following Autofac code (and again this works for the 'Get'):
var builder = new ContainerBuilder();
builder
.Register(c => new FakeDatabase())
.As<IDatabase>()
.SingleInstance();
builder
.Register(c => new GenericRepository<Machine>(c.Resolve<IDatabase>()))
.As<IResourceRepository<Machine>>();
builder
.Register(c => new MachineService(c.Resolve<IResourceRepository<Machine>>()))
.As<MachineService>();
Container = builder.Build();
Scope = Container.BeginLifetimeScope();
host = new HttpServiceHost(typeof(MachineService), HOST);
host.AddDependencyInjectionBehavior<MachineService>(Container);
host.Open();
My service is defined in the following interface:
[ServiceContract]
public interface IResourceService<in TKey, TResource>
{
[WebGet(UriTemplate = "{key}")]
TResource Get(TKey key);
[WebInvoke(Method = "PUT", UriTemplate = "{key}")]
TResource Put(TKey key, TResource resource);
[WebInvoke(Method = "POST")]
TResource Post(TResource resource);
[WebInvoke(Method = "DELETE", UriTemplate = "{key}")]
void Delete(TKey key);
}
So, for example, if I have a MachineService, it implements the interface (both class MachineService : IResourceService<string, Machine> and ... : IResourceService<int, Machine> have been trialled - Get = OK, Put = Nothing.
EDIT: I seem to be bouncing between InternalServerError and MethodNotAllowed errors - only when using the self-hosting. I have ensured that I, as a user, have rights to open the port (Win7 + non-admin) but the results of that plus my choice of ports seems predicable functional for Get. "Post" seems to be having similar issues! :-(
EDIT2: Interface has now changed to which works!
[ServiceContract]
public interface IResourceService<in TKey, TResource>
{
[WebGet(UriTemplate = "{key}")]
TResource Get(TKey key);
[WebInvoke(Method = "PUT", UriTemplate = "{key}")]
TResource Put(HttpRequestMessage<TResource> resourceRequest, TKey key);
[WebInvoke(Method = "POST", UriTemplate = "{key}")]
TResource Post(HttpRequestMessage<TResource> resourceRequest, TKey key);
[WebInvoke(Method = "DELETE", UriTemplate = "{key}")]
void Delete(TKey key);
}
Doing PUT or POST works for me when I change the method signature to accept a HttpRequestMessage request instead of T itself.