RESTful.NET service and preflight - c#

I'm developing a RESTful web service with WCF and C#. The service works fine, but when I made a request from a static HTML page, using XMLHttpRequest, all browsers except IE (who else?) first send a prefligth message (https://developer.mozilla.org/en/http_access_control) requesting the available options for the service. This kind of messages only occur when I change the headers of the message in order to send a POST request.
My question/suggestion: do you know any way to give support to this kind of messages on WCF/.NET? My idea is to implement a method to handle the preflight message; the reply would be a standard reply message with all the available options (GET, PUT, POST, DELETE), because all this operations will be available, and also tells that the only type of data available for the data on the messages is JSON. After the reply the normal request would be sent by the browser, so everything should work right.
What do you think?

You can support OPTIONS in the same way you support POST with the WebInvoke attribute. Just change the method. There is nothing special about the OPTIONS request. You just need to set the Allow header in the response.

Problem solved! I've setted the Method attribute with * and now it works fine: first the preflight request is answered and then the real request is received and handled. Code follows:
[OperationContract]
[WebInvoke(UriTemplate="*", Method = "*")]
void HandleHttpOptionsRequest();

[OperationContract]
[WebInvoke(UriTemplate="*", Method = "*")]
void HandleHttpOptionsRequest();
This is a great solution and you can implement an IDispatchMessageInspector to add the necessary CORS headers quite easily.

Related

Is it possible to add http headers to a Soap request from the SoapExtension method ProcessMessage()

I am trying to add Digital Security headers to some api calls that leverage a third-party sdk. One of the required security headers is a hash of the entire POST body being sent. The sdk we are leveraging calls a SOAP endpoint and the construction of the soap envelope is very far along in the request process. The sdk offered some obvious BeforeRequest extension points but they are executed before the SoapEnvelope is constructed.
There are a couple ways I have attempted to solve this:
I was trying to take a serializable object and recreate the request with the soap envelope but doing so did not yield a matching string as there were namespaces that were applied when they shouldnt, encoding on innner objects serialization etc (I am sure I am doing things wrong but these struggles got me looking elsewhere)
Next I found the SoapExtension class and thought this would work for me. In my testing I am seeing that I get the full request body (with the SOAP envelope) exactly as it would be sent. But I do not think or cannot find a way to modify the http headers of this request from within the hooks in SoapExtension.
So the question I am asking here is focused on my second approach and wondering if this is possible. If not I will go back to my first attempt and see if I can figure out how to correctly generate an identical request body.

Remove custom http header from future requests in .net web api

I've added a custom header X-XSRF-TOKEN and when a user logs out I want to be able to remove that header from future requests sent by the browser.
In the logout web api action I can modify the header like so:
Request.GetOwinContext().Response.Headers.Append("X-XSRF-TOKEN", "ModifiedToken");
On future requests it now sends requests with the X-XSRF-TOKEN with the value ModifiedToken. Is there a way I can remove it instead. With cookies you can expire them.
If I call the remove function on the Request or Response headers, on the next request to the server the header is still present:
Request.GetOwinContext().Response.Headers.Remove("X-XSRF-TOKEN");
or
Request.GetOwinContext().Request.Headers.Remove("X-XSRF-TOKEN");
Is it even possible to do this or even guarantee the browser will actually stop sending the header?
Try This.
Request.GetOwinContext().Response.Headers.Append("NULL", "ModifiedToken");
Note - I am not sure it will work or not. But you can try this one as well.

Why would WCF fail to call a SOAP service when a 302 response is encountered?

