I am trying to use IDispatchMessageInspector in a WCF service implementation to access custom header values.
Something like:
public class MyService : IMyService
{
public List<string> GetNames()
{
var headerInspector = new CustomHeaderInspector();
// Where do request & client channel come from?
var values = headerInspector.AfterReceiveRequest(ref request, clientChannel, OperationContext.Current.InstanceContext);
}
}
I've implemented my own IDispatchMessageInspector class.
public class CustomHeaderInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
var prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
var userName = prop.Headers["Username"];
return userName;
}
}
How do I pass
System.ServiceModel.Channels.Message and
System.ServiceModel.IClientChannel
to AfterReceiveRequest called from the service implementation?
EDIT:
Many articles like this one or this one, give examples on how to implement your own ServiceBehavior. So your service implementation looks like this:
[MyCustomBehavior]
public class MyService : IMyService
{
public List<string> GetNames()
{
// Can you use 'MyCustomBehavior' here to access the header properties?
}
}
So with this, can I access MyCustomBehavior somehow within the service operation method to access custom header values?
You have to configure the
<extensions>
<behaviorExtensions>
<add
name="serviceInterceptors"
type="CustomHeaderInspector , MyDLL, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
/>
</behaviorExtensions>
</extensions>
Then the extension will be handled in your WCF stack. The service itself has no notion of the serviceInterceptors and you do not have to do something like in your first code block. The WCF stack will inject you Inspector.
MSDN: system.servicemodel.dispatcher.idispatchmessageinspector
I'm using IClientMessageInspector for same goal.
Here is how you can apply them from code:
var serviceClient = new ServiceClientClass(binding, endpointAddress);
serviceClient.Endpoint.Behaviors.Add(
new MessageInspectorEndpointBehavior<YourMessageInspectorType>());
/// <summary>
/// Represents a run-time behavior extension for a client endpoint.
/// </summary>
public class MessageInspectorEndpointBehavior<T> : IEndpointBehavior
where T: IClientMessageInspector, new()
{
/// <summary>
/// Implements a modification or extension of the client across an endpoint.
/// </summary>
/// <param name="endpoint">The endpoint that is to be customized.</param>
/// <param name="clientRuntime">The client runtime to be customized.</param>
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new T());
}
/// <summary>
/// Implement to pass data at runtime to bindings to support custom behavior.
/// </summary>
/// <param name="endpoint">The endpoint to modify.</param>
/// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
// Nothing special here
}
/// <summary>
/// Implements a modification or extension of the service across an endpoint.
/// </summary>
/// <param name="endpoint">The endpoint that exposes the contract.</param>
/// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
// Nothing special here
}
/// <summary>
/// Implement to confirm that the endpoint meets some intended criteria.
/// </summary>
/// <param name="endpoint">The endpoint to validate.</param>
public void Validate(ServiceEndpoint endpoint)
{
// Nothing special here
}
}
And here is sample implementation of MessageInspector I'm using to pass client version to server, and retrieve server version in custom headers:
/// <summary>
/// Represents a message inspector object that can be added to the <c>MessageInspectors</c> collection to view or modify messages.
/// </summary>
public class VersionCheckMessageInspector : IClientMessageInspector
{
/// <summary>
/// Enables inspection or modification of a message before a request message is sent to a service.
/// </summary>
/// <param name="request">The message to be sent to the service.</param>
/// <param name="channel">The WCF client object channel.</param>
/// <returns>
/// The object that is returned as the <paramref name="correlationState " /> argument of
/// the <see cref="M:System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply(System.ServiceModel.Channels.Message#,System.Object)" /> method.
/// This is null if no correlation state is used.The best practice is to make this a <see cref="T:System.Guid" /> to ensure that no two
/// <paramref name="correlationState" /> objects are the same.
/// </returns>
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
request.Headers.Add(new VersionMessageHeader());
return null;
}
/// <summary>
/// Enables inspection or modification of a message after a reply message is received but prior to passing it back to the client application.
/// </summary>
/// <param name="reply">The message to be transformed into types and handed back to the client application.</param>
/// <param name="correlationState">Correlation state data.</param>
public void AfterReceiveReply(ref Message reply, object correlationState)
{
var serverVersion = string.Empty;
var idx = reply.Headers.FindHeader(VersionMessageHeader.HeaderName, VersionMessageHeader.HeaderNamespace);
if (idx >= 0)
{
var versionReader = reply.Headers.GetReaderAtHeader(idx);
while (versionReader.Name != "ServerVersion"
&& versionReader.Read())
{
serverVersion = versionReader.ReadInnerXml();
break;
}
}
ValidateServerVersion(serverVersion);
}
private static void ValidateServerVersion(string serverVersion)
{
// TODO...
}
}
public class VersionMessageHeader : MessageHeader
{
public const string HeaderName = "VersionSoapHeader";
public const string HeaderNamespace = "<your namespace>";
private const string VersionElementName = "ClientVersion";
public override string Name
{
get { return HeaderName; }
}
public override string Namespace
{
get { return HeaderNamespace; }
}
protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
{
writer.WriteElementString(
VersionElementName,
Assembly.GetExecutingAssembly().GetName().Version.ToString());
}
}
I believe you don't need to implement custom IDispatchMessageInspector to retrieve custom headers, it can be done from service operation method like this:
var mp = OperationContext.Current.IncomingMessageProperties;
var property = (HttpRequestMessageProperty)mp[HttpRequestMessageProperty.Name];
var userName = property.Headers["Username"];
It makes sense to implement custom dispatch message inspector if you want to abort message processing, for example if credentials are missing - you can just throw FaultException in this case.
But if you still want to pass value from dispatch message inspector to service operation method - probably it can be passed through some singleton along with call identifier (session id), to be extracted later by method, or using wcf extensions
What I did to access the details I set the following inside IDispatchMessageInspector.AfterReceiveRequest
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(username, "Membership Provider"), roles);
I've omitted the authentication code from this.
To access the value from the service method, you can call
Thread.CurrentPrincipal.Identity.Name
On the MSDN page that you linked to there is also a description how an inspector can be inserted and also an example of that. To quote:
Typically, message inspectors are inserted by a service behavior, an endpoint behavior, or a contract behavior. The behavior then adds the message inspector to the DispatchRuntime.MessageInspectors collection.
Later on you have the following examples:
Implementing custom IDispatchMessageInspector
Implementing custom IServiceBehavior that adds the inspector to the runtime.
Configuration of the behavior via .config file.
That should be enough to get you started. Otherwise feel free to ask :)
If you just want to get access to headers from within your service, you could try OperationContext.Current.IncomingMessageHeaders.
Related
I have been introduced to an already existing C# ASP.Net Web API project a week ago. It returns all the data in JSON and Pascal Case, and this data is used by websites using React.Js which are case sensitive.
During the last week, after a commit that changed litteraly nothing on the API project (it only added translations for the Web Client project in the solution), suddenly the project started returning JSON in Camel case and not in Pascal Case anymore.
The problem is not like this post, I don't send to the API camel instead of pascal, the API sends camel instead of pascal.
I've been searching how to fix it but there are two specifities to the project that makes it difficult to find an answer :
The project uses ASP.Net and not ASP.Net Core, making posts like this or this not helpful
The project uses NancyFx 2.0.0, so it doesn't have any starting file (like the startup class in core projects, or .asax file) but uses a custom bootstrapper (see code down below)
Bootstrapper
public class AppBootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
log4net.Config.XmlConfigurator.Configure();
base.ApplicationStartup(container, pipelines);
pipelines.BeforeRequest.AddItemToStartOfPipeline(ctx =>
{
if (ctx != null)
{
Log.Request(ctx.Request.GetHashCode(), ctx.Request.Method, ctx.Request.Path, ctx.Request.UserHostAddress, ctx.Request.Headers.UserAgent);
}
return null;
});
pipelines.AfterRequest.AddItemToEndOfPipeline(ctx =>
{
if (ctx != null)
{
Log.Response(ctx.Request.GetHashCode(), ctx.Response.StatusCode);
}
});
}
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
pipelines.AfterRequest.AddItemToEndOfPipeline(ctx =>
{
ctx.Response.WithHeader("Access-Control-Allow-Origin", "*")
.WithHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,HEAD,OPTIONS")
.WithHeader("Access-Control-Allow-Headers", "Accept, Origin, Content-type, Authorization");
});
// Gzip management, useless for this post
}
JsonSerializer
internal class JsonNetSerializer : ISerializer
{
private readonly JsonSerializer _serializer;
/// <summary>
/// Initializes a new instance of the <see cref="JsonNetSerializer"/> class.
/// </summary>
public JsonNetSerializer()
{
_serializer = JsonSerializer.CreateDefault();
}
/// <summary>
/// Initializes a new instance of the <see cref="JsonNetSerializer"/> class,
/// with the provided <paramref name="serializer"/>.
/// </summary>
/// <param name="serializer">Json converters used when serializing.</param>
public JsonNetSerializer(JsonSerializer serializer)
{
_serializer = serializer;
}
/// <summary>
/// Whether the serializer can serialize the content type
/// </summary>
/// <param name="mediaRange">Content type to serialise</param>
/// <returns>True if supported, false otherwise</returns>
public bool CanSerialize(MediaRange mediaRange)
{
return JsonHelpers.IsJsonType(mediaRange);
}
/// <summary>
/// Gets the list of extensions that the serializer can handle.
/// </summary>
/// <value>An <see cref="IEnumerable{T}"/> of extensions if any are available, otherwise an empty enumerable.</value>
public IEnumerable<string> Extensions
{
get { yield return "json"; }
}
/// <summary>
/// Serialize the given model with the given contentType
/// </summary>
/// <param name="mediaRange">Content type to serialize into</param>
/// <param name="model">Model to serialize</param>
/// <param name="outputStream">Output stream to serialize to</param>
/// <returns>Serialised object</returns>
public void Serialize<TModel>(MediaRange mediaRange, TModel model, Stream outputStream)
{
using (var writer = new JsonTextWriter(new StreamWriter(new UnclosableStreamWrapper(outputStream))))
{
_serializer.Serialize(writer, model);
}
}
}
I have looked for solutions and so long I've found some sites talking about Owin, but it seems that I have to use a owin-based server (like Nowin) to host the ASP.Net application. The problem is that I have literally never used/heard about it and I lack of time to learn how to use it. On top of that I'm not sure at all that Nowin's Start<TContext>() function will be useful to modify the API's return formatting...
Every other solution I've found was leading to ASP.Net Core technology, but nothing for ASP.Net
We have 3 cleints namely- TestingClient, ITestingClient and ITestingClientExtension. Ideally we would like to assign a variable in our winform. In order to assign the variable we declared
TestingClient client = new TestingClient(new Uri("https://rserver.contoso.com:12800")); We get an error stating delegation handler is protected. How do we establish this connection. Thank you
TestingClient.cs begins with this:
{
/// <summary>
/// The base URI of the service.
/// </summary>
public Uri BaseUri { get; set; }
/// <summary>
/// Gets or sets json serialization settings.
/// </summary>
public JsonSerializerSettings SerializationSettings { get; private set; }
/// <summary>
/// Gets or sets json deserialization settings.
/// </summary>
public JsonSerializerSettings DeserializationSettings { get; private set; }
/// <summary>
/// Subscription credentials which uniquely identify client subscription.
/// </summary>
public ServiceClientCredentials Credentials { get; private set; }
/// <summary>
/// Initializes a new instance of the TestingClient class.
/// </summary>
/// <param name='handlers'>
/// Optional. The delegating handlers to add to the http client pipeline.
/// </param>
protected TestingClient(params DelegatingHandler[] handlers) : base(handlers)
{
this.Initialize();
}
/// <summary>
/// Initializes a new instance of the TestingClient class.
/// </summary>
/// <param name='rootHandler'>
/// Optional. The http client handler used to handle http transport.
/// </param>
/// <param name='handlers'>
/// Optional. The delegating handlers to add to the http client pipeline.
/// </param>
protected TestingClient(HttpClientHandler rootHandler,params DelegatingHandler[]handlers):base(rootHandler,handlers)
{
this.Initialize();
}```
In a nutshell, you should use one of the public constructors rather than your current attempt to use one of the protected ones. Here's a screenshot (with public constructors in yellow) from a recent client I generated for that APIs, in the same way yours was generated ("Add REST Client").
For example, your code will need to look more like
Uri u = new Uri("https://rserver.contoso.com:12800")
ServiceCredential sc = ...create a relevant credential...
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fill it in
TestingClient client = new TestingClient(u, sc);
I've tried Dependency injection, but that always gives me a HttpContextAccessor.Current or ActionContext as null because I'm not in a request state (I think). So how can I get this context to just take a view, transform it to a html string (with Model if necessary) and throw it back in JS ? I even tried to call directly the Controller action, but it always gives HttpContext as null... I'm using Asp.NET Core 3.
Please, if someone has been going through, help me :-)
Thanks,
Edit:
I have an asp.net core based on Electron.net for a desktop application. I use a lot of IPC communication to retrieve data from backend c# using Electron.IpcMain.On. I register an action as listener in c# in a class. The main problem is that this class is really outside a normal HttpRequest or a Controller. Here is some sample code:
IpcBase Class
public abstract class IpcBase: IBaseIpcCommunicationClass
{
/// <summary>
/// Find a way to get rid of this and take more than the first window
/// </summary>
protected static BrowserWindow _mainWindow = Electron.WindowManager.BrowserWindows.First();
/// <summary>
/// Send an ipcEvent with a parameter class
/// </summary>
/// <param name="parameters">Parameters to fill</param>
public void RegisterIpcEvent<T>(IpcRegisterModel<T> registerModel) => Electron.IpcMain.On(registerModel.key, o => registerModel.action((T)
((JObject)o).ToObject(typeof(T))));
/// <summary>
/// Send a reply inside a registerevent
/// </summary>
/// <typeparam name="T">Type of model</typeparam>
/// <param name="model">model</param>
public void SendReply<T>(IpcSendParamsModel<T> model)
{
if (!string.IsNullOrEmpty(model.replyTo))
{
Electron.IpcMain.Send(_mainWindow, model.replyTo, model);
}
}
...
}
IpcUI (to get the controller view, just like an ajax call on a controller that retrieve the view in String (I already have that, but not with Ipc)
public class IpcUI: IpcBase
{
public IpcUI(IRazorViewToStringService razorViewRenderService)
{
Console.WriteLine("IpcUI::Constructor");
RegisterIpcEvent(new IpcRegisterModel<IpcSendParamsModel<AjaxPartialModel>>("renderPartial", async (param) =>
{
var param = new IpcSendParamsModel<AjaxPartialModel>("RenderPartial")
{
key = "renderPartial",
model = new AjaxPartialModel()
{
DataModel = "{items: [{\r\n MaterialIcon: \"\",\r\n Title: \"Games\",\r\n Selectable: true,\r\n Active: true,\r\n Key: \"GAMES\",\r\n BadgeCaption: \"new\",\r\n BadgeValue: \"123\",\r\n BadgeColor: \"red darken-1\",\r\n BadgePartialLink: \"\",\r\n BadgeContainerLink: \"\",\r\n BadgeModelLink: \"\",\r\n PartialLink: \"Home/Index\",\r\n ContainerLink: \"#body-content\",\r\n ModelLink: \"\"\r\n }] }".JsonDeserialize<MenuModelHeader>(),
PartialName = "PartialViews/_TopMenu"
}
};
try
{
param.results =
await razorViewRenderService.CreateAndResolveInstanceFromGeneric().RenderViewToStringAsync($"~/Views/{param.model.PartialName}.cshtml",
param.model.DataModel);
}
catch (Exception e)
{
IpcClasses.ExceptionManager.SendException(this, e, $"IpcUI params: {param.model.JsonSerialize()}");
}
}));
}
}
Razor Service (Mostly taken from here Generate string from view)
Added in startup:
services.AddHttpContextAccessor();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddScoped<IRazorViewToStringService, RazorRazorViewToStringService>();
When I create an instance of IpcUI, DI gives me the service, but without any HttpContext or ActionContext... Sorry for the lack of information from my last edit :-). Hope it is a bit more specific.
Oh ! I forgot something, IpcUI is created at runtime not with a new (because that don't work) but with a custom extension function that retrieves the IServiceProvider for DI:
In startup
ExtensionsUtils.ServiceProvider = app.ApplicationServices;
In ExtensionsUtils
/// <summary>
/// This is called in configure services to get the collection of services in an extension static class
/// </summary>
public static IServiceProvider ServiceProvider { get; set; }
/// <summary>
/// Create a reference from type T with DI
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="o"></param>
/// <returns></returns>
public static T CreateAndResolveInstanceFromGeneric<T>(this T o)
{
return (T)ActivatorUtilities.CreateInstance<T>(ServiceProvider);
}
Edit 2:
I have tried to access IRazorViewToStringService from a real controller constructor and it's null again... What Am I doing wrong ???
private readonly IRazorViewToStringService _razorViewRenderService;
public BaseController(IRazorViewToStringService razorViewRenderService)
{
_razorViewRenderService = razorViewRenderService;
}
...
/// <summary>
/// Return a success http code and a View rendered as string
/// </summary>
/// <param name="ViewName">Name of MVC View (or PartialView)</param>
/// <param name="Model">Model to pass if any</param>
/// <returns>JSON: { result: "type", description: "Html Code" }</returns>
public async Task<ActionResult> CheckAndReturnView(string ViewName, object Model = null)
{
return Ok(await _razorViewRenderService.RenderViewToStringAsync(ViewName, Model));
}
We are consuming an ASMX service by adding a WCF "Service Reference" to our project. When we do this, by default it is supposed to use the DataContractSerializer and if something goes wrong, it will fall back to the XmlSerializer.
I've tried forcing the DataContractSerializer when generating the proxy classes, but when I do that, they are incomplete and missing all of the custom classes used by the webservice (leaving only the interface for the Soap, SoapChannel, and the SoapClient class).
Well, something is going wrong and it is falling back to the use the XmlSerializer. I do not see any errors or warnings when I generate the reference.
How can I find out what is causing the DataContractSerializer to fail and fall back to the XmlSerializer?
Long story short is that we were unable to force VS to use the DataContractSerializer. Instead we ended up writing our own WCF Service Contracts that represented the webservice. When we consume the service we are instead creating the ChannelFactory generically by using our OWN Service Contracts. Below is the code that we used to create the channel.
/// <summary>
/// A generic webservice client that uses BasicHttpBinding
/// </summary>
/// <remarks>Adopted from: http://blog.bodurov.com/Create-a-WCF-Client-for-ASMX-Web-Service-Without-Using-Web-Proxy/
/// </remarks>
/// <typeparam name="T"></typeparam>
public class WebServiceClient<T> : IDisposable
{
private readonly T channel;
private readonly IClientChannel clientChannel;
/// <summary>
/// Use action to change some of the connection properties before creating the channel
/// </summary>
public WebServiceClient(string endpointUrl, string bindingConfigurationName)
{
BasicHttpBinding binding = new BasicHttpBinding(bindingConfigurationName);
EndpointAddress address = new EndpointAddress(endpointUrl);
ChannelFactory<T> factory = new ChannelFactory<T>(binding, address);
this.clientChannel = (IClientChannel)factory.CreateChannel();
this.channel = (T)this.clientChannel;
}
/// <summary>
/// Use this property to call service methods
/// </summary>
public T Channel
{
get { return this.channel; }
}
/// <summary>
/// Use this porperty when working with Session or Cookies
/// </summary>
public IClientChannel ClientChannel
{
get { return this.clientChannel; }
}
public void Dispose()
{
this.clientChannel.Dispose();
}
}
For a toolkit that uses a remote WCF service, I have configured a ChannelFactory<IMyService> in a UnityContainer.
Now I want to configure this channel's endpoint behavior through code (using Unity) to apply this behavior:
<behaviors>
<endpointBehaviors>
<behavior name="BigGraph">
<dataContractSerializer maxItemsInObjectGraph="1000000" />
</behavior>
</endpointBehaviors>
</behaviors>
I found this example on MSDN (http://msdn.microsoft.com/en-us/library/ms732038.aspx)
ChannelFactory<IDataService> factory = new ChannelFactory<IDataService>(binding, address);
foreach (OperationDescription op in factory.Endpoint.Contract.Operations)
{
vardataContractBehavior = op.Behaviors.Find<DataContractSerializerOperationBehavior>() as DataContractSerializerOperationBehavior;
if (dataContractBehavior != null)
{
dataContractBehavior.MaxItemsInObjectGraph = 100000;
}
}
IDataService client = factory.CreateChannel();
but now I am stuck trying to do this in a Unity configuration. Should I look into Interception?
We are using a build policy extension in unity to add behaviors on the service host. On the client we have a ServiceFactory.
/// <summary>
/// Factory for creating application service proxies used on the workstation
/// </summary>
/// <typeparam name="TInterface">Interface for the service contract</typeparam>
public class ServiceFactory<TInterface> where TInterface : class
{
private readonly List<IEndpointBehavior> m_Behaviors = new List<IEndpointBehavior>();
/// <summary>
/// Add a behavior that is added to the proxy endpoint when the channel is created.
/// </summary>
/// <param name="behavior">An <see cref="IEndpointBehavior"/> that should be added</param>.
public void AddBehavior(IEndpointBehavior behavior)
{
m_Behaviors.Add(behavior);
}
/// <summary>
/// Creates a channel of type <see cref="CommunicationObjectInterceptor{TInterface}"/> given the endpoint address which
/// will recreate its "inner channel" if it becomes in a faulted state.
/// </summary>
/// <param name="url">The endpoint address for the given channel to connect to</param>.
public TInterface CreateChannel(string url)
{
// create the channel using channelfactory adding the behaviors in m_Behaviors
}
}
Then we configure unity with an InjectionFactory
new InjectionFactory(c =>
{
var factory = new ServiceFactory<TInterface>();
factory.AddBehavior(c.Resolve<IClientTokenBehavior>());
return factory.CreateChannel(url);
});
By doing it this way you can also resolve your behavior through unity if you have some dependencies.
I think you should add one more level of indirection so you don't need to mess with interception or something like that. This problem can be easily solved by creating a new class for wraping up the WCF channel. For instance,
public class MyServiceClient : IMyService
{
public MyServiceClient(IChannelFactory<IMyService> channel)
{
}
public void DoSomething() //DoSomething is the implementation of IMyService
{
//Initialize the behavior in the channel
//Calls channel.DoSomething
}
}