Log every request to web api method to file - asp.net-mvc

My requirements are to add logging to every request and response and errors to a file. I have tried this,
public class LogRequestAndResponseHandler : DelegatingHandler
{
private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
// log request body
string requestBody = await request.Content.ReadAsStringAsync();
Log.Info(requestBody);
// let other handlers process the request
var result = await base.SendAsync(request, cancellationToken);
if (result.Content != null)
{
// once response body is ready, log it
var responseBody = await result.Content.ReadAsStringAsync();
Log.Info(responseBody);
}
return result;
}
}
Added this it to WebApiConfig of MVC5 as following,
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
log4net.Config.XmlConfigurator.Configure();
config.MessageHandlers.Add(new LogRequestAndResponseHandler());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Web config has this section,
<log4net>
<!-- file appender -->
<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="C:/logs/my_log_file.log"/>
<appendToFile value="true"/>
<rollingStyle value="Date"/>
<maxSizeRollBackups value="30"/>
<datePattern value=".yyyy-MM-dd"/>
<staticLogFileName value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>
</layout>
</appender>
<root>
<level value="DEBUG"/>
<appender-ref ref="RollingFileAppender"/>
</root>
</log4net>
Nothing is being added to file and there are no errors, is there anything I am doing wrong, it creates the file but no logging ever ?

Related

why isn't nlog saving a bad request url?

I use ASP.NET Core and NLog.Web.AspNetCore (4.3.1). NLog isn't saving a bad request url - why?
That is mine NLog.config:
<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets>
<target name="logfile" xsi:type="File" fileName="${basedir}/nlog.txt"
layout="${longdate} url: ${aspnet-request-url} | ${message}"/>
</targets>
<rules>
<logger name="*" minlevel="Warn" writeTo="logfile"/>
</rules>
</nlog>
When I have a 404 error nlog is saving:
2017-05-11 20:07:34.2466 url: | My error message
Url above is empty - why?
My Configure method in Startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
// .....
app.UseExceptionHandler("/Error/ApplicationError");
app.UseStatusCodePagesWithReExecute("/Error/Error/{0}");
// .....
loggerFactory.AddNLog();
app.AddNLogWeb();
// ......
}
My Error controller:
public class ErrorController : Controller
{
private readonly ILogger<ErrorController> _logger;
public ErrorController(ILogger<ErrorController> logger)
{
_logger = logger;
}
public IActionResult ApplicationError()
{
return View();
}
[Route("/Error/Error/{statusCode}")]
public IActionResult Error(int statusCode)
{
_logger.LogError("My error message");
return View(statusCode);
}
}
Try adding the AspnetCore extensions to your NLog configuration:
<?xml version="1.0" encoding="utf-8"?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<extensions>
<add assembly="NLog.Web.AspNetCore"/>
</extensions>
...
</nlog>
See the NLog AspnetCore documentation for more info.

log4j2 with custom appender and patternlayout not working

I am using log4j2 in my web project. I was trying to put logs directly to kafka by extending abstractAppender. As per documentation my understanding is that i can specify patternlayout for a custom appender and with that being set, my logger will send log events to kafka with formatted string but that is not happening. log4j2.xml looks like
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info" packages="com.abc.webservice.log.appender">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L- %X{sessionId}--%X{guid}- %m #]%n</pattern>
</PatternLayout>
</Console>
<Kafka name="kafka" topic="test">
<PatternLayout>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L- %X{sessionId}--%X{guid}- %m #]%n</pattern>
</PatternLayout>
<Property name="metadata.broker.list">127.0.0.1:9092</Property>
<Property name="serializer.class">kafka.serializer.StringEncoder</Property>
</Kafka>
</Appenders>
<Loggers>
<AsyncLogger name="async">
<AppenderRef ref="kafka" />
<AppenderRef ref="console" />
</AsyncLogger>
<Root level="info">
<AppenderRef ref="console" />
<AppenderRef ref="kafka" />
</Root>
<Logger name="com.abc" level="debug">
<!-- <appender-ref ref="console" level="debug"/>-->
<!--<appender-ref ref="kafka" level="debug"/>-->
<!--<appender-ref ref="console" level="error"/>-->
<appender-ref ref="kafka" level="error"/>
</Logger>
<Logger name="org.hibernate.SQL" >
<appender-ref ref="kafka" level="info" />
<appender-ref ref="console" level="info"/>
</Logger>
<Logger name="org.hibernate.type">
<appender-ref ref="console" level="info"/>
<appender-ref ref="kafka" level="info"/>
</Logger>
<Root level="info">
<AppenderRef ref="kafka"/>
<AppenderRef ref="console"/>
</Root>
</Loggers>
</Configuration>
If i use console appender then log comes in proper format but when i use custom appender, log is received without format. How can i send logs to kafka with specified paatternlayout.
Please find my appender implementation
import java.io.Serializable;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.config.Property;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.core.util.Booleans;
import org.apache.logging.log4j.message.Message;
#Plugin(name = "Kafka", category = "Core", elementType = "appender", printObject = true)
public final class KafkaAppender extends AbstractAppender {
private final Lock lock = new ReentrantLock();
private KafkaManager manager;
protected KafkaAppender(String name, Filter filter, Layout layout, boolean ignoreExceptions, KafkaManager manager) {
super(name, filter, layout, ignoreExceptions);
System.err.println("hello world hello");
this.manager = manager;
}
#PluginFactory
public static KafkaAppender createAppender(#PluginAttribute("name") final String name, #PluginElement("Filter") final Filter filter,
#PluginAttribute("ignoreExceptions") final String ignore, #PluginAttribute("topic") final String topic,
#PluginElement("Properties") final Property[] properties, #PluginElement("layout") final Layout layout) {
boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
KafkaManager kafkaManager = KafkaManager.getKafkaManager(name, topic, properties);
if (kafkaManager == null) {
return null;
}
// Layout patternLayout = PatternLayout.createLayout("%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L- %X{sessionId}--%X{guid}- %m #]%n",
// null, null, null, true, false, null, null);
// System.err.println(patternLayout.toString());
return new KafkaAppender(name, filter, layout, ignoreExceptions, kafkaManager);
}
#Override
public final void start() {
if (this.getManager() == null) {
LOGGER.error("No KafkaManager set for the appender named [{}].", this.getName());
}
super.start();
if (this.getManager() != null) {
this.getManager().startup();
}
}
#Override
public final void stop() {
super.stop();
if (this.getManager() != null) {
this.getManager().release();
}
}
public final KafkaManager getManager() {
return this.manager;
}
public void append(LogEvent event) {
this.lock.lock();
try {
String s = event.getMessage().getFormattedMessage();
Message logEvent1 = event.getMessage();
String sp = logEvent1.getFormattedMessage();
this.getManager().send(event.getMessage().getFormattedMessage());
} catch (final Exception e) {
LOGGER.error("Unable to write to kafka [{}] for appender [{}].", this.getManager().getName(), this.getName(), e);
throw new AppenderLoggingException("Unable to write to kafka in appender: " + e.getMessage(), e);
} finally {
this.lock.unlock();
}
}
#Override
public Layout<? extends Serializable> getLayout() {
Layout patternLayout = PatternLayout.createLayout("%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L- %X{sessionId}--%X{guid}- %m #]%n",
null, null, null, true, false, null, null);
return patternLayout;
}
}
In class KafkaAppender, your append method should call getLayout().toByteArray(event) to format the event.
I noticed that the sample code overrides getLayout. I would not recommend this. The AbstractAppender implementation of getLayout returns the configured layout, which allows you to control the layout in configuration without code changes.
#Override
public void append(LogEvent event) {
this.lock.lock();
try {
// let the Layout format the data in the LogEvent object
final byte[] bytes = getLayout().toByteArray(event);
// then pass the byte[] array with the formatted event to the manager
// (I assume that your manager provides this method)
manager.write(bytes, 0, bytes.length);
} catch (Exception e) {
LOGGER.error("Unable to write to kafka [{}] for appender [{}].",
this.getManager().getName(), this.getName(), e);
if (!ignoreExceptions()) {
throw new AppenderLoggingException(
"Unable to write to kafka in appender: " + e.getMessage(), e);
}
} finally {
this.lock.unlock();
}
}
// I would recommend not to override getLayout.
// The AbstractAppender implementation of getLayout returns the configured
// layout, which allows you to control the layout in configuration
// without code changes.
// #Override
// public Layout<? extends Serializable> getLayout() {...

Spring Security 3.2.3 RELEASE with JavaConfig

I have a Spring Security configured in XML that works just fine. Now, I'm trying to have it expressed in JavaConfig only so as to get rid of the XML configuration altogether.
I've looked at the reference documentation, and at many blogs and support requests, but I still cannot find the solution.
It gives me the following exception:
Could not autowire field: private org.springframework.security.web.FilterChainProxy
com.thalasoft.learnintouch.rest.config.WebTestConfiguration.springSecurityFilterChain;
Pitifully I resorted to post my own request here...
The code:
#Configuration
#ComponentScan(basePackages = { "com.thalasoft.learnintouch.rest" })
public class WebTestConfiguration {
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private FilterChainProxy springSecurityFilterChain;
}
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
public class WebInit implements WebApplicationInitializer {
private static Logger logger = LoggerFactory.getLogger(WebInit.class);
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
registerListener(servletContext);
registerDispatcherServlet(servletContext);
registerJspServlet(servletContext);
}
private void registerListener(ServletContext servletContext) {
// Create the root application context
AnnotationConfigWebApplicationContext appContext = createContext(ApplicationConfiguration.class, WebSecurityConfiguration.class);
// Set the application display name
appContext.setDisplayName("LearnInTouch");
// Create the Spring Container shared by all servlets and filters
servletContext.addListener(new ContextLoaderListener(appContext));
}
private void registerDispatcherServlet(ServletContext servletContext) {
AnnotationConfigWebApplicationContext webApplicationContext = createContext(WebConfiguration.class);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(webApplicationContext));
dispatcher.setLoadOnStartup(1);
Set<String> mappingConflicts = dispatcher.addMapping("/");
if (!mappingConflicts.isEmpty()) {
for (String mappingConflict : mappingConflicts) {
logger.error("Mapping conflict: " + mappingConflict);
}
throw new IllegalStateException(
"The servlet cannot be mapped to '/'");
}
}
private void registerJspServlet(ServletContext servletContext) {
}
private AnnotationConfigWebApplicationContext createContext(final Class... modules) {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(modules);
return appContext;
}
}
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
CustomAuthenticationProvider customAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Bean
public DelegatingFilterProxy springSecurityFilterChain() {
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
return filterProxy;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").hasRole("ROLE_ADMIN").and().httpBasic();
http.authorizeRequests().antMatchers("/admin/login", "/admin/logout", "/admin/denied").permitAll()
.antMatchers("/admin/**").hasRole("ROLE_ADMIN")
.and()
.formLogin()
.loginPage("/admin/login")
.defaultSuccessUrl("/admin/list")
.failureUrl("/admin/denied?failed=true")
.and()
.rememberMe();
http.logout().logoutUrl("/admin/logout").logoutSuccessUrl("/admin/login").deleteCookies("JSESSIONID");
}
}
The XML configuration that I hope to get rid of:
<!-- A REST authentication -->
<http use-expressions="true" pattern="/admin/**">
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
<http-basic entry-point-ref="restAuthenticationEntryPoint" />
<logout />
</http>
<!-- A form based browser authentication -->
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/admin/login" access="permitAll" />
<intercept-url pattern="/admin/logout" access="permitAll" />
<intercept-url pattern="/admin/denied" access="permitAll" />
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<form-login
login-page="/admin/login"
default-target-url="/admin/list"
authentication-failure-url="/admin/denied?failed=true"
always-use-default-target="true" />
<logout logout-success-url="/admin/login" />
<logout delete-cookies="JSESSIONID" />
</http>
<!-- A custom authentication provider on legacy data -->
<authentication-manager>
<authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>
UPDATE:
I added a Configuration directive:
#Configuration
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
and an explicit import directive:
#Import({ SecurityWebApplicationInitializer.class })
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
}
But the exception remained the exact same.
I'm running Spring Security 3.2.4.RELEASE and Spring 3.2.9.RELEASE
If you have any suggestion, it is welcomed.
I removed this bean definition from the security configuration and it seems to have solved the issue
#Bean
public DelegatingFilterProxy springSecurityFilterChain() {
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
return filterProxy;
}

Is it possible to use Spring Security (3.1.X) to get LDAP information on a user other that the one authenticated against?

