ServiceStack问题与测试方法和服务之间的对象序列化

时间:2014-06-15 22:59:00

标签: servicestack

美好的一天,

我们遇到序列化问题,其中设置了一个属性值的请求对象最终被服务接收,其值分配给不同的属性。请参阅以下内容以获取更多信息。

我们正在使用3.9.71版本的ServiceStack NuGet软件包。该解决方案由以下项目组成:

  • Project.Host:用于自托管ServiceStack并包含服务类。
  • Project.DTO:所有服务DTO和周围的类。
  • Project.Tests:包含单元测试。

问题已经确定只有一个类/服务,即MinimalUser和MinimalUserService,你可以看到下面的代码:

MinimalUser.cs

namespace Project.DTO
{
    [Route("/User/{Identity}", "GET")]
    [Route("/User/{Username}", "GET")]
    [Route("/User/{DisplayName}", "GET")]    
    public class MinimalUser : IReturn<MinimalUser>
    {
        #region Properties

        public int? Identity { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public string DisplayName { get; set; }
        public string Email { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Language { get; set; }
        public string TimeZone { get; set; }
        public string Culture { get; set; }
        public List<string> Roles { get; set; }
        public List<string> Permissions { get; set; }
        public DiscUserDetails DiscUserDetails { get; set; }

        #endregion

        #region Constructors
        public MinimalUser() { } 

        public MinimalUser(UserAuth auth)
        {
            if (auth != null)
            {
                this.Identity = auth.Id;
                this.Username = auth.UserName;
                this.DisplayName = auth.DisplayName;
                this.Email = auth.Email;
                this.FirstName = auth.FirstName;
                this.LastName = auth.LastName;
                this.Language = auth.Language;
                this.TimeZone = auth.TimeZone;
                this.Culture = auth.Culture;
                this.Roles = auth.Roles;
                this.Permissions = auth.Permissions;
                this.DiscUserDetails = auth.Get<DiscUserDetails>();
            }
        }

        #endregion

        #region Methods

        public static MinimalUser FromUserAuth(UserAuth auth)
        {
            return auth == null ? new MinimalUser() : new MinimalUser
            {
                Identity = auth.Id,
                Username = auth.UserName,
                DisplayName = auth.DisplayName,
                Email = auth.Email,
                FirstName = auth.FirstName,
                LastName = auth.LastName,
                Language = auth.Language,
                TimeZone = auth.TimeZone,
                Culture = auth.Culture,
                Roles = auth.Roles,
                Permissions = auth.Permissions,
                DiscUserDetails = auth.Get<DiscUserDetails>()
            };
        }

        #endregion
    }
}

DiscUserDetails.cs

namespace Project.DTO
{
    public class DiscUserDetails
    {
        public int? LocationId { get; set; }
        public bool IsActive { get; set; }
        public byte NumberOfFailedLoginAttempts { get; set; }
        public bool MustChangePasswordAtNextLogon { get; set; }
        public int? LastAcceptedPolicyId { get; set; }
    }
}

MinimalUserService.cs

namespace Project.Services
{
    [Authenticate]
    [RequiredRole(new string[] { RoleNames.Admin })]
    public class MinimalUserService : Service
    {
        IUserAuthRepository authRepo = AppHost.Resolve<IUserAuthRepository>() as OrmLiteAuthRepository;

        /// <summary>
        /// Return a minimalist structure of user insensitive information.
        /// </summary>
        /// <param name="request">The request containing the ID of the user.</param>
        /// <returns>A minimalist structure of user insensitive information.</returns>
        public object Get(MinimalUser request)
        {
            if (request.Identity != null)            
                return new MinimalUser(authRepo.GetUserAuth(request.Identity.ToString()));
            else if (request.Username != null)
                return new MinimalUser(authRepo.GetUserAuthByUserName(request.Username)); 
            else
                return null;
        }            
    }
}

从我的测试项目中,我运行以下测试:

[TestMethod]
public void GetMinimalUserByUsername()
{
    AuthResponse authResponse = client.Post<AuthResponse>("/auth", new Auth
    {
        UserName = "accountwithadminrole",
        Password = "blablabla",
        RememberMe = true,
        provider = CredentialsAuthProvider.Name
    });

    MinimalUser request = new MinimalUser
    {
        DisplayName = BaseAccounts.System,
    };

    MinimalUser user = client.Get<MinimalUser>(request);

    Assert.IsNotNull(user);
}

在发出client.Get方法之前,我清楚地看到,请求对象的所有属性都设置为null,但DisplayName的值为&#34; system&#34;。当MinimalUserService Get方法收到此请求时,值&#34; system&#34;现在已分配给属性UserName,DisplayName为null。

另外,我试图在MinimalUser类中逐个评论属性,怀疑其中一个字段可能导致序列化问题,我最终会有随机的错误请求&#39;在评论一定数量的房产时。虽然,我可以随机评论一个属性和一个以前导致“错误请求”的属性。不会这样做取决于其他属性被注释掉。

我真的很担心这可能发生的原因。我认为服务和DTO与同一项目中的其他人相比很简单,但我确信我在这里做了一些非常愚蠢的事情。

不要犹豫,询问更多细节,我很乐意提供您需要的所有信息。

谢谢。

1 个答案:

答案 0 :(得分:1)

填充用户名而不是DisplayName的原因是您为MinimalUser定义的路由。在MinimalUser.cs中,您定义了两条相同的路径:

[Route("/User/{Identity}", "GET")]
[Route("/User/{Username}", "GET")]
[Route("/User/{DisplayName}", "GET")]

用户名和Displayname都是字符串。这使得ServiceStack无法确定指向请求的适当路由,因为它无法区分路由。您可以通过删除路由或向其中一个路由添加其他文本来解决此问题;例如/User/ByDisplayName/{Username}