I have written a WCF REST Service as follows
namespace UserService
{
// TODO: Modify the service behavior settings (instancing, concurrency etc) based on the service's requirements. Use ConcurrencyMode.Multiple if your service implementation
// is thread-safe.
// TODO: Please set IncludeExceptionDetailInFaults to false in production environments
[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceContract]
public class Service
{
UserManager userManager = new UserManager();
[OperationContract]
[WebGet(UriTemplate = "{userName}")]
[WebHelp(Comment = "Gets an user object given the username")]
public User GetUser(string userName)
{
return userManager.Read(userName);
}
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "", RequestFormat=WebMessageFormat.Xml, ResponseFormat=WebMessageFormat.Xml, BodyStyle=WebMessageBodyStyle.Bare)]
[WebHelp(Comment = "Creates an User")]
public void CreateUser(User user)
{
userManager.Create(user);
}
}
}
I am accessing this from my ASP.NET application as follows.
HttpClient client = new HttpClient();
HttpContent content = null;
DiscussionForum.Library.User user = new User();
user.UserEmailAddress = emailAddressTextBox.Text;
user.UserName = userNameTextBox.Text;
user.UserPassword = passwordTextBox.Text;
content = HttpContentExtensions.CreateXmlSerializable<DiscussionForum.Library.User>(user);
content.LoadIntoBuffer();
HttpResponseMessage response = client.Post(new Uri("http://localhost/UserService/Service.svc"),"application/xml", content);
Response.Write(response.StatusCode.ToString());
I am getting a Badrequest 400 in the status code on the client side.
Am I missing something?
Well, one thing I immediately see is that your service uses the DataContractSerializer and your client uses the XmlSerializer, and so the XML representations of the "User" type probably aren't the same.
Either use the [XmlSerializerFormat] attribute on the service, or use HttpContentExtensions.CreateDataContract on the client (but not both of course :)
But I'm not 100% sure that this is your problem (or the only problem)... If it's not, reply here and I can help with some debugging tips.
Related
I have a WCF service. In global.asax, I put some data into the ASP.NET session. But when I call the WCF method, the Session object is always NULL.
This is my WCF service
[WebMethod(EnableSession = true)]
public List<Menu> GetMenus()
{
List<Menu> menulist = new List<Menu>();
Object[] O = HttpContext.Current.Session["menu"] as Object[];
foreach (var item in O)
{
Menu menu = new Menu();
menu.Html = ((WcfServices.Menu)(item.ToType(typeof(Menu)))).Html;
menu.Label = ((WcfServices.Menu)(item.ToType(typeof(Menu)))).Label;
menulist.Add(menu);
}
return menulist;
}
[WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.WrappedRequest)]
List<Menu> GetMenus();
Here is my global.asax file
protected void Session_Start(object sender, EventArgs e)
{
CrmHelper helper = new CrmHelper();
if (Session["service"] == null)
{
IOrganizationService s = helper.CreateService(true);
Session["service"] = s;
}
MobileHelper mobilehelper = new MobileHelper((IOrganizationService)Session["service"]);
if (Session["menu"] == null)
{
Session["menu"] = mobilehelper.GetMainMenus();
}
}
Accessing Session["service"] or Session["menu"] always returns null when I call the WCF service.
Any ideas?
The main issue is: WCF is NOT ASP.NET !
By default, WCF does NOT rely on the ASP.NET runtime (and that's a Good Thing!!) and thus doesn't have access to the ASP.NET constructs like Session ...
If you need to provide some data to a WCF service, best place to put that info is a database table where the WCF service can load the information from.
If you insist on using ASP.NET session storage, and you don't mind that you're limiting yourself to using only IIS with ASP.NET as the hosting environment for your WCF service in that case, check out this blog post showing exactly what you need to do to gain access to the ASP.NET session state.
The question:
Are there any means (perhaps a workaround) to support the PUT and DELETE HTTP verbs with a WCF 4.0 REST service secured using OAuth 2.0 and DotNetOpenAuth 4? I'm not using the WCF Web API or WCF Starter Kit.
What I have done so far:
Consider the following WCF 4.0 REST Service:
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyService
{
[WebGet(UriTemplate = "")]
public List<Resource> GetCollection()
{
/* some implementation */
}
[WebInvoke(UriTemplate = "", Method = "POST")]
public Resource Create(Resource instance)
{
/* some implementation */
}
[WebGet(UriTemplate = "{id}")]
public Resource Get(string id)
{
/* some implementation */
}
[WebInvoke(UriTemplate = "{id}", Method = "PUT")]
public Resource Update(string id, Resource instance)
{
/* some implementation */
}
[WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
public void Delete(string id)
{
/* some implementation */
}
}
OAuth 2.0 is used for controlling access to the service. It is achieved with a custom ServiceAuthorizationManager implementation using DotNetOpenAuth to do all the work. The implementation is almost identical to that provided by the DotNetOpenAuth 4.0 samples.
For POST and GET requests it works great. However, the requests fail for the PUT and DELETE requests with the error message:
'AccessProtectedResourceRequest' messages cannot be received with HTTP verb 'PutRequest'
The client code looks as similar to:
public class ResourceClient
{
public Resource UpdateResource(Resource resource)
{
var uri = new Uri(new Uri("http://api.example.com/"), Resource.Format("resources/{0}", resource.Id));
var serializer = new DataContractSerializer(typeof(Resource));
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = "PUT";
request.ContentType = "application/xml";
ClientBase.AuthorizeRequest(request, Authorization.AccessToken);
using (var requestStream = request.GetRequestStream())
{
serializer.WriteObject(requestStream, resource);
}
using (var response = (HttpWebResponse)request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
Debug.Assert(responseStream != null, "responseStream != null");
return (Resource)serializer.ReadObject(responseStream);
}
}
}
}
From the answer of another question it seems that this is may be a deficiency in DotNetOpenAuth. Looking at the OAuth 2.0 spec, I don't see anything prohibiting the use of the PUT or DELETE http verbs when accessing resources. That said, I'm not an expert in OAuth 2.0, nor do I know it by heart.
So I thought I would be "clever" and use the "X-HTTP-Method-Override" to specify PUT and then POST the request to the resource server. I implemented the behaviour as explained in this sample. Interestingly enough the custom behavior was called before my ServiceAuthorizationManager implementation, resulting in the exact same error.
An alternative would be to address the issue within DotNetOpenAuth itself, but I have no idea where to start.
I'm running out of ideas and I'd appreciate any suggestions.
This is tracked by Issue #62. You can glance at the fix for it if you want to port it to your version without waiting for the next release.
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.
When I make a standard Get Request call to a restful wcf service it returns with a content type of "application/xml". A vendor is asking we send with a content type of "text/xml". How do I switch this in wcf? Is it an attribute?
The call is this:
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, Namespace = "")]
[XmlSerializerFormat(Style = OperationFormatStyle.Document, Use=OperationFormatUse.Literal)]
public class Player
{
[WebGet(UriTemplate = "{id}")]
public string GetTestDetailsRequest(string id)
{
TestService.TestServiceClient testServiceClient = new TestServiceClient();
string xml = testServiceClient.GetTestDetailsRequest(Guid.Parse(id));
return xml;
}
}
Don't try and use WCF to call RESTful services. Just use HttpWebRequest or HttpClient, that way you will have control over your request.
You can override the content type:
WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml";
is it possible to call a service operation at a wcf endpoint uri with a self hosted service?
I want to call some default service operation when the client enters the endpoint uri of the service.
In the following sample these uris correctly call the declared operations (SayHello, SayHi):
- http://localhost:4711/clerk/hello
- http://localhost:4711/clerk/hi
But the uri
- http://localhost:4711/clerk
does not call the declared SayWelcome operation. Instead it leads to the well known 'Metadata publishing disabled' page. Enabling mex does not help, in this case the mex page is shown at the endpoint uri.
private void StartSampleServiceHost()
{
ServiceHost serviceHost = new ServiceHost(typeof(Clerk), new Uri( "http://localhost:4711/clerk/"));
ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(typeof(IClerk), new WebHttpBinding(), "");
endpoint.Behaviors.Add(new WebHttpBehavior());
serviceHost.Open();
}
[ServiceContract]
public interface IClerk
{
[OperationContract, WebGet(UriTemplate = "")]
Stream SayWelcome();
[OperationContract, WebGet(UriTemplate = "/hello/")]
Stream SayHello();
[OperationContract, WebGet(UriTemplate = "/hi/")]
Stream SayHi();
}
public class Clerk : IClerk
{
public Stream SayWelcome() { return Say("welcome"); }
public Stream SayHello() { return Say("hello"); }
public Stream SayHi() { return Say("hi"); }
private Stream Say(string what)
{
string page = #"<html><body>" + what + "</body></html>";
return new MemoryStream(Encoding.UTF8.GetBytes(page));
}
}
Is there any way to disable the mex handling and to enable a declared operation instead?
Thanks in advance, Dieter
Did you try?
[OperationContract, WebGet(UriTemplate = "/")]
Stream SayWelcome();
UPDATE:
Not sure why it is not working for you, I have a self hosted WCF service with the following service contract:
[ServiceContract]
public interface IDiscoveryService {
[OperationContract]
[WebGet(BodyStyle=WebMessageBodyStyle.Bare, UriTemplate="")]
Stream GetDatasets();
The only difference I can see is that I use WebServiceHost instead of ServiceHost.