I use Spring Security to authenticate a user against an Active Directory server. A CustomUserContext is also injected into the ldapAuthenticationProvider bean to provide access to additional LDAP attributes. Everything works quite well. I have no problem pulling whatever I want from the authenticated user.
The issue I have is that I want to retrieve some attributes, most specifically the email address, from the Active Directory server on a user other than the user that is logged in. Is it possible to achieve this by leveraging what I already have, or is my only option to use a totally separate method to access LDAP attributes from a different user?
[edit]
Configuration follows
security-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
<property name="url" value="ldap://xxxx.xxxx.xxx:389" />
<property name="base" value="dc=corp,dc=global,dc=xxxxx,dc=com" />
<property name="userDn" value="CN=lna.authquery,OU=LDAPGroups,OU=NorthAmerica,DC=corp,DC=global,DC=xxxxx,DC=com" />
<property name="password" value="xxxxxxx" />
<property name="pooled" value="true" />
<!-- AD Specific Setting for avoiding the partial exception error -->
<property name="referral" value="follow" />
</bean>
<bean id="ldapAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider" >
<constructor-arg>
<bean class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="contextSource" />
<property name="userSearch">
<bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg index="0" value="" />
<constructor-arg index="1" value="(sAMAccountName={0})" />
<constructor-arg index="2" ref="contextSource" />
</bean>
</property>
</bean>
</constructor-arg>
<constructor-arg>
<bean class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
<constructor-arg ref="contextSource" />
<constructor-arg value="" />
<property name="groupSearchFilter" value="(member={0})" />
<property name="searchSubtree" value="true" />
<!-- Settings below convert the adds the prefix ROLE_ to roles returned from AD -->
</bean>
</constructor-arg>
<property name="userDetailsContextMapper">
<bean class="net.xxxx.xxxxx.utilities.CustomUserDetailsContextMapper" />
</property>
</bean>
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg>
<list>
<ref local="ldapAuthenticationProvider" />
</list>
</constructor-arg>
</bean>
<sec:http pattern="/css/**" security="none"/>
<sec:http pattern="/images/**" security="none"/>
<sec:http auto-config="true" authentication-manager-ref="authenticationManager" >
<sec:intercept-url pattern="/login.jsp*" requires-channel="https" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<sec:intercept-url pattern="/**" requires-channel="https" access="IS_AUTHENTICATED_FULLY"/>
<sec:form-login login-page='/login.jsp'
default-target-url="/home.html"
authentication-failure-url="/login.jsp" />
</sec:http>
CustomeUserDetails.java
package net.xxxx.xxxx.utilities;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
public class CustomUserDetails extends User {
private static final long serialVersionUID = 1416132138315457558L;
// extra instance variables
final String fullname;
final String email;
final String title;
public CustomUserDetails(String username, String password, boolean enabled, boolean accountNonExpired,
boolean credentialsNonExpired, boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities, String fullname,
String email, String title) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired,
accountNonLocked, authorities);
this.fullname = fullname;
this.email = email;
this.title = title;
}
public String getFullname() {
return this.fullname;
}
public String getEmail() {
return this.email;
}
public String getTitle() {
return this.title;
}
}
CustomUserDetailsContextMapper.java
package net.xxxx.xxxxx.utilities;
import java.util.Collection;
public class CustomUserDetailsContextMapper implements UserDetailsContextMapper {
public UserDetails mapUserFromContext(DirContextOperations ctx,
String username, Collection<? extends GrantedAuthority> authorities) {
String fullname = "";
String email = "";
String title = "";
Attributes attributes = ctx.getAttributes();
try {
fullname = (String) attributes.get("displayName").get();
email = (String) attributes.get("mail").get();
title = (String) attributes.get("title").get();
} catch (NamingException e) {
e.printStackTrace();
}
CustomUserDetails details = new CustomUserDetails(username, "", true, true, true, true, authorities, fullname, email, title);
return details;
}
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
}
}
I finally did end up figuring out how to do this. I'm answering this in case it helps someone else who needs to do this. I'd be surprised if I'm the only one.
First I had to move my security-config.xml file out of the WEB-INF structure and put it under the spring resources directory. The contextSource bean I was able to reuse. However I could not reuse the CustomUserDetailsContextMapper.java nor the CustomUserDetails.java class as they were too specific to Spring security and not to just retrieving LDAP data from an unauthenticated user.
I ended up writing a separate class for the LDAP access that had the common contextSource autowired in. That class is below.
LdapDao.java
package net.xxxxx.xxx.dao;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.naming.directory.Attributes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.stereotype.Component;
#Component
public class LdapDao {
LdapTemplate template;
#Autowired
public LdapDao(LdapContextSource contextSource) {
template = new LdapTemplate(contextSource);
}
#SuppressWarnings("unchecked")
public Map<String, String> getUserAttributes(String username) {
Map<String, String> results = new HashMap<String, String>();
String objectClass = "samAccountName=" + username;
LinkedList<Map<String, String>> list = (LinkedList<Map<String, String>>) template.search("", objectClass, new UserAttributesMapper());
if (!list.isEmpty()) {
// Should only return one item
results = list.get(0);
}
return results;
}
private class UserAttributesMapper implements AttributesMapper {
#Override
public Map<String, String> mapFromAttributes(Attributes attributes) throws javax.naming.NamingException {
Map<String, String> map = new HashMap<String, String>();
String fullname = (String) attributes.get("displayName").get();
String email = (String) attributes.get("mail").get();
String title = (String) attributes.get("title").get();
map.put("fullname", fullname);
map.put("email", email);
map.put("title", title);
return map;
}
}
}
#Bill what you've done is great, though there is actually an easier way. Instead of resorting to the LdapTemplate, just use the beans you've already registered for DefaultLdapAuthoritiesPopulator and FilterBasedLdapUserSearch. This way you can get the same UserDetails object which also has the authorities populated and reuses your existing code for your net.xxxx.xxxxx.utilities.CustomUserDetailsContextMapper.
Here's what you need to do:
Split out the beens you need to inject as named beans and use ref attributes for the properties and constructor-args (DefaultLdapAuthoritiesPopulator, FilterBasedLdapUserSearch, net.xxxx.xxxxx.utilities.CustomUserDetailsContextMapper).
In your LdapDao inject references to:
FilterBasedLdapUserSearch - userSearch
DefaultLdapAuthoritiesPopulator - authPop
net.xxxx.xxxxx.utilities.CustomUserDetailsContextMapper - userMapper
Add the following method to your LdapDao:
.
public UserDetails getUserDetails(final String username) {
try {
DirContextOperations ctx = userSearch.searchForUser(username);
return userMapper.mapUserFromContext(ctx, username,
authPop.getGrantedAuthorities(ctx, username));
} catch (UsernameNotFoundException ex) {
return null;
}
}
Now you can just call getUserDetails(String) to get the same object you do when retrieving the currently logged in context, and can use the same code etc.

