HttpRequestMessage properties in Azure function - asp.net-core

When my web request is run, some properties that are in the Properties property of HttpRequestMessage are from the System.Web namespace, although the application I am developing is .NET Core 2 Azure function.
I would like to use one object from the collection, MS_HttpContext which is of type System.Web.HttpContextWrapper. Is it possible to use this object and somehow cast to it? This object seems to be from standard .NET framework.
[FunctionName("Track")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "track")]HttpRequestMessage req, TraceWriter log, ExecutionContext context)
{
// HttpContextWrapper is from System.Web, so this does not work
var ctx = (HttpContextWrapper)req.Properties["MS_HttpContext"]
}
EDIT: my question is slightly different than my previos question (Get remote IP address in Azure function), because it asks how can classes from System.Web namespace be accessed from a .NET Core application. Although I admit the desired result is the same in both questions, to get the remote IP address.

Alright, not that I know much how this works, but reflection seems to work:
private static string GetRemoteIpAddress(HttpRequestMessage req)
{
if (req.Properties.ContainsKey("MS_HttpContext") == false)
{
return null;
}
var httpContext = req.Properties["MS_HttpContext"];
var pi = httpContext.GetType().GetProperty("Request");
var request = pi.GetValue(httpContext);
pi = request.GetType().GetProperty("UserHostAddress");
var address = pi.GetValue(request);
return address == null ? null : address.ToString();
}

Related

.NET Core service startup configuration