I have written an application that starts with making a WCF call to login. I generated the client code with a service reference. It works fine for clients who have their services installed locally to their network. However, there is a saas environment, as well, where these same services are controlled by the corporate powers that be. In the saas environment, I was informed that the login was failing. Investigating using Fiddler, I found that the service call to login is returning HTML, specifically, the web page listing all the available methods from the .asmx.
The saas environment has one little quirk which may be causing the problem here, but I don't know how to verify that this is the problem, nor how to solve it if it is the problem. The quirk is that the server redirects (302) the call.
The client code:
client.Endpoint.Address = new EndpointAddress("http://" + settings.MyURL + "/myProduct/webservices/webapi.asmx");
client.DoLogin(username, password);
The raw data sent to the server, before the redirect, includes the s:Envelope XML tag. Notice the missing s:Envelope XML tag when sending to the redirected server:
GET https://www.myurl.com/myProduct/webservices/webapi.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
VsDebuggerCausalityData: uIDPo7TgjY1gCLFLu6UXF8SWAoEAAAAAQxHTAupeAkWz2p2l3jFASiUPHh+L/1xNpTd0YqI2o+wACQAA
SOAPAction: "http://Interfaces.myProduct.myCompany.com/DoLogin"
Accept-Encoding: gzip, deflate
Host: www.gotimeforce2.com
Connection: Keep-Alive
How do I get this silly thing working?
Edit: It is worth noting that I am using WCF/svcutil.exe/service-reference rather than the older ASMX/wsdl.exe/web-reference. Otherwise, for future readers of this topic, the wsdl solution suggested by Raj would have been a great solution. If you are seeing this issue and are using the wsdl technique, see Raj's excellent answer.
Edit2: After doing a bunch of research into WCF and 302, it sounds like they just don't play well together, nor does there appear to be a simple way of giving the WCF api custom code to handle the situation. As I have no control over the server, I have sucked it up and re-generated my api as a web-reference and am using Raj's solution.
Edit3: Updated the title to better reflect the solution, now that the cause of the issue is understood. Original title: Why would WCF not include s:Envelope on a redirect?
Ok, So I did some digging on this and tried to replicate the issue on my side. I was able to replicate the issue and find a solution to it as well. However I'm not sure how well this will apply in your case since it is dependent on interfacing with the server team that manages the load balancer. Here are the findings.
Looking at http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html you notice the following addendum in the explanation for HTTP Status Codes 302 and 303.
302 Found
Note: RFC 1945 and RFC 2068 specify that the client is not allowed
to change the method on the redirected request. However, most
existing user agent implementations treat 302 as if it were a 303
response, performing a GET on the Location field-value regardless
of the original request method. The status codes 303 and 307 have
been added for servers that wish to make unambiguously clear which
kind of reaction is expected of the client.
303 See Other
Note: Many pre-HTTP/1.1 user agents do not understand the 303
status. When interoperability with such clients is a concern, the
302 status code may be used instead, since most user agents react
to a 302 response as described here for 303.
Further looking at http://en.wikipedia.org/wiki/List_of_HTTP_status_codes you notice the following explanation for the HTTP status codes 302, 303 and 307.
302 Found :
This is an example of industry practice contradicting the standard. The HTTP/1.0 specification (RFC 1945) required the client to perform a temporary redirect (the original describing phrase was "Moved Temporarily"), but popular browsers implemented 302 with the functionality of a 303 See Other. Therefore, HTTP/1.1 added status codes 303 and 307 to distinguish between the two behaviors. However, some Web applications and frameworks use the 302 status code as if it were the 303.
303 See Other (since HTTP/1.1):
The response to the request can be found under another URI using a GET method. When received in response to a POST (or PUT/DELETE), it should be assumed that the server has received the data and the redirect should be issued with a separate GET message.
Here is the basic flow in a normal Client/Server Interaction
307 Temporary Redirect (since HTTP/1.1):
In this case, the request should be repeated with another URI; however, future requests should still use the original URI. In contrast to how 302 was historically implemented, the request method is not allowed to be changed when reissuing the original request. For instance, a POST request should be repeated using another POST request.
So according to this, we are able to explain the behavior of the WCF call which sends a GET request without the s:Envelope on the 302 redirect. This will undoubtedly fail on the client side.
The easiest way of fixing this is to have the server return a 307 Temporary Redirect instead of a 302 Found status code in the response. Which is where you need the help of the Server Team that manages the redirect rules on the load balancer. I tested this out locally and the client code consuming the service with the Service Reference seamlessly executes the call even with the 307 Temporary Redirect.
In fact you could test this all out with the solution I've uploaded to Github Here. I've updated this to illustrate the utilization of a service reference instead of a wsdl generated proxy class to consume the asmx service.
However if the change from 302 Found to 307 Temporary Redirect is not feasible in your environment, then I would suggest using either Solution 1 (which shouldn't have a problem whether it is a 302 or 307 status code in the response) or using my original answer which would resolve this by directly accessing the service at the correct URL based on the setting in the config file. Hope this helps!
Solution 1
If you do not have access to the config files on production or if you just plain don't want to use the multiple URLs in the config file, you could use this following approach. Link to Github repo containing sample solution Click Here
Basically, if you notice the file auto generated by wsdl.exe you will notice that the service proxy class derives from System.Web.Services.Protocols.SoapHttpClientProtocol. This class has a protected method System.Net.WebRequest GetWebRequest(Uri uri) that you can override. In here you could add a method to check to see if a 302 temporary redirect is the result of HttpWebRequest.GetResponse() method. If so, you can set the Url to the new Url returned in the Location header of the response as follows.
this.Url = new Uri(uri, response.Headers["Location"]).ToString();
So create a class called SoapHttpClientProtocolWithRedirect as follows.
public class SoapHttpClientProtocolWithRedirect :
System.Web.Services.Protocols.SoapHttpClientProtocol
{
protected override System.Net.WebRequest GetWebRequest(Uri uri)
{
if (!_redirectFixed)
{
FixRedirect(new Uri(this.Url));
_redirectFixed = true;
return base.GetWebRequest(new Uri(this.Url));
}
return base.GetWebRequest(uri);
}
private bool _redirectFixed = false;
private void FixRedirect(Uri uri)
{
var request = (HttpWebRequest)WebRequest.Create(uri);
request.CookieContainer = new CookieContainer();
request.AllowAutoRedirect = false;
var response = (HttpWebResponse)request.GetResponse();
switch (response.StatusCode)
{
case HttpStatusCode.Redirect:
case HttpStatusCode.TemporaryRedirect:
case HttpStatusCode.MovedPermanently:
this.Url = new Uri(uri, response.Headers["Location"]).ToString();
break;
}
}
}
Now comes the part that illustrates the advantage of using a proxy class manually generated using wsdl.exe instead of a service reference. In the manually created proxy class. modify the class declaration from
public partial class WebApiProxy : System.Web.Services.Protocols.SoapHttpClientProtocol
to
public partial class WebApiProxy : SoapHttpClientProtocolWithRedirect
Now invoke the DoLogin method as follows.
var apiClient = new WebApiProxy(GetServiceUrl());
//TODO: Add any required headers etc.
apiClient.DoLogin(username,password);
You will notice that the 302 redirect is handled smoothly by the code in your SoapHttpClientProtocolWithRedirect class.
One other advantage is that, by doing this, you will not have to fear that some other developer is going to refresh the service reference and lose the changes that you made to the proxy class since you had manually generated it. Hope this helps.
Original Answer
Why don't you just include the entire url for production/local service in the config file? That way you can initiate the call with the appropriate url in the appropriate location.
Also, I would refrain from using a service reference in any code destined for production. One way of using the asmx service without a service reference would be to generate the WebApiProxy.cs file using the wsdl.exe tool. Now you can just include the WebApiProxy.cs file in your project and instantiate as shown below.
var apiClient = new WebApiProxy(GetServiceUrl());
//TODO: Add any required headers etc.
apiClient.DoLogin(username,password);
Here is the GetServiceUrl() method. Use a Configuration Repository to further decouple and improve testability.
private string GetServiceUrl()
{
try
{
return
_configurationRepository.AppSettings[
_configurationRepository.AppSettings["WebApiInstanceToUse"]];
}
catch (NullReferenceException ex)
{
//TODO: Log error
return string.Empty;
}
}
Then your config file can contain the following information in the section.
<add key="StagingWebApiInstance" value="http://mystagingserver/myProduct/webservices/webapi.asmx "/>
<add key="ProductionWebApiInstance" value="https://www.myurl.com/myProduct/webservices/webapi.asmx"/>
<!-- Identify which WebApi.asmx instance to Use-->
<add key="WebApiInstanceToUse" value="ProductionWebApiInstance"/>
Also I would refrain from concatenating strings using the + overload. When doing it once, it doesn't come across as too much of a performance impact but if you have many concatenations like this throughout the code, it would lead to a big difference in execution times compared to using a StringBuilder. Check http://msdn.microsoft.com/en-us/library/ms228504.aspx for more information on why using a StringBuilder improves performance.

Rest Sharp - 406 Error - No match for accept header

I'm getting a 406 error when trying to use RestSharp to post a request to a third-party application. I'm new to REST, so I have to admit I didn't even know you could add headers. I tried adding these, but I'm still getting the same issue:
var client = new RestClient(myURL);
RestRequest request = new RestRequest("restAction", Method.POST);
request.AddHeader("Accept", "text/plain");
request.AddHeader("Content-Type", "text/plain");
request.AddParameter("parameter1", param1);
request.AddParameter("parameter2", param2);
var response = client.Execute(request);
From what I've read, this may be dealing with a header named "accept". Is that right?
Any idea what could be going on?
In general in HTTP, when a client makes a request to a server, it tells the server what kinds of formats it's prepared to understand (accept). This list of acceptable formats is what the Accept header is for. If the server can't respond using any of the media types in the Accept header, it will return a 406. Otherwise, it will indicate which media type it chose in the Content-Type header of the response. Putting "*/*" in the Accept header tells the server that the client can handle any response media type.
In my original comment to your question, I said that RestSharp looks like it's including "*" in the Accept header by default, but looking closer I see now that it's actually not. So, if you don't override the Accept header like you've done here, the default header value is "application/json","application/xml","text/json","text/x-json","text/javascript","text/xml", and it appears the server you're talking to doesn't speak any of these media types.
If the server you're working with doesn't speak json or xml, I don't think you can use RestSharp, unless you create your own deserializer. I'm not sure if you can do this from the public API or if you'd have to modify the source yourself and recompile it for you own needs.
Since you're still getting HTTP errors from the server, I would recommend taking RestSharp out of the equation for right now, and just speaking HTTP directly to the server until you actually get a correct response from the server. You can use a tool like Fiddler to make a HTTP requests directly. When you send the request (for now in the debugging stage), send an Accept header of "*/*" to get around the 406. Once you've figured out what media types the server can send back to you, you should change this back to being a media type you know you can read and you know the server can send.
It sounds like the main issue here is really just not knowing the protocol of the server. If there's any documentation on the service you're talking to, I would read that very carefully to figure out what media types it's prepared to respond with and how to craft the URLs that it expects.

Server side redirect truncating request payloads

I'm on IIS 6 and I have an ASP.Net 4.0 site that's a single page to serve as a SOAP reverse proxy. I have to modify the return content in order to delete a trouble node from the response and add a tracking node.
In order to facilitate its function as a reverse proxy for all addresses, I have the 404 on the server set to a custom "URL" of "/default.aspx" (the page for my app)
For requests without a payload, it works perfectly - such as for ?WSDL Urls. It requests the proper URL from the target system, gets the response and sends it back - it's pretty utterly transparent in this regard.
However, when a SOAP request is being made with an input payload, the Request.InputStream in the code is always empty. Empty - with one exception - using SOAPUI, I can override the end point and send the request directly to /default.aspx and it will receive the input payload. Thus, I have determined that the custom 404 handler is - when server-side transferring the request - stripping the payload. I know the payload is being sent - I have even wiresharked it on the server to be sure. But then when I add code to log the contents of Request.InputStream it's blank - even though Request.ContentLength shows the right content length for the original request.
I've also been looking for a good way to use ASP.Net to intercept the requests directly rather than allowing the normal IIS 404 handler to take care of it but even with a wildcard mapping, I can't seem to get the settings right nor am I fully confident that it would help. (But I'm hoping it would?)
Finally, I don't have corporate permission to install MVC framework.
Thus, I need either some configuration for IIS I am missing to make this work properly or some other method of ensuring that I get the request payload to my web page.
Thanks!
What about using an HTTP Handler mapped to all requests?
You'll need to add a wildcard application mapping as detailed here and correctly configure your HTTP Handler.

Categories

Resources