Asp.net session doesn't seem to work with WCF WebHttpBinding - wcf

We're doing a WCF REST json service (WebHttpBinding). Since pure WCF session doesn't work with this kind of binding, we are using asp.net session. So we set:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
In our web.config, and :
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
On top of our service implementation.
We have several service methods such as:
public void OpenSession(string userName)
public void GetSomething(int somethingId)
public void CloseSession()
Then we test the sessionId of the HttpContext in each method.
HttpContext.Current.Session.SessionID
Problem is: it's always changing.
If I make a call to OpenSession?userName='toto' in my web browser, and another one right after to GetSomething?somethingId=1234, the session id will change.
It seems that the cookies aren't well handled by wcf. If I call the OpenSession method in fiddler, in the http headers, there're no 'Set-Cookie' returned by the service.

I found that :
By default, WCF does not have cookie
enabled on the client side. So when
the server requires Cookie, you need
to turn on the cookie on the binding
for the client through the property
HttpTransportBindingElement.AllowCookies.
It's in this article. Checki if it will be usefull for you : http://blogs.msdn.com/b/wenlong/archive/2006/01/23/516041.aspx

Related

Consume wcf service in .net core which to use services.AddSingleton or services.AddTransient

I am consuming a WCF service in my .net core API project and I used the following steps:
The binding in WCF service (for testing) <add binding="basicHttpsBinding" scheme="https" />
Created a proxy class using https://learn.microsoft.com/en-us/dotnet/core/additional-tools/dotnet-svcutil-guide?tabs=dotnetsvcutil2x (This created a proxy which has an interface IService1 and calls Service1Client).
In the startup class I have bound the interface with the class services.AddSingleton<IService1, Service1Client>();
The method endpoint in the WCF service gets the data form the database according to the parameter passed to consume the WCF service.
I am not sure which one I should use, services.AddSingleton or services.AddTransient, because I am not sure what the proxy class is using to call the method.
If I create a single instance, will it be locked?
I have done a Jmeter test with 1000 rows in the database and 1000 rows from csv as a parameter to consume the API, but did not find any lock and all were successful under 3 minutes.
You can view the servicereference.cs file to see which method in the wcf service is called by the proxy class.
Then we need to instantiate the proxy class and call the WCF service through the instantiated proxy class:
ServiceReference.Service1Client service1 = new Service1Client();
By this documentation in a web api a single httpclient should be instantiated:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client#create-and-initialize-httpclient
Of course if you have multiple target services, with different endpoints, an HttpClient per service is needed (you set the base address in the HttpClient)
In your case your generated client should use an HttpClient under the covers. If you can determine if the whole genrated client is thread safe then you can inject the client as a Singleton, if not you should add it as scoped

WCF REST Service - 401 Unauthorized

We're in the process of developing a WCF REST web service which just receives a bunch of arbitrary text from any anonymous user and then performs some processing on the back end.
For example, here's one method from our web service:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class MyRESTService : IMyRESTService
{
[WebInvoke(Method = "PUT", UriTemplate = "/MyRESTMethod?paramA={paramA}&paramB={paramB}")]
public Stream MyRESTMethod(string paramA, string paramB, Stream rawData)
{
//do some stuff...
}
}
If we just use the default IIS settings we get a (401) Unauthorized. However, after much trial and error we figured out that we could get it to work by giving WRITE access to 'Everyone' on the actual .svc file for our service.
My question is: why in the world would IIS need to have WRITE access to an .svc file for this to work? Is there a better way or am I stuck with this hackish (and possibly insecure) workaround?
WTF Microsoft?
Possibly related:
PUT and DELETE in RESTful WCF Service cause 401 Unauthorized error
IIS7 Post/Put/Patch/Delete WCF oData - Authentication Failure 401.3
I have also found this can be fixed by putting
<authentication mode="None" /> inside of <system.web> in your web.config
After talking to a tech representative from M$ I was informed that this is indeed the expected behavior. The service must have write access enabled for someone to send a request to it, and when you do this it will actually set write access automatically on the .SVC file as well.

WCF -> ASMX and Cookies

