How do I access the request body in XML format in a WCF REST service? - wcf

I can create a WCF REST service and POST, PUT and GET data OK.
How do I access the request body in XML format on the service side to send to an SQL database?

You can actually pass arguments to to your web methods using this attribute
[WebGet(UriTemplate = "users/{username}")]
here is a sample method from msdn
[WebGet(UriTemplate = "users/{username}")]
[OperationContract]
User GetUserAccount(string username)
{
if (!IsUserAuthorized(username))
{
WebOperationContext.Current.OutgoingResponse.StatusCode =
HttpStatusCode.Unauthorized;
return;
}
User user = FindUser(username);
if (user == null)
{
WebOperationContext.Current.OutgoingResponse.SetStatusAsNotFound();
return null;
}
return user;
}

In MVC3, the Request object is available in the controller, with the content of the body being available in the InputStream object. This code worked for me:
this.Request.InputStream.Position = 0;
var xmlContent = new System.IO.StreamReader(this.Request.InputStream).ReadToEnd();
Hope that helps.

Related

RestRequest Body not received in .net core web api

I am trying to build a service client to simplify calling my microservices in .net core.
Here is a service client sample:
public ProductServiceClient(SystemEnvironment.MachineEnvironment? environment = null)
{
this.url = ServiceEnvironment.Urls.GetUrl(ServiceEnvironment.Service.Product, environment);
}
private RestClient GetClient(string method)
{
return new RestClient(url + "/api/" + method);
}
private RestRequest GetRestRequest(Method method)
{
var restRequest = new RestRequest(method);
restRequest.RequestFormat = DataFormat.Json;
restRequest.AddHeader("Content-Type", "application/json");
return restRequest;
}
public FindProductsResponse FindProducts(FindProductsRequest request)
{
var restRequest = GetRestRequest(Method.GET);
restRequest.AddJsonBody(request);
var client = this.GetClient("Products");
var restResponse = client.Get(restRequest);
return new JsonDeserializer().Deserialize<FindProductsResponse>(restResponse);
}
public void Dispose()
{
}
And here is how I am trying to read it in my .net core api:
[HttpGet]
public ActionResult<FindProductsResponse> Get()
{
var request = "";
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
request = reader.ReadToEnd();
}
var buildRequest = JsonConvert.DeserializeObject<FindProductsRequest>(request);
var products = _service.FindProducts(buildRequest);
if (products != null && products.Any())
{
return new FindProductsResponse()
{
Products = products
};
}
return BadRequest("Not found");
}
However the request variable is always empty after Request.Body has been processed by the StreamReader.
If I make the same request from Postman (also using GET), I get the body just fine.
What am I doing wrong here?
EDIT: This is the unit test calling the api:
[Test]
public void Test1()
{
using (var productServiceClient = new ProductServiceClient())
{
var products = productServiceClient.FindProducts(new FindProductsRequest()
{
Id = 50
}).Products;
}
}
It can be your Request.Body has been already consumed.
Try to call Request.EnableRewind() before to open the StreamReader.
I'm not sure why you are manually doing it. It looks like you are reinventing the wheel. ASP.NET Core already does that for you.
This is what your service should look like:
[HttpGet] // oops, GET requests will not allow Bodies, this won't work
public ActionResult<FindProductsResponse> Get([FromBody]FindProductsRequest buildRequest)
{
// skip all the serialization stuff, the framework does that for you
var products = _service.FindProducts(buildRequest);
if (products != null && products.Any())
{
return new FindProductsResponse()
{
Products = products
};
}
return BadRequest("Not found");
}
And if you don't want to redo all the busy work that is retyping all the code on the client side, I suggest you read up on swagger (probably in the form of Swashbuckle). Client code can be generated. Even from within Visual Studio, if you right-click on the project and in the context menu pick "Add REST API Client...". Please don't erroneously hand-code what can be generated flawlessly by a machine instead. I don't really know what went wrong in your specific case, but searching bugs that could be avoided altogether is just busywork, that time should be spent on other parts of the program.
I just realized this is a GET request. ASP.NET will not recognize bodies for GET-Requests. You will need to make it a PUT or POST request or put your parameters in the query string.
If you happen to make that mistake as often as I did, you might want to write some unit tests that cover this. Because .NET is not helping you there. Been there, done that..

REST api cacheablity with authorization

