Upload image as stream with other parameters WCF - c#

I am trying to save an image in a database through a WCF. This is my code.
public void saveImage(Stream stream, string size)
{
//int intsize = Convert.ToInt32(size);
byte[] buffer = new byte[10000];
int bytesRead, totalBytesRead = 0;
string encodedData = "";
do
{
bytesRead = stream.Read(buffer, 0, buffer.Length);
encodedData = encodedData + Convert.ToBase64String(buffer,
Base64FormattingOptions.InsertLineBreaks);
totalBytesRead += bytesRead;
} while (bytesRead > 0);
And this is the contract.
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "SaveImage/{size}")]
void saveImage(Stream stream, string size);
And finally this is part of my config file
<system.serviceModel>
<services>
<service behaviorConfiguration="RestServiceBehavior" name="ABBStreamService.ABBConnectStreamWCF">
<endpoint address="" behaviorConfiguration="web" binding="webHttpBinding" bindingConfiguration="webHttpBinding" contract="ABBStreamService.IABBConnectStreamWCF" />
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="webHttpBinding" transferMode="Streamed" maxReceivedMessageSize="2147483647" maxBufferSize="10485760" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00">
<readerQuotas maxStringContentLength="2147483647" maxArrayLength="1000000" />
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="RestServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
When i try to run the service with only the Stream as parameter, it works. But when i try to add another parameter it fails.

You could add a header to the request then read that in the service.
//Javascript to set the header
var xhr = new XMLHttpRequest();
var image = document.getElementById("yourFileInput").files[0];
xhr.setRequestHeader('size', image.size);
//C# in your service method to read the size header
IncomingWebRequestContext request = WebOperationContext.Current.IncomingRequest;
var headers = request.Headers;
string size = headers["size"];

I know i am late but this will be useful for searchers.
Send your file in body and rest of parameters in header of request.
following sample code is tested in Post man and working like a charm.
your Interface
[WebInvoke(
Method = "POST",
RequestFormat = WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "PostImage")]
void PostImage(Stream stream);
.svc Method:
public void PostImage(Stream stream)
{
// get image from stream and implement your logic
var headers = OperationContext.Current.IncomingMessageProperties["httpRequest"];
// other parameters will be accessible here e.g image name etc
var imgname = ((HttpRequestMessageProperty)headers).Headers["imgname"];
}
for reference please see this link

Related

How Create Get and Post Methods for the Login Page in wcf service in c#

I am creating a WCF service in ASP.NET and there i need to implement Get and Post Methods for simple Login page
This is for running the application on Local host.I have SQL server for the database.
C#:
This is the interface I have coded:
[ServiceContract]
public interface ILogin
{
[OperationContract(Name = "PostUserDetails")]
[WebInvoke(Method = "POST",UriTemplate = "")]
string UserName(Stream data);
string UserPassword(Stream data);
[OperationContract(Name = "GetUserDetails")]
[WebGet(UriTemplate = "GetUserDetails/inputStr/{name}")]
string UserName(string name);
string UserPassword(string name);
}
This is the class I have coded:
public class Login :ILogin
{
public string UserName(Stream data)
{
StreamReader streamReader = new StreamReader(data);
string xmlString = streamReader.ReadToEnd();
string returnValue = xmlString;
return returnValue;
}
public string UserPassword(Stream data)
{
StreamReader streamReader = new StreamReader(data);
string xmlString = streamReader.ReadToEnd();
string returnValue = xmlString;
return returnValue;
}
public string UserName(string strUserName)
{
StringBuilder strReturnValue = new StringBuilder();
// return username prefixed as shown below
strReturnValue.Append(string.Format("You have entered userName as {0}", strUserName));
return strReturnValue.ToString();
}
public string UserPassword(string strUserName)
{
StringBuilder strReturnValue = new StringBuilder();
// return username prefixed as shown below
strReturnValue.Append(string.Format("You have entered userName as {0}", strUserName));
return strReturnValue.ToString();
}
}
I have also configured the web.config as:
<system.serviceModel>
<services>
<service name="MyWCFService.Login" behaviourConfiguration ="loginbehaviour" >
<endpoint name="webHttpBinding" address="" binding="webHttpBinding" contract="MyWCFService.ILogin" behaviorConfiguration="webHttp">
</endpoint>
<endpoint name ="mexHttpBinding" address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyWCFServiceBehaviour">
<serviceMetadata httpGetEnabled="false"></serviceMetadata>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webHttp"></behavior>
<webHttp/>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
I am getting HTTP Error 404.7 - Not Found Error
First of all you cant use Stream as your input. because stream con not serialized. then you don't need to create two separate methods. create login like this and use * as your Method. something like this:
[OperationContract(Name = "PostUserDetails")]
[WebInvoke(Method = "*",UriTemplate = "")]
string UserName(Data data);
string UserPassword(Data data);

