Does anyone know, or better yet have an example, of a WCF service that will accept a form post encoded multipart/form-data ie. a file upload from a web page?
I have come up empty on google.
Ta, Ant
So, here goes...
Create your service contract which an operation which accepts a stream for its only parameter, decorate with WebInvoke as below
[ServiceContract]
public interface IService1 {
[OperationContract]
[WebInvoke(
Method = "POST",
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "/Upload")]
void Upload(Stream data);
}
Create the class...
public class Service1 : IService1 {
public void Upload(Stream data) {
// Get header info from WebOperationContext.Current.IncomingRequest.Headers
// open and decode the multipart data, save to the desired place
}
And the config, to accept streamed data, and the maximum size
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="WebConfiguration"
maxBufferSize="65536"
maxReceivedMessageSize="2000000000"
transferMode="Streamed">
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="WebBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="Sandbox.WCFUpload.Web.Service1Behavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Sandbox.WCFUpload.Web.Service1" behaviorConfiguration="Sandbox.WCFUpload.Web.Service1Behavior">
<endpoint
address=""
binding="webHttpBinding"
behaviorConfiguration="WebBehavior"
bindingConfiguration="WebConfiguration"
contract="Sandbox.WCFUpload.Web.IService1" />
</service>
</services>
</system.serviceModel>
Also in the System.Web increase the amount of data allowed in System.Web
<system.web>
<otherStuff>...</otherStuff>
<httpRuntime maxRequestLength="2000000"/>
</system.web>
This is just the basics, but allows for the addition of a Progress method to show an ajax progress bar and you may want to add some security.
I don't exactly know what you're trying to accomplish here, but there's no built-in support in "classic" SOAP-based WCF to capture and handle form post data. You'll have to do that yourself.
On the other hand, if you're talking about REST-based WCF with the webHttpBinding, you could certainly have a service methods that is decorated with the [WebInvoke()] attribute which would be called with a HTTP POST method.
[WebInvoke(Method="POST", UriTemplate="....")]
public string PostHandler(int value)
The URI template would define the URI to use where the HTTP POST should go. You'd have to hook that up to your ASP.NET form (or whatever you're using to actually do the post).
For a great introduction to REST style WCF, check out Aaron Skonnard's screen cast series on the WCF REST Starter Kit and how to use it.
Marc
Related
I have two behaviors configured in my endpoint:
One is for json serialization which is basically very similar to the exmaple here.
What's important there is the following:
public class NewtonsoftJsonBehaviorExtension : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(NewtonsoftJsonBehavior); }
}
protected override object CreateBehavior()
{
return new NewtonsoftJsonBehavior();
}
}
public class NewtonsoftJsonContentTypeMapper : WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
return WebContentFormat.Raw;
}
}
The other is for error handling. So that when exception is thrown a json formatted message will be sent to the client. The code is taken from here (The answer staring with: "Here's a complete solution based on some info from above:").
when I use only behavior 1 everything works fine. When I add the second behavior I get the following exception:
{"ExceptionType":"System.InvalidOperationException","Message":"The
incoming message has an unexpected message format 'Raw'. The expected
message formats for the operation are 'Xml', 'Json'. This can be
because a WebContentTypeMapper has not been configured on the binding.
See the documentation of WebContentTypeMapper for more details."}
Here is how my web.config looks like:
<services>
<service name="Algotec.Services.Archive.Data.ArchiveDataService" behaviorConfiguration="defaultBehavior">
<endpoint name="soap" address="soap" binding="basicHttpBinding" contract="Algotec.Interfaces.Archive.Data.IArchiveData" bindingNamespace="http://algotec.co.il/ArchiveData"/>
<endpoint name="restXml" address="" binding="webHttpBinding" contract="Algotec.Interfaces.Archive.Data.IArchiveData" behaviorConfiguration="restBehavior" bindingNamespace="http://algotec.co.il/ArchiveData"/>
<endpoint name="restJson" address="json" binding="webHttpBinding" contract="Algotec.Interfaces.Archive.Data.IArchiveData" behaviorConfiguration="jsonBehavior" bindingConfiguration="jsonBinding" bindingNamespace="http://algotec.co.il/ArchiveData"/>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"/>
</service>
</services>
...
<endpointBehaviors>
<behavior name="restBehavior">
<enhancedWebHttp defaultOutgoingRequestFormat="Xml" defaultOutgoingResponseFormat="Xml"/>
</behavior>
<behavior name="jsonBehavior">
<enhancedWebHttp defaultOutgoingRequestFormat="Json" defaultOutgoingResponseFormat="Json" helpEnabled="true"/>
<newtonsoftJsonBehavior/>
<jsonErrorBehavior/>
</behavior>
</endpointBehaviors>
...
<extensions>
<behaviorExtensions>
<add name="newtonsoftJsonBehavior" type="Algotec.Services.Infra.BehaviorExtensions.NewtonsoftJsonBehaviorExtension, Algotec.Services.Infra, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
<add name="jsonErrorBehavior" type="Algotec.Services.Infra.Behaviors.JsonErrorWebHttpBehaviorElement, Algotec.Services.Infra, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
Any ideas?
Why are you returning WebContentFormat.Raw from NewtonsoftJsonContentTypeMapper? Shouldn't you be returning WebContentFormat.Json so that the format matches correctly?
Can you clarify a bit what you're trying to accomplish?
Here is what solved my problem:
In the web.config I simply switched the order between <newtonsoftJsonBehavior/> and <jsonErrorBehavior/>.
I admit that I don't understand completely all this behaviors and don't know why it helped but it did.
My wcf reset service has only 2 contracts, one is working always and the other did not work.
Service Code:
Public Class BasicServ
Implements IBasicServ
Public Function DoWork() Implements IBasicServ.DoWork
Return "Working"
End Function
Function Authorize(ByVal id As String, ByVal pw As String) Implements IBasicServ.Authorize
Dim c As New List(Of Guid)
For i = 0 To 10
c.Add(Guid.NewGuid)
Next
Return c
'Return Guid.NewGuid
End Function
End Class
Contract File code:
<ServiceContract()>
Public Interface IBasicServ
<OperationContract()>
<WebGet(UriTemplate:="test/", BodyStyle:=WebMessageBodyStyle.Wrapped, RequestFormat:=WebMessageFormat.Json, ResponseFormat:=WebMessageFormat.Json)>
Function DoWork()
<OperationContract()>
<WebGet(UriTemplate:="Authorize/{id}/{pw}", BodyStyle:=WebMessageBodyStyle.Wrapped, RequestFormat:=WebMessageFormat.Json, ResponseFormat:=WebMessageFormat.Json)>
Function Authorize(ByVal id As String, ByVal pw As String)
End Interface
Web Config File:
<?xml version="1.0"?>
<configuration>
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="messagelistener"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="d:\logs\myMessages.svclog"></add>
</listeners>
</source>
</sources>
<trace autoflush="true"/>
</system.diagnostics>
<system.web>
<compilation debug="true" strict="false" explicit="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true"
logMessagesAtServiceLevel="false"
logMessagesAtTransportLevel="false"
logMalformedMessages="true"
maxMessagesToLog="5000"
maxSizeOfMessageToLog="2000">
</messageLogging>
</diagnostics>
<services>
<service behaviorConfiguration="ServBehav" name="AssistantWcf.BasicServ">
<endpoint address="auth" behaviorConfiguration="EndBehav" binding="webHttpBinding" name="endpointname" contract="AssistantWcf.IBasicServ" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="EndBehav">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServBehav">
<!-- To avoid disclosing metadata information, set the value below to false before deployment -->
<serviceMetadata httpGetEnabled="true" httpGetBinding="webHttpBinding" httpGetBindingConfiguration="" />
<!-- 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>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
While debugging/testing, I found that, if I return only 1 GUID in Authoriza method, it is working, but when I am sending a list of GUID, it is not working, and the SVClog file is not getting created. Could you please help me understand WCF REST service better. Thank you.
From above comments and few experiments, the solution for the question is that a return type is not compulsory if it is string, but if anything else, return type must be specified. Better if return type is provided always.
I migrated a web service to a WCF service. The service exposes two endpoints, one returns JSON and one returns SOAP.
The SOAP endpoint works as expected, but the response from the JSON endpoint is wrapped with the response object name - something that did not exist in the JSON response the web service I migrated from used to return.
I tried working with the BodyStyle property, but all I got is an exception: The body style 'Bare' is not supported by 'WebScriptEnablingBehavior'. Change the body style to be 'WrappedRequest'.
All my methods accept only POST requests.
This is how my service is configured:
<services>
<service name="MyService" behaviorConfiguration="MyServiceBehavior">
<endpoint address="" binding="basicHttpBinding" bindingNamespace="http://MyService.org/" contract="IMyService"/>
<endpoint address="json" behaviorConfiguration="JsonEndpointBehavior" binding="webHttpBinding" contract="IMyService" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="JsonEndpointBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
I am having problems calling my WCF service via jquery / JSON.
So far I have done the following:
In VS 2010, start a new "WCF Service Application" project. Visual Studio then auto generates a sample service called IService / Service, which has the function
string GetData(int value);
Inside IService.cs I add the WebGet attribute, as follows:
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
string GetData(int value);
Inside my web.config I have
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="WcfService1.Service1"
behaviorConfiguration="ServiceBehavior">
<endpoint contract="WcfService1.IService1"
binding="webHttpBinding"
behaviorConfiguration="AjaxBehavior" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
<behavior name="ServiceBehavior">
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="AjaxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
I build and run the service, and on opening
http://localhost:58403/Service1.svc/GetData?value=1
in my web browser it prints out (as expected)
{"d":"You entered: 1"}
5, I create a new asp.net web application project. Inside default.aspx, I add
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
var request = $.ajax({
type: "GET",
url: "http://localhost:58403/Service1.svc/GetData",
data: { value: "1" }
});
request.done(function (msg) {
alert(msg);
});
request.fail(function (jqXHR, textStatus) {
alert("Request failed: " + textStatus);
});
});
</script>
6, I build and run that, but instead of hitting the done callback, it hits the error callback and alerts "Request failed: error"
If I set a breakpoint in the service code, I can see that the GetData function is being hit and appears to return successfully. I can also see in the firebug net console that the web service call is returning a status code of "200 OK", but the error handler callback is being hit instead of the success callback. Does anyone know what I am doing wrong?
Well, I managed to solve this on my own. In case anyone is reading this and is curious, I changed the WCF service and web application so that they now both run via IIS instead of Visual Studio's web server.
So I now have
http://localhost/WcfService1/Service1.svc/GetData?value=1
http://localhost/WebApplication1/
I believe it was not working previously due to cross domain requests. Even though it was same domain, just different port numbers, I believe this still counts as cross domain?
I've been working in .NET for a while now, but I'm new to WCF. I'm trying to create my very first WCF service using JSON. I thought I would start really, really simple and then build from there. But I have somehow managed to screw up even the most simple of services. Here's what I've got so far.
Web.Config:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<services>
<service name="MarathonInfo.MarathonInfoService">
<endpoint address="http://localhost:10298/MarathonInfoService.svc" binding="webHttpBinding" contract="MarathonInfo.IMarathonInfo" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="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>
<serviceHostingEnvironment multipleSiteBindingsEnabled="false" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
Then, in the service file:
namespace MarathonInfo
{
public class MarathonInfoService : IMarathonInfo
{
public String GetData()
{
return "Hello World";
}
}
}
And in the interface:
namespace MarathonInfo
{
[ServiceContract]
public interface IMarathonInfo
{
[OperationContract]
[WebInvoke(Method = "GET", UriTemplate = "/GetData", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
String GetData();
}
}
So, when I go to this url:
http://localhost:10298/MarathonInfoService.svc/GetData
I get this error:
The message with To
'http://localhost:10298/MarathonInfoService.svc/GetData' cannot be
processed at the receiver, due to an AddressFilter mismatch at the
EndpointDispatcher. Check that the sender and receiver's
EndpointAddresses agree.
I am able to execute the service just fine through Visual Studio in debug mode. But in the browser, I only get that error.
What am I doing wrong?
Thanks!
Casey
If you want to create a WCF WebHTTP Endpoint (i.e., one which returns JSON, and uses the [WebGet] / [WebInvoke] attributes), the endpoint needs to have the <webHttp/> behavior associated with it.
<system.serviceModel>
<services>
<service name="MarathonInfo.MarathonInfoService">
<endpoint address="http://localhost:10298/MarathonInfoService.svc"
binding="webHttpBinding"
contract="MarathonInfo.IMarathonInfo"
behaviorConfiguration="Web"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="Web">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="false" />
</system.serviceModel>