I've been assigned to upscale a project built by a former coworker. I'm not a .NET Core specialist, I understand most of it as is similar to any other language, but I'm having trouble understanding the Fluent configuration made at startup.
At the Startup.cs, there is this function declared:
public void Configure(IApplicationBuilder app)
At some point, there is an initialzation of a service that listens for something. I can manage that from the already initialized class/service, but I'd like to understand what is this:
app.UseRawRequestRequestBodyHandler(options => options
.Handlers
.AddRange(new[] {
new RawRequestHandler
{
ContentType = NotificationSubscriber.ContentType,
StartSegments = NotificationSubscriber.StartSegments,
Response = "[OK]",
Endpoint = new Uri(_configManager.Client.BaseAddress, "v1/payments").ToString(),
ModifyRequestBodyAsyncFunc = async (handler, context, bodyContent) =>
{
using (var scope = app.ApplicationServices.CreateScope())
{
var subscriber = scope.ServiceProvider
.GetRequiredService<INotificationSubscriber>();
await subscriber.QueueAndAkcknowledgeAsync(handler, context, bodyContent);
}
return bodyContent;
}
},
I'm having special trobule with the ModifyRequestBodyAsyncFunc function, that is declared (in the interface) like this:
public Func<RawRequestHandler, HttpContext, string, Task<string>> ModifyRequestBodyAsyncFunc { set; get; }
Also, I don't get how or where are initialized handler, context and bodyContent (RawRequestHandler handler, HttpContext context, string bodyContent as declared in the NotificationSubscriber class). I pressume these are loaded by Dependency Injection, but It would be different for other DI implementations I've seen.
Any help would be appreciated; also, I take reading recommendations.
Thank you very much!
I'm having special trobule with the ModifyRequestBodyAsyncFunc function
This is a special C# type, called a delegate. The delegate in question is a function that accepts RawRequestHeader, HttpContext, string and returns a Task<string>, which tells us that it's asynchronous.
Next, this is a syntax to create an anonymous async function and assign it to the delegate property:
/* SomeProp */ = async (handler, context, bodyContent) =>
{
// ...
return bodyContent;
}
Also, I don't get how or where are initialized handler, context and bodyContent
The .UseRawRequestRequestBodyHandler(...) registers a middleware which is basically a piece of code which runs for every request. So, somewhere inside that middleware, there is code that has access to said parameters and probably passes them like that:
// the params are not necessarily named exactly like this, only the types must match
string content = await rawRequestHeader.ModifyRequestBodyAsyncFunc(handler, context, bodyContent);
Notice the await keyword (we must await asynchronous functions) and also the fact that the delegate is invoked just like a normal method.

How to dynamically resolve controller with endpoint routing?

Upgrading to asp.net core 2.2 in my hobby project there is a new routing system I want to migrate to. Previously I implemented a custom IRouter to be able to set the controller for the request dynamically. The incoming request path can be anything. I match the request against a database table containing slugs and it looks up the a matching data container class type for the resolved slug. After that I resolve a controller type that can handle the request and set the RouteData values to the current HttpContext and passing it along to the default implementation for IRouter and everything works ok.
Custom implementaion of IRouter:
public async Task RouteAsync(RouteContext context)
{
var requestPath = context.HttpContext.Request.Path.Value;
var page = _pIndex.GetPage(requestPath);
if (page != null)
{
var controllerType = _controllerResolver.GetController(page.PageType);
if (controllerType != null)
{
var oldRouteData = context.RouteData;
var newRouteData = new RouteData(oldRouteData);
newRouteData.Values["pageType"] = page.PageType;
newRouteData.Values["controller"] = controllerType.Name.Replace("Controller", "");
newRouteData.Values["action"] = "Index";
context.RouteData = newRouteData;
await _defaultRouter.RouteAsync(context);
}
}
}
A controller to handle a specific page type.
public class SomePageController : PageController<PageData>
{
public ActionResult Index(PageData currentPage)
{
return View("Index", currentPage);
}
}
However I got stuck when I'm trying to figure out how I can solve it using the new system. I'm not sure where I'm suppose to extend it for this behavior. I don't want to turn off the endpoint routing feature because I see an opportunity to learn something. I would aso appreciate a code sample if possible.
In ASP.NET 3.0 there is an new dynamic controller routing system. You can implement DynamicRouteValueTransformer.
Documentation is on the way, look at the github issue

How Dynamically change URL in a WCF Custom Behavior

Class is defined as follows:
public class BizTalkRESTTransmitHandler : IClientMessageInspector
I'm a method with this signature:
public object BeforeSendRequest(ref Message request, IClientChannel channel)
So I think I need to manipulate the channel object.
The reason is this is being using in BizTalk 2010 SendPort to support JSON.
I tried this so far:
if (channel.RemoteAddress.Uri.AbsoluteUri == "http://api-stage2.mypartner.com/rest/events/2/"
|| channel.RemoteAddress.Uri.AbsoluteUri == "http://api.mypartner.com/rest/events/2/")
{
//TODO - "boxout" will become a variable obtained by parsing the message
Uri newUri = new Uri(channel.RemoteAddress.Uri.AbsoluteUri + "boxout");
channel.RemoteAddress.Uri = newUri;
}
Above gives compile error: "System.ServiceModel.EndpointAddress.Uri" cannot be assigned to - it is ready only" RemoteAddress seems to be read only as well.
I have referenced these questions but they don't use channel object.
Assign a URL to Url.AbsoluteUri in ASP.NET, and
WCF change endpoint address at runtime
But they don't seem to be dealing with channel object.
Update 1: I tried the following:
//try create new channel to change URL
WebHttpBinding myBinding = new WebHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress(newURL);
ChannelFactory<IClientChannel> myChannelFactory = new ChannelFactory<IClientChannel>(myBinding, myEndpoint); //Change to you WCF interface
IClientChannel myNewChannel = myChannelFactory.CreateChannel();
channel = myNewChannel; //replace the channel parm passed to us
but it gave this error:
System.InvalidOperationException: Attempted to get contract type for IClientChannel, but that type is not a ServiceContract, nor does it inherit a ServiceContract.
IClientMessageInspector is not the right place the manipulate the Channel, you should use IEndpointBehavior instead:
From MSDN
Implements methods that can be used to extend run-time behavior for an
endpoint in either a service or client application.
Here is a simple example:
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
Uri endpointAddress = endpoint.Address.Uri;
string address = endpointAddress.ToString();
if (address == "http://api-stage2.mypartner.com/rest/events/2/"
|| address == "http://api.mypartner.com/rest/events/2/")
{
//TODO - "boxout" will become a variable obtained by parsing the message
Uri newUri = new Uri(address + "boxout");
ServiceHostBase host = endpointDispatcher.ChannelDispatcher.Host;
ChannelDispatcher newDispatcher = this.CreateChannelDispatcher(host, endpoint, newUri);
host.ChannelDispatchers.Add(newDispatcher);
}
}
Here you can read the excelent post of Carlos Figueira about IEndpointBehavior:
https://blogs.msdn.microsoft.com/carlosfigueira/2011/04/04/wcf-extensibility-iendpointbehavior/
Another alternative is to implement a simple Routing with WCF, here is link with an example:
WCF REST service url routing based on query parameters
Hope it helps.
Using the interface IEndpointBehavior, you'll have access to the ApplyClientBehavior method, which exposes the ServiceEndPoint instance.
Now you can change the value for the Address by defining a new EndpointAddress instance.
public class MyCustomEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint serviceEndpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.ClientRuntime behavior)
{
serviceEndpoint.Address = new System.ServiceModel.EndpointAddress("http://mynewaddress.com");
}
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
}
public void Validate(ServiceEndpoint serviceEndpoint)
{
}
}
I might be a bit too late but hoe it helps a bit.
I recently had a similar objective (also related to biztalk) where I needed to change the url based on some value sent on the message.
I tried using the ApplyDispatchBehavior method but it was never called and also, I couldn't see how to access the message from here so I started looking at method BeforeSendRequest (in the Inspector class).
Here is what i came up with:
object IClientMessageInspector.BeforeSendRequest(ref Message request, IClientChannel channel)
{
var queryDictionary = HttpUtility.ParseQueryString(request.Headers.To.Query);
string parameterValue = queryDictionary[this.BehaviourConfiguration.QueryParameter];
//Only change parameter value if it exists
if (parameterValue != null)
{
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
//Necessary in order to read the message without having WCF throwing and error saying
//the messas was already read
var reqAux = buffer.CreateMessage();
//For some reason the message comes in binary inside tags <Binary>MESSAGE</Binary>
using (MemoryStream ms = new MemoryStream(Convert.FromBase64String(reqAux.ToString().Replace("<Binary>", "").Replace("</Binary>", ""))))
{
ms.Position = 0;
string val = ExtractNodeValueByXPath(ms, this.BehaviourConfiguration.FieldXpath);
queryDictionary.Set(this.BehaviourConfiguration.QueryParameter, DateTime.Now.ToString("yyyyMMddHHmmssfff") + "_" +
this.BehaviourConfiguration.Message + (string.IsNullOrWhiteSpace(val) ? string.Empty : "_" + val) + ".xml");
UriBuilder ub = new UriBuilder(request.Headers.To);
ub.Query = queryDictionary.ToString();
request.Headers.To = ub.Uri;
}
}
return null;
}
So, I discovered that, messing with the request.Headers.To I could change the endpoint.
I had several problems getting the message content and most examples on the internet (showing to use the MessageBuffer.CreateNavigator or Message.GetBody< string > which was always throwing an expcetion i couldn't get around) would not give me the biztalk message but rather the soap message?... not sure but it had a node header, body and inside the body there was some base64 string which was not my biztalk message.
Also, as you can see in Convert.FromBase64String(reqAux.ToString().Replace("<Binary>", "").Replace("</Binary>", "")), I had to do this ugly replaces. I don't don't why this comes in base64, probably some WCF configuration?, but by doing it, I could then look for my value.
NOTE: I haven't fully tested this, but so far it as worked for my examples.
By the way, any idea on what can i switch my MemoryStream with so it becomes a more streaming solution?

How to implement an async wcf client for a synchronous service

Is there a way to create an async client for a synchronous WCF service without adding a service reference? This is for a .NET 4 client.
A service reference in Visual Studio is nothing else than a code generator that creates a proxy class with corresponding data elements necessary to call your web service. Of course you can hand build a proxy if you really want to go over tedious and boring work.
Maybe start by decompiling System.ServiceModel.ClientBase using .net reflector?
Do some research on ChannelFactory: http://msdn.microsoft.com/en-us/library/system.servicemodel.channelfactory.aspx
Even when implementing my own client by wrapping a ChannelFactory, I am still using the Add Service reference in another project to create the class definitions and move them into the real project. That's a good compromise.
Here's a simple async service interface:
[ServiceContract(Name = "IService")]
public interface IServiceAsync
{
[OperationContract(AsyncPattern = true)]
IAsyncResult BeginGetStuff(string someData, AsyncCallback callback, object state);
IEnumerable<Stuff> EndGetStuff(IAsyncResult result);
}
The .NET contract might look like this:
[ServiceContract]
public interface IService
{
[OperationContract]
IEnumerable<Stuff> GetStuff(string someData);
}
Then in code, assuming you use HTTP, No security and binary message encoding, something like this (Sorry I haven't compiled any of this, just typed it using some of the code I have written for projects):
//Create a binding for the proxy to use
HttpTransportBindingElement httpTransportBindingElement;
httpTransportBindingElement = new HttpTransportBindingElement();
absoluteServiceUri = new Uri(absoluteServiceUri.OriginalString + BinaryEndpointUri, UriKind.Absolute);
}
//Create the message encoding binding element - we'll specify binary encoding
var binaryMessageEncoding = new BinaryMessageEncodingBindingElement();
//Add the binding elements into a Custom Binding
var customBinding = new CustomBinding(binaryMessageEncoding, httpTransportBindingElement);
// Set send timeout
customBinding.SendTimeout = this.SendTimeout;
var factory = new ChannelFactory<IServiceAsync>(customBinding, new EndpointAddress(absoluteServiceUri, new AddressHeader[0]));
var channel = factory.CreateChannel();
channel.BeginGetStuff(Bla, results => { // Do something }, null);

Providing workflow extensions to a workflow service - WF 4.0

Greetings one and all!
I'm new to WF 4.0 and WWF in general so forgive me if this seems like a newbie type of question, but believe me I've scoured the depths of the Internet for a solution to this problem, but to no avail.
I have created a sample WF application with a custom CodeActivity that requires an extension be provided, as per below:
public sealed class PreparePizza : CodeActivity
{
public InArgument<Order> Order { get; set; }
protected override void CacheMetadata(CodeActivityMetadata metadata)
{
base.CacheMetadata(metadata);
if (this.Order == null)
metadata.AddValidationError("You must supply an Order.");
metadata.RequireExtension<IPreparePizzaExtension>();
}
// If your activity returns a value, derive from CodeActivity<TResult>
// and return the value from the Execute method.
protected override void Execute(CodeActivityContext context)
{
// Obtain the runtime value of the Text input argument
Order order = context.GetValue(this.Order);
var extension = context.GetExtension<IPreparePizzaExtension>();
extension.Prepare(order);
}
}
public interface IPreparePizzaExtension
{
void Prepare(Order order);
}
I then slot this activity into a workflow service and attempt to consume via my web app by adding a service reference. However, when I add the reference I get:
System.Activities.ValidationException: An extension of type 'PizzaMan.ActivityLibrary.IPreparePizzaExtension' must be configured in order to run this workflow.
Fair enough - of course my activity requires that I pass it an implementation of IPreparePizzaExtension - after all, I've told it to!
So my question is, how on earth do I pass this to the service? I can manage this easily enough in a console app scenario, using the WorkflowInvoker, but I cannot see any obvious way to do this via the service approach. I would assume that obviously a programmatic approach to adding the reference is what's needed, but again I'm at a loss as to precisely how to go about this.
Any help would be greatly appreciated.
Best regards
Ian
The WorkflowServiceHost has a WorkflowExtensions property where you can add the workflow extenstion. There are several ways you can do that. If you are self hosting this is easy as you create the WorkflowServiceHost. If you are usign IIS you need to create a ServiceHostFactory to configure you WorkflowServiceHost. Finally there is an option to add the workflow extension in the CacheMetadata of your activity using the metadata.AddDefaultExtensionProvider() function.
Solved it as follows, self-hosting style:
static void Main(string[] args)
{
Workflow1 workflow = new Workflow1();
// Provide some default values; note: these will be overriden once method on the service is called.
workflow.productID = -1;
Uri address = new Uri("http://localhost:1234/WorkflowService1");
WorkflowServiceHost host = new WorkflowServiceHost(workflow, address);
// Behaviours
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
host.Description.Behaviors.Remove(typeof(ServiceDebugBehavior));
host.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true });
// Persistence
var connStr = #"";
var behavior = new SqlWorkflowInstanceStoreBehavior(connStr);
behavior.InstanceCompletionAction = InstanceCompletionAction.DeleteNothing;
behavior.InstanceLockedExceptionAction = InstanceLockedExceptionAction.AggressiveRetry;
behavior.InstanceEncodingOption = InstanceEncodingOption.None;
host.Description.Behaviors.Add(behavior);
// Add extension implementations
if (!TEST_MODE)
{
host.WorkflowExtensions.Add(new MyExtension());
}
else
{
host.WorkflowExtensions.Add(new MyExtensionTest());
}
host.Faulted += new EventHandler(host_Faulted);
host.Open();
foreach (System.ServiceModel.Description.ServiceEndpoint endpoint in host.Description.Endpoints)
{
Console.WriteLine(endpoint.Address);
}
Console.WriteLine("Listening...");
Console.ReadLine();
host.Close();
}
My toolkit has configuration support for this. See http://neovolve.codeplex.com/wikipage?title=Neovolve.Toolkit.Workflow.dll%20-%201.1
There is also this method of doing things:
http://wf.codeplex.com/wikipage?title=How%20do%20I%20add%20an%20extension%20to%20a%20WCF%20Workflow%20Service?