WCF service streaming images

I'd like to make WCF service which can put image into stream.
I have next in config:
<service name="Images" behaviorConfiguration="ImagesBehavior">
<endpoint address="http://localhost:5523/Images.svc"
binding="basicHttpBinding" contract="Images" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:5523/Images" />
</baseAddresses>
</host>
</service>
<behavior name="ImagesBehavior">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
And code:
[OperationContract]
[WebGet(UriTemplate = "GetImage/{imageID}",
BodyStyle = WebMessageBodyStyle.Bare)]
public Stream GetImage(string imageID)
{
try
{
if (WebOperationContext.Current != null)
WebOperationContext.Current.OutgoingResponse.ContentType = "image/jpeg";
var ms = new MemoryStream(myImage);
return ms;
}
catch (Exception e)
{
if (WebOperationContext.Current != null)
WebOperationContext.Current.OutgoingResponse.ContentType = "text/xml";
Console.WriteLine("GetImage ERROR:" + e.Message + "\r\n" + e.StackTrace);
byte[] errorBytes = Encoding.UTF8.GetBytes("ERROR");
return new MemoryStream(errorBytes);
}
}
But when I'm trying this via browser like
http://localhost:5523/Images.svc/GetImage/imagecodeblabla
I've got
400 Bad Request
And when
http://localhost:5523/Images/GetImage/imagecodeblabla
405 Method Not Allowed
What's wrong?
Is your service SOAP or REST? It appears that you're using REST syntax (WebGetAttribute) but your binding is BasicHttpBinding (SOAP).
Try using WebHttpBinding instead and see how that goes!

Why WCF nettcpBinding is slow on local machine

I am trying to check the WCF latency on my local machine. And it takes 2-4 millisecond/request (which seems to slow). I am not sending or receiving any complex object (so no serialization/deserialization involved).
Here is the service part:
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
Here is the server's config.
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https"/>
</protocolMapping>
<bindings>
<netTcpBinding>
<binding name="defaultNetTcpBinding" closeTimeout="00:10:00" sendTimeout="00:10:00" receiveTimeout="00:10:00" openTimeout="00:10:00" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647" maxBufferSize="2147483647">
<readerQuotas maxArrayLength="2147483647" maxStringContentLength="2147483647" />
<security mode="None"></security>
</binding>
</netTcpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
<services>
<service name="WcfTestApplication.Service1">
<endpoint address="net.tcp://localhost/WcfTestApplication/Service1.svc" contract="WcfTestApplication.IService1" binding="netTcpBinding" />
<endpoint address="mex" contract="IMetadataExchange" binding="mexTcpBinding"/>
</service>
</services>
And here is the client code
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.None;
EndpointAddress address = new EndpointAddress("net.tcp://localhost/WcfTestApplication/Service1.svc");
ChannelFactory<IService1> channelFactory = new ChannelFactory<IService1>(binding, address);
IService1 _clientProxy = channelFactory.CreateChannel();
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 1000; i++)
{
IService1 _clientProxy1 = channelFactory.CreateChannel();
var data = _clientProxy1.GetData(4);
((IClientChannel)_clientProxy1).Close();
}
watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds.ToString());
Console.ReadLine();
Updated:
Strange enough, there is no difference when I change binding to Named pipe. It still takes 2-4 millisecond/request.

WCF REST service url routing based on query parameters

