Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NsWag generated code for c# dose not match with response dto object #2552

Closed
Rgl88 opened this issue Sep 30, 2017 · 6 comments
Closed

NsWag generated code for c# dose not match with response dto object #2552

Rgl88 opened this issue Sep 30, 2017 · 6 comments

Comments

@Rgl88
Copy link

Rgl88 commented Sep 30, 2017

I use blow code for get token in winform:

var tokenService = new Services.WebApi.TokenAuthClient("http://localhost:21021");
var result = await tokenService.AuthenticateAsync(new Services.WebApi.AuthenticateModel()
{
    UserNameOrEmailAddress = "admin",
    Password = "123qwe",
    RememberClient = true
});

The service class generated via Nswag.
but problem where blow method want deserialize json response to AuthenticateResultModel and it not work.

public async System.Threading.Tasks.Task<AuthenticateResultModel> AuthenticateAsync(AuthenticateModel model, System.Threading.CancellationToken cancellationToken)
{
...
      result_ = Newtonsoft.Json.JsonConvert.DeserializeObject<AuthenticateResultModel>(responseData_ , _settings.Value);
....
}

deserializer not work because response template do not match with AuthenticateResultModel
Response:

{
    "result":
    {
    "accessToken":"5",
    "encryptedAccessToken":"6",
    "expireInSeconds":86400,
    "userId":2
    },
"targetUrl":null,
"success":true,
"error":null,
"unAuthorizedRequest":false,
"__abp":true
}

But AuthenticateResultModel:

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "9.6.5.0 (Newtonsoft.Json v9.0.0.0)")]
public partial class AuthenticateResultModel 
{
    [Newtonsoft.Json.JsonProperty("accessToken", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public string AccessToken { get; set; }
    
    [Newtonsoft.Json.JsonProperty("encryptedAccessToken", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public string EncryptedAccessToken { get; set; }
    
    [Newtonsoft.Json.JsonProperty("expireInSeconds", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public int? ExpireInSeconds { get; set; }
    
    [Newtonsoft.Json.JsonProperty("userId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
    public long? UserId { get; set; }
    
    public string ToJson() 
    {
        return Newtonsoft.Json.JsonConvert.SerializeObject(this);
    }
        
    public static AuthenticateResultModel FromJson(string data)
    {
        return Newtonsoft.Json.JsonConvert.DeserializeObject<AuthenticateResultModel>(data);
    }
}

I test with new model class and it work how configure Nswag or Abp to generate correct code?

public class AuthenticateResultModel
{
    public AuthenticateResultModel Result{ get; set; }
    public string TargetUrl { get; set; }
    public bool Success { get; set; }
    public string Error { get; set; }
    public bool UnAuthorizedRequest { get; set; }
    public bool __abp { get; set; }
}

public class AuthenticateResultModel
{
    public string AccessToken { get; set; }
    public string EncryptedAccessToken { get; set; }
    public int? ExpireInSeconds { get; set; }
    public long? UserId { get; set; }
}
@ismcagdas
Copy link
Member

@Rgl88, ABP wraps result with AjaxResponse. I think NSwag cannot understand this. Maybe you can use a class for NSwag to get only Result field of returned response. Have you checked on NSwag's repo if this is possible ?

@Rgl88
Copy link
Author

Rgl88 commented Oct 3, 2017

@ismcagdas as for response from Nswag owner, i can add attribute to controller methods, but in abp controllers generated dynamicly (Application Services as Controllers).
How i can add this attribute to controller methods? Of course, there is another solution (implement a custom OperationProcessor ...)

Attribute for controller methods
[SwaggerResponse(200, typeof(MyResponse)]

public class MyResponse<T>
{
    public T Result { get; set; }
    public string TargetUrl { get; set; }
    public bool Success { get; set; }
    public string Error { get; set; }
    public bool UnAuthorizedRequest { get; set; }
    public bool __abp { get; set; }
}

@hikalkan
Copy link
Member

hikalkan commented Oct 3, 2017

You can add that attribute to application service classes (not interfaces) and it should work. Surely, you need to add a reference to swagger in that case, but there is no other way.

@Rgl88
Copy link
Author

Rgl88 commented Oct 3, 2017

It is very hard to add this attribute to all methods and for AsyncCrudAppService must all methods overrided,
I go implement custom OperationProcessor for NSwag.
I test it but Nswag do not understand it:

[SwaggerResponse(200, typeof(Response))]
public override Task<PagedResultDto<BankDto>> GetAll(PagedResultRequestDto input)
{
      return base.GetAll(input);
}
public class Response
{
     public PagedResultDto<BankDto> Result { get; set; }
     public string TargetUrl { get; set; }
     public bool Success { get; set; }
     public string Error { get; set; }
     public bool UnAuthorizedRequest { get; set; }
     public bool __abp { get; set; }
}

@Rgl88 Rgl88 closed this as completed Oct 3, 2017
@Rgl88
Copy link
Author

Rgl88 commented Oct 8, 2017

Very simple solution:

var str = File.ReadAllText(filePath);

var methodReturnPattern = @"System.Threading.Tasks.Task<(?'TypeName'\w+){1}\>+";

var deserializerPattern = @"result_ = Newtonsoft.Json.JsonConvert.DeserializeObject<(?'TypeName'\w+){1}\>{1}";

var resultDefinitionPattern = @"default\((?'TypeName'\w+){1}\);{1}";

var baseUrlPattern = @"(?'VarName'urlBuilder_)\.Append";

var result = Regex.Replace(str, methodReturnPattern, m => m.Value.Replace(m.Groups["TypeName"].Value, "Response<"+m.Groups["TypeName"].Value + ">"));
result = Regex.Replace(result, deserializerPattern, m => m.Value.Replace(m.Groups["TypeName"].Value, "Response<" + m.Groups["TypeName"].Value + ">"));
result = Regex.Replace(result, resultDefinitionPattern, m => m.Value.Replace(m.Groups["TypeName"].Value, "Response<" + m.Groups["TypeName"].Value + ">"));
result = Regex.Replace(result, baseUrlPattern, m => m.Value.Replace(m.Groups["VarName"].Value, m.Groups["VarName"].Value + ".Append(BaseUrl)"));
File.WriteAllText(filePath, result);

@jogoertzen-stantec
Copy link
Contributor

@ismcagdas as for response from Nswag owner, i can add attribute to controller methods, but in abp controllers generated dynamicly (Application Services as Controllers).
How i can add this attribute to controller methods? Of course, there is another solution (implement a custom OperationProcessor ...)

Attribute for controller methods
[SwaggerResponse(200, typeof(MyResponse)]

public class MyResponse<T>
{
    public T Result { get; set; }
    public string TargetUrl { get; set; }
    public bool Success { get; set; }
    public string Error { get; set; }
    public bool UnAuthorizedRequest { get; set; }
    public bool __abp { get; set; }
}

This worked for me, but I used

[ProducesResponseType(typeof(MyResponse<...>), 200)]

instead of

[SwaggerResponse(200, typeof(MyResponse<...>)]

as per this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants