4
votes

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}&amp;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&amp;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&amp;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[]&amp; outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&amp; rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&amp; rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&amp; 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&amp;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?

1
Looks like your request from postman is not going as POST request. In POST request, your parameters service and auth need to be part of HTTP request body and not the URL as in your case. That's the reason "no end point operation" error is coming.Vikas Bhardwaj
I have noticed that the convention seems to be that POST doesn't take URL parameters and that information should be in the request body - but since various APIs use it and it seems to be accepted elsewhere, shouldn't it be supported?Tom W
Did you check using Fiddler yr generated request, I doubt it's a POST request. as per HTTP protocol, POST request is to submit the data to server and often the data includes secured information which passing in URL is not recommended anyway. If you want to retrieve something then you should use HTTP GET method.Vikas Bhardwaj
I'm using Postman to generate test requests and yes, it is definitely a POST. I do explain quite clearly that this is what I'm doing and the reason I'm doing it, and the documentation I linked to confirms that this particular API just does it that way, regardless of what the general convention is. As the request changes the state of the server (i.e. a session exists that did not before) this request must not be a GET.Tom W
Did you figure it out? I have a similar problem, where the error goes from an AddressFilter mismatch at the EndpointDispatcher to endpoint not found.Dragomok

1 Answers

0
votes

I can see two issues here. First, you have configured GET on the receive port and you try a POST. If the request body are meant to be empty, you should probably use a GET. If you want to use POST change to POST in the configuration.

Second, when BizTalk receive the request, the reqest body is inserted to the message body. If the message body is empty, BizTalk fails. You have to create a custom pipeline on the receive port which create a message body based on the schema the orchestration is expecting. If the promoted variables are message based, they have to be inserted to the message.

When the message body is in place, it should work as expected.