I'm building a protected api for a web application.
for each web service call client sends an access token.
when call for a resource depending on the access token it returns different responses.
ex:- call to /employees will return accessible employees only. accessibility will be defined for each access token.
my question is how it's possible to cache the response if it's returned different things depend on the access token.
is the access token part of the request which is considered in caching?
can the API be REST if it's not cacheable?
is partial access to resource allowed in REST?
#DamithK please clear what you want to do i am not getting it.."when call for a resource depending on the access token it returns different responses.
But as much i understand is that you want to authenticate your each api call.
If you are using RestClient for call api you can do it in following way.To call Api
var client = new RestClient(Serviceurl);
var request = new RestRequest("/Apimethod/{Inputs}?oauth_consumer_key=1ece74e1ca9e4befbb1b64daba7c4a24", Method.GET);
IRestResponse response = client.Execute(request);
In your service
public static class Authentication
{
public static bool AuthenticateRequest(IncomingWebRequestContext context)
{
bool Authenticated = false;
try
{
NameValueCollection param = context.UriTemplateMatch.QueryParameters;
if (param != null && param["oauth_consumer_key"] != null)
{
string consumerSecretKey = "1ece74e1ca9e4befbb1b64daba7c4a24";
Authenticated = param["oauth_consumer_key"] == consumerSecretKey;
}
else
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
}
}
catch (Exception)
{
}
return Authenticated;
}
}
Validate each request in called method using
Authentication.AuthenticateRequest(WebOperationContext.Current.IncomingRequest)
All this is c# code

Logging the XML or JSON sent by RestSharp

I'm using RestSharp to send information to a API. I would like to log the XML that I've sent to this API so I can debug later.
I would like to do something like this:
var request = new RestRequest(resourcePath, method);
request.AddBody(dto);
Logger.Log(request.Content);
var response = Client.Execute(request);
But, the actual request sent by RestSharp does not seem to be exposed.
Everything sent in the request is available in request.Parameters.
To make getting the request body easier I created this extension method:
public static class RestSharpExtensions
{
public static string GetBody(this IRestRequest request)
{
var bodyParameter = request.Parameters
.FirstOrDefault(p => p.Type == ParameterType.RequestBody);
return bodyParameter == null
? null
: bodyParameter.Value.ToString();
}
}

How to consume third party webservice through Wcf service SOAP with out service reference

I am trying to call the third party webservice in WCF service.
i am able to call the the service by adding the service reference in(URL) through WCF service, but while doing that i heard from the people it is not the right way of doing this, when i googled this, found through channelfactory we can call the service .
But the problem is it is saying the contract dlls should get shared between client and server, this i am not getting.
can any body provide the sample to call the service with out adding service reference.
http://www.codeproject.com/Tips/558163/Difference-between-Proxy-and-Channel-Factory-in-WC
and what are the issues if we add service reference in the project and what are the pitfalls?
I am sure that i have to call SOAP service only
Thanks
You can use HttpWebRequest to create a web request and then pass the XML document describing whole SOAP request and get the response. Something like below code I used in one of my application. I have removed few bits from it that are specific to my application but you will get the idea.
public static string ProcessRequestSOAP()
{
string result = "";
try
{
XmlDocument soapEnvelop = new XmlDocument();
soapEnvelop.LoadXml(SOAPXML_Request); // This is the SOAP xml document that is generated when calling web service. You have to manually create this.
string url = webServiceURL; // External Web Service URL
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.ContentType = "text/xml;charset=\"utf-8\"";
webRequest.Accept = "text/xml";
webRequest.Method = "POST";
webRequest.Credentials = new NetworkCredential(userName,password); // Username/password to call web service if required
using (Stream stream = webRequest.GetRequestStream())
{
soapEnvelop.Save(stream);
}
//get response from server
WebResponse response = webRequest.GetResponse();
//read response stream
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
if (reader != null)
{
result = System.Xml.Linq.XDocument.Load(reader).ToString();
}
else
{
result = null;
}
}
}
catch (SoapException ex)
{
result = new ExceptionXElement(ex, false).ToString();
}
catch (Exception ex)
{
result = new ExceptionXElement(ex, false).ToString();
}
return result;
}

Is anything wrong with my manual implementation of Basic HTTP Authentication?

I am using WCF to develop a RESTful Web Service that acts as a proxy to a set of stored procedures that, for security reasons, cannot be directly accessed by Internet-facing applications.
The class implementing the service's DataContract has a helper property that retrieves the name of the currently logged-in user, or generates an HTTP 401 Unauthorized response if no user is currently logged in.
[ServiceContract]
public class MyService
{
// The helper property
string UserName
{
get
{
WebOperationContext context = WebOperationContext.Current;
if (context != null)
{
string authHeader = context.IncomingRequest.Headers[HttpRequestHeader.Authorization];
if (authHeader != null && authHeader.StartsWith("Basic "))
{
string string64 = authHeader.Substring(6);
byte[] array64 = Convert.FromBase64String(string64);
string decoded = ASCIIEncoding.ASCII.GetString(array64);
string[] authParts = decoded.Split(':');
if (ValidateLogin(authParts[0] /*userName*/,
authParts[1] /*password*/))
return authParts[0];
}
}
OutgoingWebResponseContext outgoing = context.OutgoingResponse;
outgoing.StatusCode = HttpStatusCode.Unauthorized;
outgoing.Headers[HttpResponseHeader.WwwAuthenticate] = "Basic";
return null;
}
}
[OperationContract]
public int LengthOfUserName()
{
return UserName.Length;
}
}
However, when I attempt to log in with a valid user name and password, I still get an Unauthorized error. What's wrong with my code?