I am using IdentityServer4. I want to change the fixed warning messages while logging in. How can I do that.
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
var existUser = await _userManager.FindByEmailAsync(context.UserName);
if (existUser == null) return;
var passwordCheck = await _userManager.CheckPasswordAsync(existUser, context.Password);
if (passwordCheck == false) return;
context.Result = new GrantValidationResult(existUser.Id.ToString(), OidcConstants.AuthenticationMethods.Password);
}
Assuming you are using default template AspNetCore.Identity with IdentityServer4.
You need to override the messages on IdentityErrorDescriber.
public class CustomIdentityErrorDescriber : IdentityErrorDescriber
{
public override IdentityError DefaultError()
{
return new IdentityError
{
Code = nameof(DefaultError),
Description = "Your custom message"
};
}
}
On startup add AddErrorDescriber on your AddIdentity:
services.AddIdentity<IdentityUser, IdentityRole>()
.AddErrorDescriber<CustomIdentityErrorDescriber>();
Update
It will break OIDC spec... but here we go.
add a CustomResourceOwnerPasswordValidator
public class CustomResourceOwnerPasswordValidator<TUser> : IResourceOwnerPasswordValidator
where TUser : class
{
private readonly SignInManager<TUser> _signInManager;
private readonly UserManager<TUser> _userManager;
private readonly ILogger<CustomResourceOwnerPasswordValidator<TUser>> _logger;
/// <summary>
/// Initializes a new instance of the <see cref="CustomResourceOwnerPasswordValidator{TUser}"/> class.
/// </summary>
/// <param name="userManager">The user manager.</param>
/// <param name="signInManager">The sign in manager.</param>
/// <param name="logger">The logger.</param>
public CustomResourceOwnerPasswordValidator(
UserManager<TUser> userManager,
SignInManager<TUser> signInManager,
ILogger<CustomResourceOwnerPasswordValidator<TUser>> logger)
{
_userManager = userManager;
_signInManager = signInManager;
_logger = logger;
}
/// <summary>
/// Validates the resource owner password credential
/// </summary>
/// <param name="context">The context.</param>
/// <returns></returns>
public virtual async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
var user = await _userManager.FindByNameAsync(context.UserName);
if (user != null)
{
var result = await _signInManager.CheckPasswordSignInAsync(user, context.Password, true);
if (result.Succeeded)
{
var sub = await _userManager.GetUserIdAsync(user);
_logger.LogInformation("Credentials validated for username: {username}", context.UserName);
context.Result = new GrantValidationResult(sub, OidcConstants.AuthenticationMethods.Password);
return;
}
else if (result.IsLockedOut)
{
_logger.LogInformation("Authentication failed for username: {username}, reason: locked out", context.UserName);
}
else if (result.IsNotAllowed)
{
_logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", context.UserName);
}
else
{
_logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", context.UserName);
}
}
else
{
_logger.LogInformation("No user found matching username: {username}", context.UserName);
}
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,"your custom message here");
}
}
On startup:
services.AddIdentityServer()
.AddResourceOwnerValidator<CustomResourceOwnerPasswordValidator<User>>();//Must be last, but before AddDeveloperSigningCredential
Will return this :
"{"error":"invalid_grant","error_description":"your custom message here"}"
I'm new to Castle, NHibernate and WCF.
I implemented the session management for my MVC application based on the following article because it seemd to be the most advanced implementation of all posts I've read so far :
http://nhibernate.info/blog/2011/03/02/effective-nhibernate-session-management-for-web-apps.html
The only problem I got was that this uses some Asp.net specific functionality that isn't available in my WCF service like (HttpContext.Current.Items).
I started to use WcfFacility
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<WcfFacility>().Register
(
Component.For<IRepository>().ImplementedBy(typeof(RepositoryBase<,>)),
Component.For<ITimeService>()
.ImplementedBy<myTimeMvc.Webservice.TimeService>()
.Named("myTimeMvc.Webservice.TimeService"));
container.Register(
Component.For<IServiceBehavior>()
.ImplementedBy<WcfSessionPerRequestBehavior>()
);
}
My Persistance configuration:
public class PersistenceInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Kernel.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<ISessionFactory>().UsingFactoryMethod(k => CreateNhSessionFactory()));
container.Register(Component.For<ISessionFactoryProvider>().AsFactory());
container.Register(Component.For<IEnumerable<ISessionFactory>>().UsingFactoryMethod(k => k.ResolveAll<ISessionFactory>()));
container.Register(Classes.FromAssembly(Assembly.GetAssembly(typeof(HdtRepository))).InSameNamespaceAs<HdtRepository>().WithService.DefaultInterfaces().LifestyleTransient());
}
/// <summary>
/// Creates NHibernate Session Factory.
/// </summary>
/// <returns>NHibernate Session Factory</returns>
private static ISessionFactory CreateNhSessionFactory()
{
var connStr = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString;
return Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2008
.UseOuterJoin()
.ConnectionString(x => x.FromConnectionStringWithKey("DefaultConnection"))
.ShowSql()
)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<TimeRecord>())
.ExposeConfiguration(cfg =>
cfg.Properties[Environment.CurrentSessionContextClass] = typeof(LazySessionContext).AssemblyQualifiedName
)
.BuildSessionFactory();
}
}
Then i tried to solve the problem with "HttpContext.Current.Items" by adding a custom extension:
namespace MyTimeService.WcfExtension
{
///<summary>
/// This class incapsulates context information for a service instance
///</summary>
public class WcfInstanceContext : IExtension<InstanceContext>
{
private readonly IDictionary items;
private WcfInstanceContext()
{
items = new Hashtable();
}
///<summary>
/// <see cref="IDictionary"/> stored in current instance context.
///</summary>
public IDictionary Items
{
get { return items; }
}
///<summary>
/// Gets the current instance of <see cref="WcfInstanceContext"/>
///</summary>
public static WcfInstanceContext Current
{
get
{
WcfInstanceContext context = OperationContext.Current.InstanceContext.Extensions.Find<WcfInstanceContext>();
if (context == null)
{
context = new WcfInstanceContext();
OperationContext.Current.InstanceContext.Extensions.Add(context);
}
return context;
}
}
/// <summary>
/// <see cref="IExtension{T}"/> Attach() method
/// </summary>
public void Attach(InstanceContext owner) { }
/// <summary>
/// <see cref="IExtension{T}"/> Detach() method
/// </summary>
public void Detach(InstanceContext owner) { }
}
}
registered the following way:
<extensions>
<behaviorExtensions>
<add name="WcfInstanceContext" type="MyTimeService.WcfExtension, MyTimeService.WcfExtension.WcfInstanceContext" />
</behaviorExtensions>
</extensions>
Then I created a custom ServiceBehavior
public class WcfSessionPerRequestBehavior : IServiceBehavior
{
private ISessionFactoryProvider _sfp;
public WcfSessionPerRequestBehavior(ISessionFactoryProvider sfp)
{
_sfp = sfp;
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (var cdb in serviceHostBase.ChannelDispatchers)
{
var channelDispatcher = cdb as ChannelDispatcher;
if (null != channelDispatcher)
{
foreach (var endpointDispatcher in channelDispatcher.Endpoints)
{
foreach (var dispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
{
dispatchOperation.CallContextInitializers.Add(new WcfSessionPerRequestCallContextInitializer(_sfp));
}
}
}
}
}
followed by a custom ICallContextInitializer:
public class WcfSessionPerRequestCallContextInitializer : ICallContextInitializer
{
private ILogger logger = NullLogger.Instance;
public ILogger Logger
{
get { return logger; }
set { logger = value; }
}
private ISessionFactoryProvider sfp;
public WcfSessionPerRequestCallContextInitializer(ISessionFactoryProvider s)
{
this.sfp = s;
}
public object BeforeInvoke(InstanceContext instanceContext, IClientChannel channel, Message message)
{
foreach (var sf in sfp.GetSessionFactories())
{
var localFactory = sf;
LazySessionContext.Bind(new Lazy<NHibernate.ISession>(() => BeginSession(localFactory)), sf);
}
return null;
}
public void AfterInvoke(object correlationState)
{
foreach (var sf in sfp.GetSessionFactories())
{
var session = LazySessionContext.UnBind(sf);
if (session == null) continue;
EndSession(session);
}
}
private static NHibernate.ISession BeginSession(ISessionFactory sf)
{
var session = sf.OpenSession();
session.BeginTransaction();
return session;
}
private void ContextEndRequest(object sender, EventArgs e)
{
foreach (var sf in sfp.GetSessionFactories())
{
var session = LazySessionContext.UnBind(sf);
if (session == null) continue;
EndSession(session);
}
}
private static void EndSession(NHibernate.ISession session)
{
if (session.Transaction != null && session.Transaction.IsActive)
{
session.Transaction.Commit();
}
session.Dispose();
}
}
and at last I adjusted the ICurrentSessionContext:
public class LazySessionContext : ICurrentSessionContext
{
private readonly ISessionFactoryImplementor factory;
private const string CurrentSessionContextKey = "NHibernateCurrentSession";
public LazySessionContext(ISessionFactoryImplementor factory)
{
this.factory = factory;
}
/// <summary>
/// Retrieve the current session for the session factory.
/// </summary>
/// <returns></returns>
public NHibernate.ISession CurrentSession()
{
Lazy<NHibernate.ISession> initializer;
var currentSessionFactoryMap = GetCurrentFactoryMap();
if (currentSessionFactoryMap == null || !currentSessionFactoryMap.TryGetValue(factory, out initializer))
{
return null;
}
return initializer.Value;
}
/// <summary>
/// Bind a new sessionInitializer to the context of the sessionFactory.
/// </summary>
/// <param name="sessionInitializer"></param>
/// <param name="sessionFactory"></param>
public static void Bind(Lazy<NHibernate.ISession> sessionInitializer, ISessionFactory sessionFactory)
{
var map = GetCurrentFactoryMap();
map[sessionFactory] = sessionInitializer;
}
/// <summary>
/// Unbind the current session of the session factory.
/// </summary>
/// <param name="sessionFactory"></param>
/// <returns></returns>
public static NHibernate.ISession UnBind(ISessionFactory sessionFactory)
{
var map = GetCurrentFactoryMap();
var sessionInitializer = map[sessionFactory];
map[sessionFactory] = null;
if (sessionInitializer == null || !sessionInitializer.IsValueCreated) return null;
return sessionInitializer.Value;
}
/// <summary>
/// Provides the CurrentMap of SessionFactories.
/// If there is no map create/store and return a new one.
/// </summary>
/// <returns></returns>
private static IDictionary<ISessionFactory, Lazy<NHibernate.ISession>> GetCurrentFactoryMap()
{
//var currentFactoryMap = (IDictionary<ISessionFactory, Lazy<NHibernate.ISession>>)HttpContext.Current.Items[CurrentSessionContextKey];
var currentFactoryMap = (IDictionary<ISessionFactory, Lazy<NHibernate.ISession>>)WcfInstanceContext.Current.Items[CurrentSessionContextKey];
if (currentFactoryMap == null)
{
currentFactoryMap = new Dictionary<ISessionFactory, Lazy<NHibernate.ISession>>();
WcfInstanceContext.Current.Items[CurrentSessionContextKey] = currentFactoryMap;
}
return currentFactoryMap;
}
}
This seems to work but since I'm new to all that stuff I can't say if I did this corretly.
Can anyone have a look at it and give me feedback?
Cheers,
Stefan
Your are using OperationContext.Current which is the correct way to do per request context implementations for WCF services, so it looks good to me...
Question is, why are you not simply using the default implementation which comes out of the box with nhibernate? The implementation is in NHibernate.Context.WcfOperationSessionContext and you would just have to use this one within your session factory setup
For example:
Fluently.Configure()
...
.ExposeConfiguration(cfg => cfg.SetProperty(
Environment.CurrentSessionContextClass,
"wcf")
or Fluently.Configure()...CurrentSessionContext<WcfOperationSessionContext>()
You could also simply setaspNetCompatibilityEnabled=true and you will get HttpContext available to both MVC and WCF.
I'm subclassing the AuthorizeAttribute so I can implement token authentication whereby the token is passed in the request header. I'm also using Ninject for IoC. The overriden OnAuthorization method gets called and validates the token but I still receive a 401.
Any ideas on why this is happening?
TokenAuthorisationAttribute.cs
public class TokenAuthorisationAttribute : AuthorizeAttribute
{
private readonly IRepository _repository;
public TokenAuthorisationAttribute(IRepository repository)
{
_repository = repository;
}
public override void OnAuthorization(
HttpActionContext actionContext)
{
if (!ValidateToken(actionContext.ControllerContext.Request))
HandleUnauthorizedRequest(actionContext);
base.OnAuthorization(actionContext);
}
private bool ValidateToken(HttpRequestMessage request)
{
const string authenticationToken = "Authentication-Token";
var token = request.Headers.GetValues(authenticationToken).FirstOrDefault();
if (token == null)
return false;
var device = _repository.FindSingleOrDefault<Device>(x => x.Id.Equals(token));
if (device == null || !token.Equals(device.Id))
return false;
return true;
}
}
NinjectWebCommon.cs
public static class NinjectWebCommon
{
private static readonly Bootstrapper Bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
Bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
Bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
var connectionString = ConfigurationManager.ConnectionStrings["MONGOHQ_URL"].ConnectionString;
var databaseName = ConfigurationManager.AppSettings["Database"];
kernel.Bind<IRepository>().To<MongoRepository>()
.WithConstructorArgument("connectionString", connectionString)
.WithConstructorArgument("databaseName", databaseName);
kernel.BindHttpFilter<TokenAuthorisationAttribute>(FilterScope.Global);
}
I managed to resolve this issue by overriding the IsAuthorized method instead of OnAuthorization. Not 100% sure if this is the right approach? Any opinions ??
public class TokenAuthorisationAttribute : AuthorizeAttribute
{
private readonly IRepository _repository;
public TokenAuthorisationAttribute(IRepository repository)
{
_repository = repository;
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Authorization == null)
return false;
var authToken = actionContext.Request.Headers.Authorization.Parameter;
var decodedToken = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
var deviceToken = new DeviceToken(decodedToken);
var device = _repository.FindSingleOrDefault<Device>(x => x.Id.Equals(deviceToken.GetDeviceId()));
if (device != null)
{
HttpContext.Current.User = new GenericPrincipal(new ApiIdentity(device), new string[] {});
return true;
}
return base.IsAuthorized(actionContext);
}
}
The ASP.NET WebAPI is an open source project. So you can read the correspondingly codes here:
AuthorizationFilterAttribute http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/03357424caf5#src%2fSystem.Web.Http%2fFilters%2fAuthorizationFilterAttribute.cs
AuthorizeAttribute
http://aspnetwebstack.codeplex.com/SourceControl/changeset/view/03357424caf5#src%2fSystem.Web.Http%2fAuthorizeAttribute.cs
There are two facts an AuthorizationFilterAttribute takes in to consider when it is making decision:
Is OnAuthorization throw exception; or
Is the actionContext's Response field is filled;
Either one is fulfilled, the remaining action filters and action are shortcut.
Based on your code, I'm curious if the function HandleUnauthorizedRequest does either of the operation above.
The reason why overriding IsAuthorized works, is because it works at AuthorizeAttribute level. The OnAuthorization call to the IsAuthorized overload and set value to actionContext's Request property.
Thanks,
Troy
I am building a twitter app for windows phone 7. But the twitter's OAuth is giving me a lot of problems. I am trying to get a Request Token and every single time I get the message: Failed to validate oauth signature and token. Any help would be appreciated.
here is my code:
Parameters base string:
public static string GetParameterString()
{
SystemParameters SysParameters = new SystemParameters();
Parameters Param = new Parameters();
StringBuilder sb = new StringBuilder();
sb.Append(SysParameters.CallbackUrl()).Append("=");
sb.Append(PercentEncoder(Param.callbackURL)).Append("&");
sb.Append(SysParameters.CosumerToken()).Append("=");
sb.Append(PercentEncoder(Param.consumerKey)).Append("&");
sb.Append(SysParameters.Nonce()).Append("=");
sb.Append(PercentEncoder(GetNonce())).Append("&");
sb.Append(SysParameters.SignatureMethod()).Append("=");
sb.Append(PercentEncoder("HMAC-SHA1")).Append("&");
sb.Append(SysParameters.TimeStamp()).Append("=");
sb.Append(PercentEncoder(GetTimeStamp().ToString())).Append("&");
sb.Append(SysParameters.OauthVersion()).Append("=");
sb.Append(PercentEncoder("1.0"));
return sb.ToString();
}
Signature Base string:
public static string GetSignatureBase()
{
SystemParameters SysParameters = new SystemParameters();
Parameters Param = new Parameters();
StringBuilder sb = new StringBuilder();
sb.Append("POST").Append("&");
sb.Append(PercentEncoder(SysParameters.RequestTokenURL())).Append("&");
sb.Append(PercentEncoder(GetParameterString()));
return sb.ToString();
}
Get signature:
public static string GetSignature()
{
SystemParameters SysParameters = new SystemParameters();
Parameters param = new Parameters();
string signature;
signature = Convert.ToBase64String((new HMACSHA1(Encoding.UTF8
.GetBytes(PercentEncoder(param.consumerSecret) + "&")))
.ComputeHash(Encoding.UTF8.GetBytes(GetSignatureBase())));
return PercentEncoder(signature);
}
get token:
private void button2_Click(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.Append(param.RequestURL)
.Append(OauthHelper.RequestType.RequesToken).Append("?");
sb.Append(sysParam.CallbackUrl()).Append("=");
sb.Append(OauthHelper.PercentEncoder(param.callbackURL)).Append("&");
sb.Append(sysParam.CosumerToken()).Append("=");
sb.Append(OauthHelper.PercentEncoder(param.consumerKey)).Append("&");
sb.Append(sysParam.Nonce()).Append("=");
sb.Append(OauthHelper.PercentEncoder(OauthHelper.GetNonce())).Append("&");
sb.Append(sysParam.Signature()).Append("=");
sb.Append(OauthHelper.PercentEncoder(OauthHelper.GetSignature())).Append("&");
sb.Append(sysParam.SignatureMethod()).Append("=");
sb.Append(OauthHelper.PercentEncoder("HMAC-SHA1")).Append("&");
sb.Append(sysParam.TimeStamp()).Append("=");
sb.Append(OauthHelper.PercentEncoder(OauthHelper.GetTimeStamp().ToString()))
.Append("&");
sb.Append(sysParam.OauthVersion()).Append("=");
sb.Append(OauthHelper.PercentEncoder("1.0"));
WebBrowserTask task = new WebBrowserTask();
task.URL = sb.ToString();
task.Show();
}
I have done this implementation referring another code.. my code goes something like this..
#region web query response methods
/// <summary>
/// Event that initiates QueryResponse for RequestToken request
/// </summary>
/// <param name="sender"></param>
/// <param name="e">Webresponse event argument</param>
void requestTokenQuery_QueryResponse(object sender, WebQueryResponseEventArgs e)
{
try
{
var parameters = TwitterHelperMethods.GetQueryParameters(e.Response);
OAuthTokenKey = parameters["oauth_token"];
tokenSecret = parameters["oauth_token_secret"];
var authorizeUrl = TwitterSettings.AuthorizeUri + "?oauth_token=" + OAuthTokenKey;
Dispatcher.BeginInvoke(() =>
{
this.browseTwitter.Navigate(new Uri(authorizeUrl));
});
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(ex.Message);
});
}
}
/// <summary>
/// Event that initiates QueryResponse for AccessToken request
/// </summary>
/// <param name="sender"></param>
/// <param name="e">Webresponse event argument</param>
void AccessTokenQuery_QueryResponse(object sender, WebQueryResponseEventArgs e)
{
try
{
var parameters = TwitterHelperMethods.GetQueryParameters(e.Response);
accessToken = parameters["oauth_token"];
accessTokenSecret = parameters["oauth_token_secret"];
userID = parameters["user_id"];
userScreenName = parameters["screen_name"];
TwitterHelperMethods.SetKeyValue<string>("AccessToken", accessToken);
TwitterHelperMethods.SetKeyValue<string>("AccessTokenSecret", accessTokenSecret);
TwitterHelperMethods.SetKeyValue<string>("ScreenName", userScreenName);
cacheManager.SaveToIsolatedStorage(Utilities.TwitterID, userID);
cacheManager.SaveToIsolatedStorage(Utilities.TwitterAccessToken, accessToken);
cacheManager.SaveToIsolatedStorage(Utilities.TwitterSecretAccessToken, accessTokenSecret);
// NavigationService.Navigate(new Uri("/HomePage.xaml", UriKind.RelativeOrAbsolute));
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(ex.Message);
});
}
}
#endregion
#region Browser events methods
/// <summary>
/// Called after the broswer is loaded
/// </summary>
/// <param name="sender">Browser</param>
/// <param name="e">Routed Event arguments</param>
private void browseTwitter_Loaded(object sender, RoutedEventArgs e)
{
accessToken = TwitterHelperMethods.GetKeyValue<string>("AccessToken");
accessTokenSecret = TwitterHelperMethods.GetKeyValue<string>("AccessTokenSecret");
userScreenName = TwitterHelperMethods.GetKeyValue<string>("ScreenName");
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(accessTokenSecret))
{
var requestTokenQuery = TwitterOAuthHelper.GetRequestTokenQuery();
requestTokenQuery.RequestAsync(TwitterSettings.RequestTokenUri, null);
requestTokenQuery.QueryResponse += new EventHandler<WebQueryResponseEventArgs>(requestTokenQuery_QueryResponse);
}
}
/// <summary>
/// Called when browser Initiates Navigation to Uri provided
/// </summary>
/// <param name="sender">Browser</param>
/// <param name="e">Navigating event arguments</param>
private void browseTwitter_Navigating(object sender, NavigatingEventArgs e)
{
if (e.Uri.ToString().StartsWith(TwitterSettings.CallbackUri))
{
cacheManager = new CacheManager();
var AuthorizeResult = TwitterHelperMethods.GetQueryParameters(e.Uri.ToString());
var VerifyPin = AuthorizeResult["oauth_verifier"];
//We now have the Verification pin
//Using the request token and verification pin to request for Access tokens
var AccessTokenQuery = TwitterOAuthHelper.GetAccessTokenQuery(
OAuthTokenKey, //The request Token
tokenSecret, //The request Token Secret
VerifyPin // Verification Pin
);
AccessTokenQuery.QueryResponse += new EventHandler<WebQueryResponseEventArgs>(AccessTokenQuery_QueryResponse);
AccessTokenQuery.RequestAsync(TwitterSettings.AccessTokenUri, null);
NavigationService.Navigate(new Uri("/OtherPages/HomePage.xaml", UriKind.RelativeOrAbsolute));
}
}
#endregion
My Twitterhelpermethods class goes like this
public class TwitterHelperMethods
{
public static Dictionary<string, string> GetQueryParameters(string response)
{
Dictionary<string, string> nameValueCollection = new Dictionary<string, string>();
string[] items = response.Split('&');
foreach (string item in items)
{
if (item.Contains("="))
{
string[] nameValue = item.Split('=');
if (nameValue[0].Contains("?"))
nameValue[0] = nameValue[0].Replace("?", "");
nameValueCollection.Add(nameValue[0], System.Net.HttpUtility.UrlDecode(nameValue[1]));
}
}
return nameValueCollection;
}
}
and twitteroauthhelper.cs is
public class TwitterOAuthHelper
{
public static OAuthWebQuery GetRequestTokenQuery()
{
var oauth = new OAuthWorkflow
{
ConsumerKey = TwitterSettings.consumerKey,
ConsumerSecret = TwitterSettings.consumerKeySecret,
SignatureMethod = OAuthSignatureMethod.HmacSha1,
ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
RequestTokenUrl = TwitterSettings.RequestTokenUri,
Version = TwitterSettings.oAuthVersion,
CallbackUrl = TwitterSettings.CallbackUri
};
var info = oauth.BuildRequestTokenInfo(WebMethod.Get);
var objOAuthWebQuery = new OAuthWebQuery(info);
objOAuthWebQuery.HasElevatedPermissions = true;
objOAuthWebQuery.SilverlightUserAgentHeader = "Hammock";
objOAuthWebQuery.SilverlightMethodHeader = "GET";
return objOAuthWebQuery;
}
public static OAuthWebQuery GetAccessTokenQuery(string requestToken, string RequestTokenSecret, string oAuthVerificationPin)
{
var oauth = new OAuthWorkflow
{
AccessTokenUrl = TwitterSettings.AccessTokenUri,
ConsumerKey = TwitterSettings.consumerKey,
ConsumerSecret = TwitterSettings.consumerKeySecret,
ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
SignatureMethod = OAuthSignatureMethod.HmacSha1,
Token = requestToken,
Verifier = oAuthVerificationPin,
Version = TwitterSettings.oAuthVersion
};
var info = oauth.BuildAccessTokenInfo(WebMethod.Post);
var objOAuthWebQuery = new OAuthWebQuery(info);
objOAuthWebQuery.HasElevatedPermissions = true;
objOAuthWebQuery.SilverlightUserAgentHeader = "Hammock";
objOAuthWebQuery.SilverlightMethodHeader = "GET";
return objOAuthWebQuery;
}
}
twittersettings.cs :-
public class TwitterSettings
{
public static string RequestTokenUri = "https://api.twitter.com/oauth/request_token";
public static string AuthorizeUri = "https://api.twitter.com/oauth/authorize";
public static string AccessTokenUri = "https://api.twitter.com/oauth/access_token";
public static string CallbackUri = "http://www.qwinixtech.com";
public static string StatusUpdateUrl { get { return "http://api.twitter.com"; } }
// #error TODO REGISTER YOUR APP WITH TWITTER TO GET YOUR KEYS AND FILL THEM IN HERE
public static string consumerKey = "Your consumer key here";
public static string consumerKeySecret = "Your consumer secret key here";
public static string AccessToken = "Your access token here";
public static string AccessTokenSecret = "Your Secret access token here";
public static string oAuthVersion = "1.0a";
}
This works perfectly well.. all u need to make this work is Hammock library.. I hope u already have it.. else pls download it..
When I serialize a enum value using DataContractJsonSerializer, it serializes the numerical value of the enum, not the string name.
IE:
enum foo
{
bar,
baz
}
Serializing a value of foo.bar returns "0", not "bar".
I'd prefer it the other way around, is there a way to override this?
Edit:
Because I didn't want to change the serializer, I used a simple workaround hack.
I exposed a property in the class to serialize that calls ToString on the value, ie:
// Old
[DataMember]
public EnumType Foo
{
get { return _foo; }
set { _foo = value; }
}
// New, I still kept the EnumType but I only serialize the string version
public EnumType Foo
{
get { return _foo; }
set { _foo = value; }
}
[DataMember]
public string FooType
{
get { return _foo.ToString(); }
private set {}
}
It looks like this is by design and this behavior cannot be changed:
Enumeration member values are treated
as numbers in JSON, which is different
from how they are treated in data
contracts, where they are included as
member names.
Here's an example using an alternative (and IMO better and more extensible) serializer which achieves what you are looking for:
using System;
using Newtonsoft.Json;
class Program
{
static void Main(string[] args)
{
var baz = Foo.Baz;
var serializer = new JsonSerializer();
serializer.Converters.Add(new JsonEnumTypeConverter());
serializer.Serialize(Console.Out, baz);
Console.WriteLine();
}
}
enum Foo
{
Bar,
Baz
}
public class JsonEnumTypeConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Foo);
}
public override void WriteJson(JsonWriter writer, object value)
{
writer.WriteValue(((Foo)value).ToString());
}
public override object ReadJson(JsonReader reader, Type objectType)
{
return Enum.Parse(typeof(Foo), reader.Value.ToString());
}
}
I went crazy trying to find an elegant solution to this problem as it seems that everyone defaulted to Newtonsoft's serializer to workaround this issue.
Though Newtonsoft provides more features, it does have some severe drawbacks.
To enumerate a few: the need for parameterless constructors, crazy behaviour if you wish to serialize classes that implement IEnumerable, and it performs very badly when abstract types are used (as it does not make use of the KnownTypes attribute, and the workaround generates a verbose output that exposes your internal namespaces to callers).
On the other hand, there are little examples on how to customize the DataContractJsonSerializer when using it on an MVC4 WebApi solution.
It took me a while to find a solution that represents enums as strings and addresses the known DateTime formatting issues that comes with the DataContractJsonSerializer.
PART I - Put these extension methods in an extensions class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#region JSon
/// <summary>Serializes an object to JSon.</summary>
/// <param name="obj">The object to serialize.</param>
/// <returns>Returns a byte array with the serialized object.</returns>
/// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
public static byte[] SerializeJson(this object obj)
{
using (MemoryStream b = new MemoryStream())
{
SerializeJson(obj, b);
return b.ToArray();
}
}
/// <summary>Serializes an object to JSon.</summary>
/// <param name="obj">The object to serialize.</param>
/// <param name="stream">The stream to write to.</param>
/// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
public static void SerializeJson(this object obj, Stream stream)
{
var settings = new DataContractJsonSerializerSettings();
settings.DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssZ");
settings.DataContractSurrogate = new EnumToStringDataContractSurrogate();
var type = obj == null ? typeof(object) : obj.GetType();
var enumerationValue = obj as System.Collections.IEnumerable;
var fixedValue = enumerationValue != null
? type.IsGenericType && !type.GetGenericArguments()[0].IsInterface
? enumerationValue.ToArray(type.GetGenericArguments()[0])
: enumerationValue.OfType<object>().ToArray()
: obj;
if (enumerationValue != null && (!type.IsGenericType || (type.IsGenericType || type.GetGenericArguments()[0].IsInterface)))
{
var firstMember = (fixedValue as System.Collections.IEnumerable).OfType<object>().FirstOrDefault();
if (firstMember != null)
fixedValue = enumerationValue.ToArray(firstMember.GetType());
}
var fixedType = obj == null
? type
: fixedValue.GetType();
var jsonSer = new DataContractJsonSerializer(fixedType, settings);
jsonSer.WriteObject(stream, fixedValue);
}
/// <summary>
/// Deserializes an object.
/// </summary>
/// <typeparam name="T">The output type of the object.</typeparam>
/// <param name="data">The serialized contents.</param>
/// <returns>Returns the typed deserialized object.</returns>
/// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
public static T DeserializeJSon<T>(this byte[] data)
{
using (MemoryStream b = new MemoryStream(data))
return DeserializeJSon<T>(b);
}
/// <summary>Deserializes a JSon object.</summary>
/// <typeparam name="T">The output type of the object.</typeparam>
/// <param name="stream">The stream to read from.</param>
/// <returns>Returns the typed object.</returns>
/// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
public static T DeserializeJSon<T>(this Stream stream)
{
var settings = new DataContractJsonSerializerSettings();
settings.DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssZ");
settings.DataContractSurrogate = new EnumToStringDataContractSurrogate();
var jsonSer = new DataContractJsonSerializer(typeof(T), settings);
return (T)jsonSer.ReadObject(stream);
}
/// <summary>Deserializes a JSon object.</summary>
/// <param name="data">The serialized contents.</param>
/// <param name="targetType">The target type.</param>
/// <returns>Returns the typed deserialized object.</returns>
/// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
public static object DeserializeJSon(this byte[] data, Type targetType)
{
using (MemoryStream b = new MemoryStream(data))
{
return DeserializeJSon(b, targetType);
}
}
/// <summary>Deserializes a JSon object.</summary>
/// <param name="data">The serialized contents.</param>
/// <param name="targetType">The target type.</param>
/// <returns>Returns the typed deserialized object.</returns>
/// <remarks>This implementation outputs dates in the correct format and enums as string.</remarks>
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "JSon")]
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")]
public static object DeserializeJSon(this Stream data, Type targetType)
{
var settings = new DataContractJsonSerializerSettings();
settings.DateTimeFormat = new System.Runtime.Serialization.DateTimeFormat("yyyy-MM-dd'T'HH:mm:ssZ");
settings.DataContractSurrogate = new EnumToStringDataContractSurrogate();
var jsonSer = new DataContractJsonSerializer(targetType, settings);
return jsonSer.ReadObject(data);
}
/// <summary>Enumerator contract surrogate.</summary>
internal class EnumToStringDataContractSurrogate : IDataContractSurrogate
{
Type IDataContractSurrogate.GetDataContractType(Type type)
{
return type == typeof(Enum) ? typeof(string) : type;
}
object IDataContractSurrogate.GetDeserializedObject(object obj, Type targetType)
{
if (targetType.IsEnum)
{
return obj == null
? System.Enum.GetValues(targetType).OfType<int>().FirstOrDefault()
: System.Enum.Parse(targetType, obj.ToString());
}
return obj;
}
object IDataContractSurrogate.GetObjectToSerialize(object obj, Type targetType)
{
if (obj is Enum)
{
var pair = Enum.GetName(obj.GetType(), obj);
return pair;
}
return obj;
}
object IDataContractSurrogate.GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
object IDataContractSurrogate.GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
void IDataContractSurrogate.GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
Type IDataContractSurrogate.GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
System.CodeDom.CodeTypeDeclaration IDataContractSurrogate.ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
}
#endregion
/// <summary>Creates an array from a non generic source.</summary>
/// <param name="source">The source.</param>
/// <param name="type">The target type of the array.</param>
/// <returns>Returns a typed array.</returns>
public static Array ToArray(this IEnumerable source, Type type)
{
var param = Expression.Parameter(typeof(IEnumerable), "source");
var cast = Expression.Call(typeof(Enumerable), "Cast", new[] { type }, param);
var toArray = Expression.Call(typeof(Enumerable), "ToArray", new[] { type }, cast);
var lambda = Expression.Lambda<Func<IEnumerable, Array>>(toArray, param).Compile();
return lambda(source);
}
PART II - Create your own formatter by encapsulating the DataContractJsonSerializer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// <summary>Custom implementation of DataContract formatter.</summary>
public class DataContractJsonFormatter : MediaTypeFormatter
{
/// <summary>Creates a new instance.</summary>
public DataContractJsonFormatter()
{
SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
}
/// <summary>Gets if the formatter the write a given type.</summary>
/// <param name="type">The type to handle.</param>
/// <returns>Returns if the formatter the write a given type.</returns>
public override bool CanWriteType(Type type)
{
return true;
}
/// <summary>Gets if the formatter the read a given type.</summary>
/// <param name="type">The type to handle.</param>
/// <returns>Returns if the formatter the read a given type.</returns>
public override bool CanReadType(Type type)
{
return true;
}
/// <summary>Deserializes an object.</summary>
/// <param name="type">The target type.</param>
/// <param name="readStream">The stream to read from.</param>
/// <param name="content">The http content.</param>
/// <param name="formatterLogger">A logger.</param>
/// <returns>Returns the deserialized object.</returns>
public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, System.Net.Http.HttpContent content, IFormatterLogger formatterLogger)
{
var task = Task<object>.Factory.StartNew(() =>
{
return readStream.DeserializeJSon(type);
});
return task;
}
/// <summary>Serializes an object.</summary>
/// <param name="type">The target type.</param>
/// <param name="value">The object to serialize.</param>
/// <param name="writeStream">The stream to write to.</param>
/// <param name="content">The http content.</param>
/// <param name="transportContext">The context.</param>
/// <returns>Returns the deserialized object.</returns>
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, System.Net.Http.HttpContent content, System.Net.TransportContext transportContext)
{
var task = Task.Factory.StartNew(() =>
{
value.SerializeJson(writeStream);
});
return task;
}
}
PART III - Edit your Global.asax file and consume your new JSon formatter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// <summary>Event handlers of when the application starts.</summary>
[SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
protected void Application_Start()
{
//Register my custom DataContract JSon serializer
GlobalConfiguration.Configuration.Formatters.Insert(0, new DataContractJsonFormatter());
//Register areas
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
// BundleConfig.RegisterBundles(BundleTable.Bundles);
//JSON serialization config
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.UseDataContractJsonSerializer = false;
}
To get 2 way serialization/deserilization for wcf json you can add a second get set property of type string, when you're construction your json object in javascript use the string named property, on the server side use the strongly typed enum version: e.g.
public class DTOSearchCriteria {
public int ? ManufacturerID { get; set; }
public int ? ModelID { get; set; }
private SortBy _sort;
public SortBy SortType {
get { return _sort; }
set { _sort = value; }
}
public String Sort {
get {
return _sort.ToString();
}
set {
_sort = (SortBy) Enum.Parse(typeof(SortBy), value);
}
}
public int PageSize { get; set; }
public int PageNumber { get; set; }
}
public enum SortBy {
PriceDescending,
PriceAscending
}
edit: Sorry just got up no coffee :(
Here is the code to do what you want to do with the Json Serializer, not the DataContractJsonSerializer.
I haven't done any work with DataContractJsonSerializer yet but after quickly scanning the docs, I am rather disappointed in MS. They obviously went to extremes to make WCF very extensible, but with the DataContractJsonSerializer there is no extensibility. You have to use MS flavored JSON with it. SUPER lame of MS ... already messing up WCF.
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Web.Script.Serialization;
Some Test Objects & Enum:
public enum SomeSillyEnum
{
Foo,Bar,Doo,Daa,Dee
}
public class UseSillyEnum
{
public SomeSillyEnum PublicEnum { get; set; }
public string SomeOtherProperty { get; set; }
public UseSillyEnum()
{
PublicEnum = SomeSillyEnum.Foo;
SomeOtherProperty = "Testing";
}
}
JavaScriptConverters. One for all enums, and one for an object using an enum.
public class EnumStringConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
foreach(string key in dictionary.Keys)
{
try { return Enum.Parse(type, dictionary[key].ToString(), false); }
catch(Exception ex) { throw new SerializationException("Problem trying to deserialize enum from JSON.",ex); }
}
return Activator.CreateInstance(type);
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Dictionary<string,object> objs = new Dictionary<string, object>();
objs.Add(obj.ToString(), ((Enum)obj).ToString("D"));
return objs;
}
public override IEnumerable<Type> SupportedTypes{get {return new Type[] {typeof (Enum)};}}
}
public class SillyConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
UseSillyEnum se = new UseSillyEnum();
foreach (string key in dictionary.Keys)
{
switch(key)
{
case "PublicEnum":
se.PublicEnum = (SomeSillyEnum) Enum.Parse(typeof (SomeSillyEnum), dictionary[key].ToString(), false);
break;
case "SomeOtherProperty":
se.SomeOtherProperty = dictionary[key].ToString();
break;
}
}
return se;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
UseSillyEnum se = (UseSillyEnum)obj;
Dictionary<string, object> objs = new Dictionary<string, object>();
objs.Add("PublicEnum", se.PublicEnum);
objs.Add("SomeOtherProperty", se.SomeOtherProperty);
return objs;
}
public override IEnumerable<Type> SupportedTypes { get { return new Type[] { typeof(UseSillyEnum) }; } }
}
And using it inside a page:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
/* Handles ALL Enums
JavaScriptSerializer jsonSer = new JavaScriptSerializer();
jsonSer.RegisterConverters( new JavaScriptConverter[] { new EnumStringConverter() } );
string json = jsonSer.Serialize(new UseSillyEnum());
Response.Write(json);
UseSillyEnum obj = jsonSer.Deserialize<UseSillyEnum>(json);
Response.Write(obj.PublicEnum);
*/
/* Handles Object that uses an enum */
JavaScriptSerializer jsonSer = new JavaScriptSerializer();
jsonSer.RegisterConverters( new JavaScriptConverter[] { new SillyConverter() } );
string json = jsonSer.Serialize(new UseSillyEnum());
Response.Write(json);
UseSillyEnum obj = jsonSer.Deserialize<UseSillyEnum>(json);
Response.Write(obj.PublicEnum);
}
}
I have put together all of the pieces of this solution using the Newtonsoft.Json library in a way that works within WCF. It fixes the enum issue and also makes the error handling much better, and it works in IIS hosted services. It's quite a lot of code, so you can find it on GitHub here: https://github.com/jongrant/wcfjsonserializer/blob/master/NewtonsoftJsonFormatter.cs
You have to add some entries to your Web.config to get it to work, you can see an example file here:
https://github.com/jongrant/wcfjsonserializer/blob/master/Web.config