How to do IServiceLocator constructor injection via config file?

How to inject IServiceLocator to my class constructor?
When I tried to do this via my config, described above I got an Exception that it could not to create a RequestHandlersFactory class because unity could't find the constructor with serviceLocator and assemblyName.
I got two interfaces
public interface IPublicService
{
[OperationContract]
[ServiceKnownType("GetKnownTypes", typeof(KnownTypeProvider))]
Response Handle(Request request);
}
public interface IRequestHandlersFactory
{
IRequestHandler GetHandler(Type requestType);
IRequestHandler GetHandler<T>()
where T : Request;
IRequestHandler<T, TK> GetHandler<T, TK>()
where T : Request
where TK : Response;
}
and two classes:
public sealed class PublicService: IPublicService
{
private readonly IRequestHandlersFactory _requestHandlersFactory;
public PublicService(IRequestHandlersFactory requestHandlersFactory)
{
_requestHandlersFactory = requestHandlersFactory;
}
public Response Handle(Request request)
{
var handler = _requestHandlersFactory.GetHandler(request.GetType());
return handler.Handle(request);
}
}
public sealed class RequestHandlersFactory : IRequestHandlersFactory
{
private readonly IServiceLocator _serviceLocator;
private RequestHandlersFactory(IServiceLocator serviceLocator)
{
_serviceLocator = serviceLocator;
...
}
public RequestHandlersFactory(IServiceLocator serviceLocator, String assemblyName) : this(serviceLocator)
{
AddHandlersFromAssembly(Assembly.Load(assemblyName));
}
public RequestHandlersFactory(IServiceLocator serviceLocator, Assembly assembly) : this(serviceLocator)
{
AddHandlersFromAssembly(assembly);
}
...
}
Now I want to create unity config file:
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="IPublicService" type="MyAssembly.IPublicService, MyAssembly"/>
<alias alias="PublicService" type="MyAssembly.PublicService, MyAssembly"/>
<alias alias="IRequestHandlersFactory" type="MyAssembly.IRequestHandlersFactory, MyAssembly"/>
<alias alias="RequestHandlersFactory" type="MyAssembly.RequestHandlersFactory, MyAssembly"/>
<container>
<register type="IPublicService" mapTo="PublicService">
<lifetime type="singleton"/>
</register>
<register type="IRequestHandlersFactory" mapTo="RequestHandlersFactory">
<lifetime type="singleton"/>
<constructor>
<param name="assemblyName">
<value value="MyAssemblyWithHandlers" />
</param>
<param name="serviceLocator" dependencyName="WcfServiceLocator" dependencyType="Microsoft.Practices.ServiceLocation.IServiceLocator, Microsoft.Practices.ServiceLocation"/>
</constructor>
</register>
</container>
My config code:
var container = new UnityContainer();
//configure container
var unitySection = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
var serviceLocator = new UnityServiceLocator(container );
container.RegisterInstance<IServiceLocator>("WcfServiceLocator", serviceLocator, new ContainerControlledLifetimeManager());
unitySection.Configure(container);
Try swapping the order of the constructor parameters in the config file so they line up with the actual parameter list in the class.

Resources