I know there are a lot of questions out there about this - believe me I've read a lot of them and tried the answers out.
(This project is for an internal company lan, not the internet)
We have a WCF webservice which is RESTFUL and sends/receives JSON, it requires NTLM (Kerb would also be good) auth/credentials to ensure that the calling user (from the browser is who they say they are), and this is negotiated between the browser/client and the service by the WCF bindings:
<bindings>
<webHttpBinding>
<binding name="webHttpBindingAuth">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm"/>
</security>
</binding>
</webHttpBinding>
</bindings>
Using Fiddler, I can call the methods on the service GET and POST successfully and provided I adjust the JSON we're sending to the webservice (to include the session ID for example) it trundles along happily.
The problems started when we tried to use JavaScript/jQuery to call the webservice; the idea is that a web server will supply the HTML/JS to the client browser, the browser should then call the WCF webservice to get a session and allow a user to perform a few actions (we have 3 methods in total).
Firstly, we ran into the X-Domain issue, which we tried to resolve by getting the web server to return the correct headers (Access-Control-Allow-Origin). This didn't prevent browsers like SRIron from telling us that;
XMLHttpRequest cannot load http://{webServiceUri}/InstantMessagingService/chat/start/{username}. Origin http://{web**Server**Uri} is not allowed by Access-Control-Allow-Origin.
Following this, I investigated the possibility of using Silverlight (doesn't seem to support NTLM auth over WebHttpBinding), reverse proxy is out, as the IIS server being used in dev won't be used in prod (I believe it is WebSphere, but not under our control); next I looked at this:
http://msmvps.com/blogs/paulomorgado/archive/2007/04/27/wcf-building-an-http-user-agent-message-inspector.aspx
Which left me under the impression that the WCF web service was in fact the thing that needed to tell the browser where it was allowed be called from (if that makes sense). Having implemented all the code from the example, I found that the ApplyClientBehavior was never called to attempt to return the headers to the client (monitoring this in Fiddler too). Some more Googling led me to:
http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/707aa031-f4ff-47ab-ba5b-730f7930605e/
Since we're accessing the web service using jQuery and not some .NET client/service reference/proxy/app.. blah, I'm thinking that it's not possible to pre-request-send these headers to allow access to the service. Also, Fiddler seems to think that it's getting a 401 Unauthorized when it does make the attempt to call the http://../chat/start/.. method.
Here's the jQuery I'm using to attempt to make the call (I supplied a few settings in the hope it was going to work):
var url = webserviceUrl + "chat/start/" + remoteUserUri;
$.ajax({
type: 'GET',
url: url,
crossDomain: true,
beforeSend: function(xhr){ xhr.withCredentials = true; },
contentType: "application/json; charset=utf-8",
success: function (data) { conversationStarted(data); },
dataType: 'json'
});
Ok, if anyone has helpful hints or ideas, please fire away. I'll reply and edit etc. to make sure this is kept up to date, I hope I haven't missed anything (but my heads spinning a little from my Googling).
Also, I know that there might be better ways to do this, but I'd like to do it in the cleanest/quickest way from where I am now - i.e. not a lot of code changes, rewrites etc. I can also post up configs if people think they really are useful.
In the end I had to kind of work around, we got the guys in charge of the web server to agree to serving up an IFrame which points to our IIS hosted page (the one containing the JS etc.)
Then using the magic of WCF/Windows we host the WCF services on port 80 which gets around the X-Domain origin stuff. So I can then have REST/JSON without having to use JSONP.
I know it's not the answer to the question given the criteria I specified, but I think I ran out of options completely in the end.
Can you use JSONP? It'll solve your cross-domain problem in a heartbeat.
Related
Using WSO2 API Manager 1.3.1. Trying to use the API Manager to proxy to a REST service. I have set up the service in API Mgr and can successfully post and get responses, typically json, though some are text.
However, when I try to GET a resource that returns binary content (a zip "file", content-type:application/octet-stream), the API Manager does not seem to respond and I can see an error in the console window (i'm running wso2server.bat in console):
[2013-07-03 11:52:05,048] WARN - SourceHandler Connection time out
while writing the response: 173.21.1.22:1268->173.21.1.22:8280
I have an HTTPModule on my internal service and it seems to be responding with the appropriate content (I can see the GET and response data logged). I can also call to the internal service directly and get a response, so that end of things seems OK. But going through the API Manager seems to fail.
I found information on enabling other content-types:
WSO2 API Manager - Publishing API with non-XML response
http://wso2.com/library/articles/binary-relay-efficient-way-pass-both-xml-non-xml-content-through-apache-synapse
Using that information I tried to enable the application/octet-stream for messageFormatter and messageBuilder using the binary relay and it did not help (or seem to make a difference). I have even disabled all other content-types and use the binary relay for all content-types and it does not help.
Currently, I'm running with just the following in both axis2.xml and axis2_client.xml (in their appropriate sections):
<messageBuilder contentType=".*" class="org.wso2.carbon.relay.BinaryRelayBuilder"/
<messageFormatter contentType=".*" class="org.wso2.carbon.relay.ExpandingMessageFormatter"/>
I still get my json and text responses, but WSO2 times out getting the zip content. I saw the JIRA referenced in axis2.xml about enabling the ".*" relay, but as the other requests seem to work, I'm not sure it's an issue for me. I did try adding
'format="rest"' to the API definition, but it seemed to break all operations even the ones that worked prior so I've pulled it back out.
Any ideas on what is happening or how to dig in and debug this will help. Thanks!
After working with this for much too long, it turns out that my WSO2 configuration was correct, using the Message Relay and BinaryRelayBuilder, etc. While my REST service could reply immediately, I was setting a HTTP header that I assume WSO2 does not like, because when i removed it WSO2 would reply at an expected rate (instantly).
I was setting the header:
Transfer-Encoding: binary
When I removed that header from my service reply, then WSO2 operated as expected. I don't know if that's a "bug" in WSO2 or if I was implementing incorrectly, but I do have what seems like a "workaround" by omitting that header from my service response.
I am trying to set up a simple WCF REST service that we will be able to call from jquery. When I visit the URL in the browser, I get a response back. When I attempt to send an HTTPRequest directly (using the Firefox plugin RESTClient), I get a response back. These responses look correct to me, containing headers and a string of JSON and apparently nothing else.
However, when I call this same service via a jquery.get() or jquery.post() call, nothing happens. Using the jquery.ajax() call reveals the error callback gets called, but to me the data given to the error callback doesn't give me clues as to why this isn't working.
<a id="thelink" href="javascript:void(0)">GO</a>
<script type="text/javascript">
function doAjax() {
$.ajax({type: "POST",
dataType: "json", // see note below
url: "http://localhost:54459/MySimpleService.svc/json/24",
success: function(jqXHR, textStatus) {
alert("success");
},
error: function(jqXHR, textStatus, errorThrown) {
alert("error");
}
});
}
$('#thelink').click(doAjax);
</script>
Note: for the dataType parameter I have tried all of "text", "json", "jsonp" and leaving the parameter off. Similarly, I have tried with both GET and POST (modifying the web service to respond to GET or POST respectively).
The method called at that endpoint doesn't do anything spectacular that should be failing -- it simply concatenates the string to a constant and returns (just testing at this point, it will clearly do more interesting things in the future)
Firefox shows me getting back 200 OK when I click the link.
The WCF service is running out of my Visual Studio 2010 debug environment. It hits breakpoints placed in the method that responds to this service method.
I have tried accessing the html posted above from a file on my local machine (port 80) as well as hosting it elsewhere, with the same results. Could this be a security issue?
The values passed to the error callback don't seem terribly helpful:
jqXHR - readyState : 0, status : 0, statusText : error
textStatus - "error"
errorThrown - ""
So, why does my service seem to work, for GET or POST, when I call the url in a browser or RESTClient, but fail when I attempt to call it from jquery.ajax()?
EDIT: after more tests based on the comments I received, I tried hosting the page with the javascript on the same port as the web service, and this shows success. In my real-world scenarios, I won't have the luxury of hosting the client side code and the web service on the same domain/port. I'm assuming this is a security model issue -- where do I look to allow this sort of access (whitelisting domains/ports or otherwise)
The issue seemed to be in my web.config; as we worked out in comments, the issue is one of cross-domain security. I was already using a webHttpBinding on my endpoint, but did not realize it needed to be configured:
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webHttpBindingWithJsonP" crossDomainScriptAccessEnabled="true"/>
</webHttpBinding>
</bindings>
...
</system.serviceModel>
Setting crossDomainScriptAccessEnabled="true" seems to have done the trick. Note that I already have <serviceMetadata httpGetEnabled="true"/> as part of my service behavior.
make sure you have already configured your service to be called from script clients: adding a couple of elements in web.config in your service:
<behaviors>
<endpointBehaviors>
<behavior name="webScriptEnablingBehavior">
<enableWebScript/>
</behavior>
</endpointBehaviors>
more info here: http://msdn.microsoft.com/en-us/library/bb763177(v=vs.90).aspx
hope it helps,
I am working on the fastest way to write cookies from a WCF self hosted console app REST service with WebHttpBinding.
I prepare the "Set Cookies" but only the first cookie is set in the browser (using FireFox 11.0 on Windows) as well as the "Expires" value.
var expiry = DateTime.Now.AddMonths( 6 );
var outresp = WebOperationContext.Current.OutgoingResponse;
outresp.Headers.Add( HttpResponseHeader.SetCookie , "cookie1=fd9416ea-2705-4d44-be76-f2f7b95e6b13; cookie2=7d489e69-d9c9-4b8d-a4b1-bce0da1c5966;expires=" + expiry );
I also tried putting a comma between the first cookie and the second one, that did not work either:
outresp.Headers.Add( HttpResponseHeader.SetCookie , "cookie1=fd9416ea-2705-4d44-be76-f2f7b95e6b13;, cookie2=7d489e69-d9c9-4b8d-a4b1-bce0da1c5966;expires=" + expiry );
I cut this down, and fired up Fiddler:
outresp.Headers.Add( HttpResponseHeader.SetCookie , "foo=foovalue;,bar=barvalue;" );
and Fiddler shows this:
Response sent 27 bytes of Cookie data:
Set-Cookie: foo=foovalue;,bar=barvalue;
This response did not contain a P3P Header.
Validate P3P Policies at: http://www.w3.org/P3P/validator.html
Learn more at: http://www.fiddler2.com/redir/?id=p3pinfo
Which tells me that I am not formatting things on the .NET side to make the cookies acceptable to the browser.
How can I get all of the cookies into the browser insetad of just the first value? What did I miss?
Thanks.
[ EDIT ]
I took the issue to Microsoft for support, and learned this:
The fact that "Set-Cookie" is munged into a single cookie line is logged as a bug within Microsoft for .NET Framework 4.0 and also Framework 4.5.
Within the Microsoft WCF development group, the bug is listed as "closed" and "won't fix".
According to Microsoft, the only workaround is to move from self-hosted to hosting within IIS and then using the (IIS) Response object directly (different code path that does not have the bug).
Although this is an older post, since this is still an ongoing issue and using the AspNetCompatibilityMode with WCF has significant performance downside, the option that MSFT has supplied is not viable under many/most/any circumstances. I'm going to answer this question late since this issue is still lingering.
The only way to resolve this issue is to output the Set-Cookie response header just as you've shown, but to process that response header on the client side with javascript and place it in to the browser since, as you've noted, the browser will not handle this response. Neither will applying more than one Set-Cookie response header work as noted in this bug I created.
http://connect.microsoft.com/VisualStudio/feedback/details/779111/wcf-rest-service-two-set-cookie-http-headers-invalid-set-cookie-header-syntax
So, process the response header and use document.cookie in client-side javascript to place the cookies in the browser within the success handler of your jQuery .ajax request.
To expand on #jeff-fischer 's answer, AspNetCompatibilityMode does work and requires the following:
AspNetCompatibilityRequirements is set for the service class to either Allowed or Required e.g.:
[AspNetCompatibilityRequirements(RequirementsMode
= AspNetCompatibilityRequirementsMode.Allowed)]
public class AppService : IAppService
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> is set in <system.serviceModel>
This then gives access to HttpContext (you'll need using System.Web; to get access to this) and cookies can be set using:
var aCookie = new HttpCookie("foo")
{
HttpOnly = true,
Value = "bar",
Expires = DateTime.Now.AddDays(1)
};
HttpContext.Current.Response.Cookies.Add(aCookie);
This will then need to be run as an application on IIS rather than though the WCF launcher and if multiple cookies are set, multiple cookie headers will actually appear.
I am trying to call a wcf service over https and have followed the suggested setup from here. It works fine over http, but I get a RemoteCertificateNameMismatch error over https which I am handling with this (as suggested) -
System.Net.ServicePointManager.ServerCertificateValidationCallback +=
(se, cert, chain, sslerror) => { return true; };
I later get a 415 unsupported media type error which I can't figure out. I have a win .net test client that is able to call the service and receive results, but from monotouch I can't get it to work. Has anyone been able to do this successfully and wouldn't mind pasting an example?
Any help is much appreciated!
HTTP error codes comes from the server side. Of course the client configuration may play a role into this.
If possible switch to HTTP and compare sessions (e.g. using wireshark) between your Windows's and MonoTouch clients. Doing so in HTTPS may tell you a few things but that's less likely to be helpful.
Also check for similars 415 errors affecting other (non-MonoTouch) projects. Since the error comes the server the information they provide might help you find what's going on.
Scenario:
Web Service needed to calculate values and send results back as json. These calls are to be made cross-domain so I'm using jsonp. The problem I'm having occurs both on the same domain and cross-domain.
Problem:
I'm having an issue getting back json data from a WCF Service. While on my local machine it works fine, but when on the server the response from the service is cut short (if I run it through Visual Studio on the server though, it's fine).
The content length seems to be set as the length of my response before wrapping it in the jquery callback function (example data below).
Local: jQuery151017220264650085249_1309423933796({"d":"[\"Flat\",\"Terrace\",\"Semi\",\"Detached\",\"Bungalow\"]"});
Local: jQuery151017220264650085249_1309423933797({"d":"[\"New build\",\"2000 to 2010\",\"1990 to 2000\",\"1970 to 1990\",\"1950 to 1970\",\"Pre 1950\"]"});
Live: jQuery1510246237260361726_1309424024004({"d":"[\"Flat\",\"Terrace
Live: jQuery1510246237260361726_1309424024005({"d":"[\"New build\",\"2000 to 2010\",\"1990 to 2000\",\"1970 to
To wrap the json response with the callback function I had to use a httpmodule I found on the net.
The server uses IIS 7.5, locally i'm just using Visual Studio. (Some kind of IIS configuration?)
Right, hopefully I've provided enough information, if not let me know.
So if anyone has any idea how I could fix this issue it would be greatly appreciated.
Thanks
You don't need to use an HTTP Module to wrap the response in JSONP - this feature is available in WCF 4.0. You can set the property CrossDomainScriptAccessEnabled in WebHttpBinding, and if the request comes from a different domain (and it has a ?callback=FunctionName) parameter, the response will be wrapped in FunctionName(...).