I have a Website that contains a number of webpages and some WCF services.
I have a logging IHttpModule which subscribes to PreRequestHandlerExecute and sets a number of log4net MDC variables such as:
MDC.Set("path", HttpContext.Current.Request.Path);
string ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if(string.IsNullOrWhiteSpace(ip))
ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
MDC.Set("ip", ip);
This module works well for my aspx pages.
To enable the module to work with WCF I have set aspNetCompatibilityEnabled="true" in the web.config and RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed on the service.
But when the service method is called the MDC no longer contains any of the set values. I have confirmed they are being set by putting a logging method in the PreRequestHandlerExecute.
I think the MDC is loosing the values because in the log I can see the PreRequestHandlerExecute handler method and service method calls are on separate
threads.
The post log4net using ThreadContext.Properties in wcf PerSession service suggests using log4net.GlobalContext but I think that solution would run into issues if two users hit the application at the same time as GlobalContext is shared by all threads.
Is there a way to make this work?
Rather than taking the values from the HttpContext and storing them in one of log4net's context objects, why not log the values directly from the HttpContext? See my answer to the linked question for some techniques that might work for you.
Capture username with log4net
If you go to the bottom of my answer, you will find what might be the best solution. Write an HttpContext value provider object that you can put in log4net's GlobalDiagnosticContext.
For example, you might do something like this (untested)
public class HttpContextValueProvider
{
private string name;
public HttpContextValueProvider(string name)
{
this.name = name.ToLower();
}
public override string ToString()
{
if (HttpContext.Current == null) return "";
var context = HttpContext.Current;
switch (name)
{
case "path":
return context.Request.Path;
case "user"
if (context.User != null && context.User.Identity.IsAuthenticated)
return context.User.Identity.Name;
case "ip":
string ip = context.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if(string.IsNullOrWhiteSpace(ip))
ip = context.Request.ServerVariables["REMOTE_ADDR"];
return ip;
default:
return context.Items[name];
}
return "";
}
}
In the default clause I assume the name, if it is not a specifically case that we want to handle, represents a value in the HttpContext.Current.Items dictionary. You could make it more generic by also adding the ability to access Request.ServerVariables and/or other HttpContext information.
You would use this object like so:
Somewhere in your program/web site/service, add some instances of the object to log4net's global dictionary. When log4net resolves the value from the dictionary, it will call ToString before logging the value.
GDC.Set("path", new HttpContextValueProvider("path"));
GDC.Set("ip", new HttpContextValueProvider("ip"));
Note, you are using log4net's global dictionary, but the objects that you are putting in the dictionary are essentially wrappers around the HttpContext.Current object, so you will always be getting the information for the current request, even if you are handling simultaneous requests.
Good luck!
Related
I got a CXF OSGi Web service (based on the example demo in servicemix: https://github.com/apache/servicemix/tree/master/examples/cxf/cxf-jaxws-blueprint)
The Web service works fine and i call all the available implemented methods of the service.
My question is how can i retrieve the request inside a WS method and parse in a string XML format.
I have found that this is possible inside interceptors for logging, but i want also to the WS-Request inside my methods.
For storing the request in the database I suggest to extend the new CXF message logging.
You can implement a custom LogEventSender that writes into the database.
I had similar requirement where I need to save data into DB once method is invoked. I had used ThreadLocal with LoggingInInterceptor and LoggingOutInterceptor. For example in LoggingInInterceptor I used to set the message into ThreadContext and in webservice method get the message using LoggingContext.getMessage() and in LoggingOutInterceptor I used to removed the message(NOTE: Need to be careful here you need to explictly remove the message from thread context else you will end up with memory leak, and also incase of client side code interceptors get reversed.
public class LoggingContext {
private static ThreadLocal<String> message;
public static Optional<String> getMessage() {
return Optional.ofNullable(message.get());
}
public static void setMessage(final String message) {
LoggingContext.message = new ThreadLocal<>();
LoggingContext.message.set(message);
}
}
Not an answer to this question but i achieved to do my task by using JAXB in the end and do some manipulations there.
Maybe I'm thinking about this wrong, but I'm trying to create a custom attribute for our CMS to handle auth checks.
https://gist.github.com/sitefinitysteve/62ab761256a64a84d8a6#file-sitefinityjwt-cs-L39
So if this service is called from within the CMS from a logged in user, user data is all there for the service method already.
But in the context of being called from an app, the user is technically Anonymous, however I can decode the token and get the user just fine... but not sure how to like pass that over to the service.
Am I just maybe looking at this wrong, and the proper thing to do is to call a CMS API method to just log that person in (seems slow if I already have the persons user object from line 33, and the service context expires instantly.
Use Request.Items Dictionary
You would use the IRequest.Items dictionary for any data you want to pass throughout ServiceStack's Request Pipeline:
//RequestFilter:
req.Items["info"] = new MyRequestInfo { ... };
In Service:
var info = (MyRequestInfo)base.Request.Items["info"];
Have DTO's share common interface
Another option for adding extra info to your Service is to have Request DTO's implement an interfaces, e.g:
public interface IHasInfo
{
MyRequestInfo Info { get; set; }
}
Which you could then populate in your Request Filter, e.g:
((MyRequestInfo)dto).Info = new MyRequestInfo { ... };
Access in Service like any other DTO property, e.g:
public object Any(Request request)
{
var info = request.Info;
}
I am using ServiceStack's SocialBootstrapApi and it contains a class CustomUserSession that I can use to override the OnRegistered method. I want to override it because I am attempting to obtain information about the registration so that I can publish an event that a new user has registered. This handler provides an instance of the RegistrationService that handled the registration but not anything about the registration request itself or the resulting UserAuth instance. For instance, I'd like to get the e-mail address used to register.
public override void OnRegistered(IServiceBase registrationService)
{
base.OnRegistered(registrationService);
// Ideally, I could do get the registered user's primary e-mail address from the UserAuth instance.
var primaryEmail = ((RegistrationService) registrationService)
.UserAuthRepo
.GetUserAuth(this, null) //<--- 'this' is a mostly empty session instance
.PrimaryEmail;
}
This of course doesn't work because the session instance I'm using for the GetUserAuth call doesn't contain any of the necessary authentication information to be useful for looking up the user's authentication information. So GetUserAuth returns null as you would expect. So how should I go about obtaining this information? Would it be incorrect design for the OnRegistered handler to be passed the UserAuth instance created by the RegistrationService?
public interface IAuthSession
{
...
void OnRegistered(IServiceBase registrationService, UserAuth userAuth); // <-- new signature
...
}
That would be convenient! :)
Or perhaps there's another way to go about this?
Thanks in advance.
So how should I go about obtaining this information?
You should be able to access all the data of the Registration request via the registrationService. You just have to do a little digging and casting...
public override void OnRegistered(IServiceBase registrationService)
{
base.OnRegistered(registrationService);
var requestContext = (HttpRequestContext)registrationService.RequestContext;
var dto = ((Registration)requestContext.Dto);
var primaryEmail = dto.Email;
}
Would it be incorrect design for the OnRegistered handler to be passed the UserAuth instance created by the RegistrationService?
I'll leave design decisions to the professionals. The above code should work. The casting seems a bit ugly but all the necessary data is there.
I do not like hack into SS, so I chose to select user auth info from UserAuth collection by dto.UserName
Some months back I was working on a project to display the WCF methods and thier parameters in a dropdown. At that time I was creating a proxy using Add Service Reference and hardcoded the service interface in the code.
How can I show all the methods that are available in my WCF in a dropdown
But when I try to create the proxy dynamically to do the same, the below code doesn't work. Please help me to show only the methods that was defined by me.
// Using Dynamic Proxy Factory by Vipul Modi # Microsoft
DynamicProxyFactory factory = new DynamicProxyFactory(txtService.Text);
// endpoints.
string sContract = "";
foreach (ServiceEndpoint endpoint in factory.Endpoints)
{
sContract = endpoint.Contract.Name; //this is the service interface name, IAccountInfoService
}
DynamicProxy proxy = factory.CreateProxy(sContract);
Type proxyType = proxy.ProxyType;
MethodInfo[] methods = proxyType.GetMethods();
foreach (var method in methods)
{
//if (method.GetCustomAttributes(typeof(OperationContractAttribute), true).Length == 0)
// continue;
string methodName = method.Name;
ddlMethods.Items.Add(methodName);
}
The code commented method.GetCustomAttributes(typeof(OperationContractAttribute), true).Length doesn't work. It doesn't show any method. If I comment it out, then the result is all methods and variables. I want to restrict it to only user defined methods.
i dont know anything about DynamicProxyFactory but looking at http://blogs.msdn.com/b/vipulmodi/archive/2006/11/16/dynamic-programming-with-wcf.aspx it makes me think that
1) the proxy doesnt actually emit methods with the attribute. it doesn't seem to have a need to though i suppose you could tweak the code on your own to make that happen.
2) if you just want a list of method names, it seems you can get that from factory.Contracts
I would like a method on my in my domain service similar to:
public SystemState GetSystemStatus()
{
return new SystemStatus
{
InterestingStatusValue1 = 1223,
OtherInterstingStatusValue = "abc",
}
}
That doesn't work. Nothing is auto-generated for the Silverlight client app. Howerver if I make this an IQueryable method, then I get something generated on the client. I'll get a SystemStates property and a Query method on the context object.
Is there no way to make this a simple WCF call? I suppose I could a WCF Silverlight Enabled service to my RIA Web site, and then setting a Service Reference (that can't be right?) (and why can't I see the Services Reference in the Silverlight app?)
On first blush it seems that RIA services forces a very data-centric/easy CRUD which is great for table editors, but not so much for LOB applications that go behind drag on a datagrid and you're done.
You can return just one entity using an attribute (assuming that SystemState is your entity):
Ex:
[Query(IsComposable = false)]
public SystemState GetSystemStatus()
{
return new SystemStatus
{
InterestingStatusValue1 = 1223,
OtherInterstingStatusValue = "abc",
}
}
Remember that this is still a query and Ria Services will generate a method in your DomainContext like:
EntityQuery<SystemState> GetSystemStatusQuery()
Use it like a normal EntityQuery, but keep in mind that you can't perform query operations (sorting or filtering) on the returned object.
If you want to execute an operation on server, try using the [Invoke] attribute. Ex:
[Invoke]
public SystemState GetSystemStatus()
{
return new SystemStatus
{
InterestingStatusValue1 = 1223,
OtherInterstingStatusValue = "abc",
}
}
I don't know how complex your return type can be, but I guess if it can be serialized, it will work (not sure).