Error 415 when posting JSON data to WCF Service

11,047

Solution 1

For a WCF endpoint to recognize the [WebInvoke] (or [WebGet]) annotations, it needs to be defined as a WebHTTP endpoint (a.k.a. REST endpoint) - which means using the webHttpBinding and the webHttp endpoint behavior. Since you didn't define any endpoints in web.config, it is using the default binding for the scheme, which is BasicHttpBinding. Endpoints with that binding only respond to XML (SOAP) requests, which is why you're getting that error when sending a JSON request.

Try replacing your <system.serviceModel> section in your web.config, and that should define your endpoint as you need it:

  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="Web">
          <webHttp/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <services>
      <service name="ProjectorService">
        <endpoint address=""
                  behaviorConfiguration="Web"
                  binding="webHttpBinding"
                  contract="IProjectorService" />
      </service>
    </services>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
  </system.serviceModel>

Solution 2

The problem is most likely that your service method requires 2 parameters. Default only 1 body parameter can be present.

You can fix this in 2 ways. First way: Add BodyStyle = WebMessageBodyStyle.Wrapped to your WebInvoke Attributes like so:

[WebInvoke(Method = "POST", UriTemplate = "projectors", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
void RecordBreakdown(string sn, int modelCode);

Second way: Create a class for the POST data. You can for example create a class for all the variables you want to post to your service as JSON. To create classes from JSON you can use Json2CSharp (or Paste JSON as Classes in VS2012 Update 2). The following JSON

{"sn":2705, "modelCode":1702 } 

will result in the following class:

public class Record
{
    public string sn { get; set; }
    public int modelCode { get; set; }
}

After that you will have to change your methods to accept this class like so:

void RecordBreakdown(string sn, int modelCode);

to:

void RecordBreakdown(Record record);

After that you now should be able to post JSON to the service.

Hope this helps!

Edit: Joining my answer from below with this one.

After looking again to your configuration file, it also looks like a binding misconfiguration. The default binding for WCF is a 'bassicHttpBinding' which is basically using SOAP 1.1.

Since you want to use JSON and a RESTFul service you will have to use a 'webHttpBinding'. Here is a link to a basic configuration we are always using for our RESTFul services. You can set the security mode to Transport when secure transport (f.e.: https) is needed.

<security mode="Transport"></security>
Share:
11,047
beaumondo
Author by

beaumondo

Updated on July 03, 2022

Comments

  • beaumondo
    beaumondo almost 2 years

    I am trying to send some JSON data to a service I have created in c# and wcf. In Fiddler my POST request is as follows

    Fiddler:

    Request Header
    User-Agent: Fiddler
    Host: localhost
    Content-Length: 29
    Content-Type: application/json; charset=utf-8
    
    Request Body
    {sn:"2705", modelCode:1702 } 
    

    Below is the service interface. That uses the WebInvoke attribute to manage the POST request

    [ServiceContract]
    public interface IProjectorService
    {
        [WebInvoke(Method="POST", UriTemplate="projectors", RequestFormat=WebMessageFormat.Json, ResponseFormat=WebMessageFormat.Json )]
        [OperationContract]
        void RecordBreakdown(Record record);
    }
    

    The implementation of the service interface takes the variables passed in to the argument and uses ado to send this data to a SQL db.

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
    public partial class ProjectorService : IProjectorService
    {
    
        public void RecordBreakdown(Record record)
        {
            //ado code to send data to db (ie do record.SerialNumber...
        }        
    }
    

    POCO object to represent the multiple parameters

    [DataContract]
    public class Record
    {      
        [DataMember]
        public string SerialNumber { get; set; }
        [DataMember]
        public int ModelCode { get; set; }
    }
    

    .svc file

    <%@ ServiceHost Language="C#" Debug="true" Service="ProjectorService" CodeBehind="~/App_Code/ProjectorService.cs" %>
    

    Web.config:

    <?xml version="1.0"?>
    <configuration>
      <appSettings>
        <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
      </appSettings>
      <system.web>
        <compilation debug="true" targetFramework="4.5"/>
        <httpRuntime targetFramework="4.5"/>
      </system.web>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior>
              <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <protocolMapping>
          <add binding="basicHttpsBinding" scheme="https"/>
        </protocolMapping>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/>
      </system.serviceModel>
      <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
        <directoryBrowse enabled="true"/>
      </system.webServer>
    </configuration>
    

    In fiddler, when I click on "execute", I receive an error 415 "unsupported media type". My current line of thought is that in my Projector service class, maybe im supposed to create a response object and send the 200 code back?!