Since WCF routing doesn't support routing for REST services, I created a REST service that has one enpoint which accepts all incoming requests and than redirects those requests based on the query parameters.
I did this by following this article http://blog.tonysneed.com/2012/04/24/roll-your-own-rest-ful-wcf-router/.
This approach works for passing through requests and returning the results. The problem is whenever I get an error, like a 404, from the actual service the message that is returned to the client is a 400 (Bad Request).
What I would like to have is a routing proxy that actually just redirects the calls to the real service based on the query and returns all the errors to the client as they come from the real service.
Is this even the right approach to what I'm trying to accomplish, or are there easier or better solutions?
Any help is appreciated!
In the following I added what my code looks like.
app.config:
<!--
System.net
-->
<system.net>
<settings>
<servicePointManager expect100Continue="false" useNagleAlgorithm="false" />
</settings>
<connectionManagement>
<add address="*" maxconnection="24" />
</connectionManagement>
</system.net>
<!--
System.ServiceModel
-->
<system.serviceModel>
<!--
Services
-->
<services>
<service name="RoutingGateway.RoutingService">
<endpoint address="/api/routing" binding="webHttpBinding" bindingConfiguration="secureWebHttpBinding" contract="RoutingGateway.IRoutingService" behaviorConfiguration="RESTBehaviour" />
</service>
</services>
<client>
<endpoint binding="webHttpBinding" bindingConfiguration="secureWebHttpBinding" contract="RoutingGateway.IRoutingService" name="routingService" behaviorConfiguration="RESTBehaviour" />
</client>
<!--
Bindings
-->
<bindings>
<webHttpBinding>
<binding name="secureWebHttpBinding" hostNameComparisonMode="StrongWildcard" maxReceivedMessageSize="2147483647" transferMode="Streamed">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</webHttpBinding>
</bindings>
<!--
Behaviors
-->
<behaviors>
<endpointBehaviors>
<behavior name="RESTBehaviour">
<dispatcherSynchronization asynchronousSendEnabled="true" />
<webHttp helpEnabled="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false before deployment -->
<serviceMetadata httpsGetEnabled="false" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false" />
<!-- Enable Throttling -->
<serviceThrottling maxConcurrentCalls="100" maxConcurrentInstances="100" maxConcurrentSessions="100" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
IRoutingService.cs:
[ServiceContract(Namespace = "https://test/api/routing")]
public interface IRoutingService
{
[OperationContract(Action = "*", ReplyAction = "*")]
[WebInvoke(UriTemplate = "*", Method = "*")]
Message ProcessRequest(Message requestMessage);
}
RoutingService.cs:
public Message ProcessRequest(Message requestMessage)
{
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
Uri originalRequestUri = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.RequestUri;
// Gets the URI depending on the query parameters
Uri uri = GetUriForRequest(requestMessage);
// Select rest client endpoint
string endpoint = "routingService";
// Create channel factory
var factory = new ChannelFactory<IRoutingService>(endpoint);
Uri requestUri = new Uri(uri, originalRequestUri.PathAndQuery);
factory.Endpoint.Address = new EndpointAddress(requestUri);
requestMessage.Headers.To = requestUri;
// Create client channel
_client = factory.CreateChannel();
// Begin request
Message result = _client.ProcessRequest(requestMessage);
return result;
}
I ended up catching all CommunicationExceptions and then rethrowing WebFaultExceptions with the appropriate messages and status codes.
Here is the code:
Message result = null;
try
{
result = _client.ProcessRequest(requestMessage);
}
catch (CommunicationException ex)
{
if (ex.InnerException == null ||
!(ex.InnerException is WebException))
{
throw new WebFaultException<string>("An unknown internal Server Error occurred.",
HttpStatusCode.InternalServerError);
}
else
{
var webException = ex.InnerException as WebException;
var webResponse = webException.Response as HttpWebResponse;
if (webResponse == null)
{
throw new WebFaultException<string>(webException.Message, HttpStatusCode.InternalServerError);
}
else
{
var responseStream = webResponse.GetResponseStream();
string message = string.Empty;
if (responseStream != null)
{
using (StreamReader sr = new StreamReader(responseStream))
{
message = sr.ReadToEnd();
}
throw new WebFaultException<string>(message, webResponse.StatusCode);
}
else
{
throw new WebFaultException<string>(webException.Message, webResponse.StatusCode);
}
}
}
}

Cannot open host WCF REST Services

I am trying to implement some WCF and REST services to upload a file on my server, and I have found some code which I am trying to implement, but no success yet.
My code
class Program
{
static void Main(string[] args)
{
string address = "http://localhost/UploadService/UploadService.svc/UploadFile/theFile.txt";
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(address);
req.Method = "POST";
req.ContentType = "text/plain";
Stream reqStream = req.GetRequestStream();
string fileContents = "the quick brown fox jumped over the lazy dog.";
byte[] bodyContents = Encoding.UTF8.GetBytes(fileContents);
reqStream.Write(bodyContents, 0, bodyContents.Length);
reqStream.Close();
HttpWebResponse resp;
try
{
resp = (HttpWebResponse)req.GetResponse();
}
catch (WebException e)
{
resp = (HttpWebResponse)e.Response;
}
Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription);
}
}
public class UploadService : IUploadService
{
#region IUploadService Members
public void UploadFile(string fileName, Stream fileContent)
{
using (StreamReader fileContentReader = new StreamReader(fileContent))
{
string content = fileContentReader.ReadToEnd();
File.WriteAllText(Path.Combine(#"c:\temp", fileName), content);
}
}
#endregion
}
[ServiceContract]
public interface IUploadService
{
[OperationContract]
[WebInvoke(UriTemplate = "UploadFile/{fileName}")]
void UploadFile(string fileName, Stream fileContent);
}
Web.Config
<system.serviceModel>
<services>
<service behaviorConfiguration="ServiceBehavior" name="UploadService">
<endpoint address="" binding="webHttpBinding" contract="IUploadService" behaviorConfiguration="RestBehavior">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug httpHelpPageEnabled="false" includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="RestBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
At the moment, in the response resp = (HttpWebResponse)req.GetResponse(); I am getting:
The remote server returned an error: (400) Bad Request.
What should I do to fix this?
You are making a service request from the same process that is hosting the service. I think they should be separate, e.g. your service host in a console app and your test in another.

Categories

Resources