I'm trying out the BizTalk 2013 REST functionality using the WCF-WebHttp adapter.
I'm trying to simulate the behaviour of a REST API that is publicly available - the Nordnet nExt API. In particular, I'm trying to simulate the behaviour of the login feature: an empty POST request to /[version]/login?service=[service]&auth=[blob] should return a response body with a bunch of parameters in it including a session token (the response is not important at this time as I am not implementing that yet)
I have performed the following steps:
- set up a receive port with an associated receive location
- set the receive location's adapter type to WCF-WebHttp
- set the endpoint address to /1/login/Service1.svc (for the moment I am deferring doing any URL rewriting and just referencing the mandatory WCF service definition file in the URL)
- set the HTTP method and URL mapping section to:
<BtsHttpUrlMapping> <Operation Name="Login" Method="POST" Url="?service={SERVICE}&auth={AUTH}"/> <Operation Name="Logout" Method="DELETE" Url="/{SESSION_KEY}"/> </BtsHttpUrlMapping>
- mapped the URL parameters to some relevant context properties
- used the BizTalk WCF configuration wizard to create an application in IIS matching the above parameters
Using PostMan from the local machine to submit an empty POST to http://localhost
/1/login/Service1.svc?service=foo&auth=bar yields the following error message:
<Fault
xmlns="http://schemas.microsoft.com/ws/2005/05/envelope/none">
<Code>
<Value>Sender</Value>
<Subcode>
<Value
xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:DestinationUnreachable
</Value>
</Subcode>
</Code>
<Reason>
<Text xml:lang="en-US">The message with To 'http://localhost/1/login/Service1.svc?service=foo&auth=bar' cannot be processed at the receiver, due to an AddressFilter mismatch at the EndpointDispatcher. Check that the sender and receiver's EndpointAddresses agree.</Text>
</Reason>
</Fault>
Why?
My hunch is that it is something to do with the empty POST body as some other questions about similar messages from plain WCF services suggest that the message is expected to contain a WS-Addressing 'To' field. However, this is REST, not SOAP, so this property is not relevant. Unlike most of the other questions referencing this error message, I do not have control over the service definition as the service instance is created by BizTalk through a custom host factory (this is just the way it does things, in common with all BizTalk WCF adapters)
How can I get the BizTalk-surfaced WCF service to recognise that the empty POST body is correct and route it to the correct endpoint?
edit #1:
Some progress. Applying the WebHttpBehaviour to the receive location stops this error message from appearing, but a new one has taken its place. Now the response to a POST request is:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Service</title>
<style>BODY { color: #000000; background-color: white; font-family: Verdana; margin-left: 0px; margin-top: 0px; } #content { margin-left: 30px; font-size: .70em; padding-bottom: 2em; } A:link { color: #336699; font-weight: bold; text-decoration: underline; } A:visited { color: #6699cc; font-weight: bold; text-decoration: underline; } A:active { color: #336699; font-weight: bold; text-decoration: underline; } .heading1 { background-color: #003366; border-bottom: #336699 6px solid; color: #ffffff; font-family: Tahoma; font-size: 26px; font-weight: normal;margin: 0em 0em 10px -20px; padding-bottom: 8px; padding-left: 30px;padding-top: 16px;} pre { font-size:small; background-color: #e5e5cc; padding: 5px; font-family: Courier New; margin-top: 0px; border: 1px #f0f0e0 solid; white-space: pre-wrap; white-space: -pre-wrap; word-wrap: break-word; } table { border-collapse: collapse; border-spacing: 0px; font-family: Verdana;} table th { border-right: 2px white solid; border-bottom: 2px white solid; font-weight: bold; background-color: #cecf9c;} table td { border-right: 2px white solid; border-bottom: 2px white solid; background-color: #e5e5cc;}</style>
</head>
<body>
<div id="content">
<p class="heading1">Service</p>
<p>Endpoint not found.</p>
</div>
</body>
</html>
...which is almost totally useless in itself.
Digging into the WCF trace log, the actual problem is in fact:
<TraceRecord Severity="Warning" Channel="Analytic" xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord">
<TraceIdentifier>http://msdn.microsoft.com/en-US/library/System.ServiceModel.Diagnostics.TraceHandledException.aspx</TraceIdentifier>
<Description>Handling an exception. Exception details: System.InvalidOperationException: The incoming HTTP request's URI 'http://localhost/1/login/Service1.svc?service=foo&auth=bar' does not match any service operation.</Description>
<AppDomain>/LM/W3SVC/1/ROOT/1/login-2-130560354135646221</AppDomain>
<Exception>
<ExceptionType>System.InvalidOperationException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>The incoming HTTP request's URI 'http://localhost/1/login/Service1.svc?service=foo&auth=bar' does not match any service operation.</Message>
<StackTrace>
at System.Runtime.Diagnostics.EtwDiagnosticTrace.WriteExceptionToTraceString(XmlTextWriter xml, Exception exception, Int32 remainingLength, Int32 remainingAllowedRecursionDepth)
at System.Runtime.Diagnostics.EtwDiagnosticTrace.ExceptionToTraceString(Exception exception, Int32 maxTraceStringLength)
at System.Runtime.Diagnostics.EtwDiagnosticTrace.GetSerializedPayload(Object source, TraceRecord traceRecord, Exception exception, Boolean getServiceReference)
at System.Runtime.TraceCore.HandledExceptionWarning(EtwDiagnosticTrace trace, String param0, Exception exception)
at System.ServiceModel.Dispatcher.HttpUnhandledOperationInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)
at System.ServiceModel.Dispatcher.ChannelHandler.DispatchAndReleasePump(RequestContext request, Boolean cleanThread, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.HandleRequest(RequestContext request, OperationContext currentOperationContext)
at System.ServiceModel.Dispatcher.ChannelHandler.AsyncMessagePump(IAsyncResult result)
at System.Runtime.ActionItem.DefaultActionItem.TraceAndInvoke()
at System.Runtime.ActionItem.CallbackHelper.InvokeWithoutContext(Object state)
at System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
at System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
</StackTrace>
<ExceptionString>System.InvalidOperationException: The incoming HTTP request's URI 'http://localhost/1/login/Service1.svc?service=foo&auth=bar' does not match any service operation.</ExceptionString>
</Exception>
</TraceRecord>
So the question remains: Why?
edit #2:
I downloaded and installed the WCF-WebHttpAdapter Sample code to see what that did differently - and disappointingly it didn't include a WebHttp receive location, but it did prompt me to try a few more things. I tried to copy the WSHttp receive location that was already there in order to get that application to work RESTfully, and I found that:
- The WebHttpBehaviour seems to be a red herring
- Removing the query parameters and replacing them with simple /route/segments seems to work as expected - I now get service faults and suspended messages, which indicate that a message is getting through the WCF endpoint.
So then the question is now the following: Does the WCF-WebHttp adapter support query parameters correctly, or is this feature broken?