I have a web application that communicates to a WCF service through a WCF client. At the point my code is invoked, authentication cookies have been issued and I have a dependency on an ASMX service that expects those authentication cookies.
I need to pass the cookies from the web application through the WCF client to the WCF service to the ASMX service.
Any ideas? It looks like my best bet may be setting allowCookies to false, parsing out the cookie headers, attempting to re-create them on the WCF service and then attaching them to the SOAP request.
Note: I looked at this article, which seems close but not quite applicable to this question. In the linked scenario, an ASMX service is creating cookies, which must be persisted to a subsequent ASMX service by the same WCF client.
Ok, so there's two main things that need to occur:
Get the cookie from the web app context to the WCF service
Get the cookie from the WCF service to the ASMX service
NOTE: Because you didn't specify, I'm going to assume that you're using a WCF client within your WCF service to talk to the ASMX service. If this is not the case please let me know and I will revise this post accordingly.
Step #1:
I would recommend writing an IClientMessageInspector which you bind to your client endpoints using an IEndpointBehavior. In your implementation of IClientMessageInspector::BeforeSendRequest you basically read the cookie out of the current HttpContext::Request::Cookies collection and append the value as a message header. That would look a little something like this:
public void BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Get the cookie from ASP.NET
string cookieValue = HttpContext.Current.Request.Cookies["MyCookieName"].Value;
// Create a header that represents the cookie
MessageHeader myCookieNameHeader = MessageHeader.CreateHeader("MyCookieHeaderName", "urn:my-custom-namespace", cookieValue);
// Add the header to the message
request.Headers.Add(myCookieNameHeader);
}
One you configure the endpoint with this message inspector every logical request will automatically flow the cookie value through as a header to your WCF service. Now, since your WCF service doesn't actually care about the header itself, it can basically ignore it. In fact, if you only did this step, you should be able to run all your code right now and not experience any difference.
Step #2:
Now we need the cookie to go from the WCF service to the ASMX service. Once again all you need to do is implement an IClientMessageInspector, except your BeforeSendMessageRequest is going to be a little different:
public void BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Get the cookie value from the custom header we sent in from step #1
string cookieValue = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>("MyCookieHeaderName", "urn:my-custom-namespace");
HttpRequestMessageHeaderProeperty httpRequestMessageHeaderProperty;
MessageProperties outgoingMessageProperties = OperationContext.Current.OutgoingMessageProperties;
// Get the HttpRequestMessageHeaderProperty, if it doesn't already exist we create it now
if(!outgoingMessageProperties.TryGetValue(HttpRequestMessageHeaderProperty.Name, out httpRequestMessageHeaderProperty))
{
httpRequestmessageHeaderProperty = new HttpRequestMessageHeaderProperty();
outgoingMessageProperties.Add(HttpRequestMessageHeaderProperty.Name, httpRequestmessageHeaderProperty);
}
// Set the cookie header to our cookie value (note: sample assumes no other cookies set)
httpRequestmessageHeaderProperty.Headers[HttpRequestHeader.Cookie] = cookieValue;
}
Once again you need to bind this to the endpoint for your ASMX service using an IEndpointBehavior and every logical request you make will automatically pass the cookie through.

HttpContext.Current.User is always null

I have a WCF service that has a method to return the Windows Username of a Silverlight client that is consuming the service . The WCF service is using basicHttpBinding with the TransportCredentialOnly mode set and the TransportClientCredentialType set to Windows. In IIS Windows authentication is enabled and anon authentication disabled.
Despite (apparently) configuring the service correctly when I call the service and ask it to return the username it errors. Closer examination shows that HttpContext.Current.User is always null (so a nullreferenceexception is thrown).
Does anyone have any other ideas why this is not working?
Try adding -
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
to your config file
In WCF, there is the OperationContext object from which you can retrieve the security credentials passed in by the caller/client by using the ServiceSecurityContext property.

Invoking WCF service method through a browser

I've a WCF service which uses basic http binding.
How do I invoke its operations/methods through a browser?
You would need to add WebGetAttribute to your method like following sample
[OperationContract]
[WebGet(UriTemplate = "/placesList/{userId}",
ResponseFormat = WebMessageFormat.Xml)]
List<Places> GetAllPlacesForUser(String userId)
{
string xml = "";
// build xml here
return xml;
}
Now in the browser, you could invoke the method like this
http://localhost:8085/GeoPlacesDataService/placesList/10
where 10 is the userId parameter.
Note: In order to add WebGetAttribute you have to reference System.ServiceModel.Web namespace which is found in a separate assembly
I would recommend setting up multiple endpoints for the Service. Add an endpoint using webHttpBinding to get an XML version of the service. If this is done correctly the response you will get from the service is identical to the basicHttpBinding endpoint, but without the SOAP overhead.
Other than that, you can't call a SOAP web service directly from the browser because it requires a form post. You could use a tool to test it using SOAP though, I recommend Soap UI. Its written in Java but I try not to hold that against it. :)
After adding the above code, the endpoint property has to be modified in web.config, binding="webHttpBinding" and behaviorConfiguration="webHttp".