I am working with my first Blazor app and running into a strange problem. I have a Web API that returns a fairly basic JSON response:
{
"entityId": 26,
"notifications": [
{
"type": "Success",
"message": "The operation completed successfully."
}
],
"hasErrors": false,
"hasWarnings": false
}
There is a standard POCO class that matches the above properties. From the Blazor app, when I try to get the response from an Http.PutAsJsonAsync<T>, an instance of the POCO class is created (so it's not null and doesn't throw an error), but none of the values above are actually present. The EntityId property is null and Notifications is instantiated but empty. The way I'm trying to access the response is:
var result = await Http.PutAsJsonAsync<ManufacturerModel>($"manufacturers", (ManufacturerModel)context.Model);
if (result.IsSuccessStatusCode)
{
var response = await JsonSerializer.DeserializeAsync<EntityResponseModel>(result.Content.ReadAsStream());
//response isn't null - it's just a newly created object with nothing mapped
}
Via the console in Chrome, I've confirmed the proper JSON is returned so it's really confusing why it'd create a new instance of that class, but not map any of the values.
Any ideas?
**** EDIT - including POCO definition ****
public class EntityResponseModel : BaseModel
{
/// <summary>
/// Gets or sets the Id of the affected entity
/// </summary>
public long? EntityId { get; set; }
}
public class BaseModel
{
public BaseModel()
{
this.Notifications = new EntityNotificationCollection();
}
#region Validation
/// <summary>
/// Adds a notification to this model.
/// </summary>
/// <param name="type">The type of notification</param>
/// <param name="message">The message to display</param>
public void AddNotification(EntityNotificationTypes type, string message)
{
this.Notifications.Add(new EntityNotification { Type = type, Message = message });
}
/// <summary>
/// Gets or sets the collection of notifications
/// </summary>
public EntityNotificationCollection Notifications { get; private set; }
/// <summary>
/// Gets whether errors exist on this model.
/// </summary>
//[JsonIgnore]
public bool HasErrors { get => this.Notifications.HasErrors; }
/// <summary>
/// Gets whether warnings exist on this model
/// </summary>
//[JsonIgnore]
public bool HasWarnings { get => this.Notifications.HasWarnings; }
#endregion
}
You can specify your serialization settings and define it as being case sensitive or insensitive.
CharlieFace provided this answer above.
Looks like you need to add your JsonAttribute to managing your case sensitivity.
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
This is my full solution:
var retList = new List<PocoSomeClassData> ();
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode) {
using (var reponseStream = await response.Content.ReadAsStreamAsync())
{
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
retList = await JsonSerializer.DeserializeAsync < List<PocoSomeClassData> > (reponseStream,options);
}
}
Related
Im currently trying to call a field on graphql-query from code, without using the http layer. In a test case I had success using this snippet inside of a field resolver. The breakpoint hits.
var newContext = new ResolveFieldContext(context);
var query = context.ParentType;
var ticketQueryField = query.GetField("getTickets");
await (Task) ticketQueryField.Resolver.Resolve(context);
So I think its possible to fill the copied ResolveFieldContext with my real needed fields/arguments and call it like this. But its very ... complicated to fill the ResolveFieldContext by hand. So maybe there is a easier way to create the context. Like:
var newContext = new ResolveFieldContext("query test { getTickets(id: 1) { number, title } }");
That would be really awesome and in my real scenario there a more then just field which I want to access with the generated query.
Why I want to use the Graph like this? The Batch-Loader which we are using inside the GraphQL-Types are perfect for our needs.
You can execute a GraphQL query without http by using the DocumentExecutor directly, and providing your own DocumentWriter if you want the data in a specific format. There is an extension method which returns JSON, but you can write your own.
https://github.com/graphql-dotnet/graphql-dotnet/blob/master/src/GraphQL.NewtonsoftJson/DocumentWriter.cs
This is an example test base class for testing queries:
https://github.com/graphql-dotnet/graphql-dotnet/blob/master/src/GraphQL.Tests/BasicQueryTestBase.cs
This is a console example that returns JSON, not using http.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using GraphQL;
using GraphQL.Authorization;
using GraphQL.SystemTextJson;
using GraphQL.Types;
using GraphQL.Validation;
using Microsoft.Extensions.DependencyInjection;
namespace BasicSample
{
internal class Program
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "main")]
private static async Task Main()
{
using var serviceProvider = new ServiceCollection()
.AddSingleton<IAuthorizationEvaluator, AuthorizationEvaluator>()
.AddTransient<IValidationRule, AuthorizationValidationRule>()
.AddTransient(s =>
{
var authSettings = new AuthorizationSettings();
authSettings.AddPolicy("AdminPolicy", p => p.RequireClaim("role", "Admin"));
return authSettings;
})
.BuildServiceProvider();
string definitions = #"
type User {
id: ID
name: String
}
type Query {
viewer: User
users: [User]
}
";
var schema = Schema.For(definitions, builder => builder.Types.Include<Query>());
// remove claims to see the failure
var authorizedUser = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim("role", "Admin") }));
string json = await schema.ExecuteAsync(_ =>
{
_.Query = "{ viewer { id name } }";
_.ValidationRules = serviceProvider
.GetServices<IValidationRule>()
.Concat(DocumentValidator.CoreRules);
_.RequestServices = serviceProvider;
_.UserContext = new GraphQLUserContext { User = authorizedUser };
});
Console.WriteLine(json);
}
}
/// <summary>
/// Custom context class that implements <see cref="IProvideClaimsPrincipal"/>.
/// </summary>
public class GraphQLUserContext : Dictionary<string, object>, IProvideClaimsPrincipal
{
/// <inheritdoc />
public ClaimsPrincipal User { get; set; }
}
/// <summary>
/// CLR type to map to the 'Query' graph type.
/// </summary>
public class Query
{
/// <summary>
/// Resolver for 'Query.viewer' field.
/// </summary>
[GraphQLAuthorize("AdminPolicy")]
public User Viewer() => new User { Id = Guid.NewGuid().ToString(), Name = "Quinn" };
/// <summary>
/// Resolver for 'Query.users' field.
/// </summary>
public List<User> Users() => new List<User> { new User { Id = Guid.NewGuid().ToString(), Name = "Quinn" } };
}
/// <summary>
/// CLR type to map to the 'User' graph type.
/// </summary>
public class User
{
/// <summary>
/// Resolver for 'User.id' field. Just a simple property.
/// </summary>
public string Id { get; set; }
/// <summary>
/// Resolver for 'User.name' field. Just a simple property.
/// </summary>
public string Name { get; set; }
}
}
I' using ASP.net Core and swagger, I have created the following method and verbose documentation:
/// <summary>Creates a package with the requested information.</summary>
/// <param name="createPackage">The package information to create.</param>
/// <response code="201" cref="PackageCreatedExample">Created</response>
/// <returns><see cref="PackageCreated"/> package created.</returns>
[HttpPost]
[SwaggerResponse(201, "Package created", typeof(PackageCreated))]
[SwaggerResponse(400, "Validation failure", typeof(ApiErrorResult))]
[SwaggerResponseExample(201, typeof(PackageCreatedExample))]
public async Task<IActionResult> CreatePackage(PackageCreate createPackage)
{
if (!ModelState.IsValid)
{
return BadRequest(new ApiErrorResult(ModelState));
}
var createdPackageInfo = new PackageCreated();
// Created item and location returned.
return CreatedAtAction(nameof(GetPackage), new { createdPackageInfo.Id, Version = "2" }, createdPackageInfo);
}
I am trying to get an example response to appear in swagger but it always defaults the response sample as follows:
As you can see from the code above, I created a "PackageCreatedExample" class that I want to be picked up and used in swagger but it's just being ignored. I tried using comments response code="201" cref="PackageCreatedExample" and wrote the SwaggerResponseExample attribute, but neither get picked up. Here's my example code:
public class PackageCreatedExample : IExamplesProvider<PackageCreated>
{
public PackageCreated GetExamples()
{
return new PackageCreated {
Outcome = "PackageCreated",
Reference = "6178",
ShippingDocumentation = new List<Documentation> {
new Documentation {
Document = "JVBERi0xLjMNCjEgMCBvYmoNCjw8DQovVHlwZSAvQ2F...",
Format = "Pdf",
Type = DocumentationType.TYPE3
}
},
ReturnsDocumentation = new List<Documentation> {
new Documentation {
Document = "YmoNCjw8DQovVHlwZSAvQ2F0YWxvZw0KL1BhZ2VzIDQgMJVBERi0xLjMNCjEgMCBv...",
Format = "doc",
Type = DocumentationType.TYPE4
}
}
};
}
}
What am I doing wrong?
Thanks in advance for any pointers!
According to the author, this method should not be used anymore (link). This section of the official readme describes the way how the descriptions and examples should be handled.
In short, you have to
Remove all the annotations and use the followings instead:
/// <summary>Creates a package with the requested information.</summary>
/// <param name="createPackage">The package information to create.</param>
/// <returns>package created.</returns>
/// <response code="201">Created</response>
/// <response code="400">Validation failure</response>
[HttpPost]
[ProducesResponseType(typeof(PackageCreated), 201)]
[ProducesResponseType(typeof(ApiErrorResult), 400)]
public async Task<IActionResult> CreatePackage(PackageCreate createPackage)
Enable generating XML documentation under Project -> Properties -> Build tab -> Check XML documentation file in the Output section
Configure swagger to use the generated XML file
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1",
new OpenApiInfo
{
Title = "My API - V1",
Version = "v1"
}
);
var filePath = Path.Combine(System.AppContext.BaseDirectory, "MyApi.xml");
c.IncludeXmlComments(filePath);
}
Add example comments to the properties of your input model classes:
PackageCreate.cs
public PackageCreate
{
// Built-in type
/// <summary>
/// Outcome value
/// </summary>
/// <example>PackageCreated</example>
public string Outcome { get; set; }
// Custom class -> comment its properties in Documentation.cs
public Documentation { get; set; }
// Array type -> comment the properties in Documentation.cs
public IList<Documentation> { get; set; }
}
Documentation.cs:
public Documentation
{
/// <summary>
/// Document name
/// </summary>
/// <example>YmoNCjw8DQovVHlwZSAvQ2F0YWxvZw0KL1BhZ2VzIDQgMJVBERi0xLjMNCjEgMCBv</example>
public string Document { get; set; }
/// <summary>
/// Document format
/// </summary>
/// <example>doc</example>
public string Format { get; set; }
}
I'm developing API via c# that will send notification to specific user ( android user) , then when user open the notification I want to redirect him to specific activity.
So I needed to send data along with notification message. I've tested it using Firebase Console and it's working fine , The notification is received and my launcher activity receive the extra from data has been sent
I've also tested it from my backend and the notification is received except that my launcher intent doesn't receive any extra.
I've been struggling for hours now , Any idea would help !
this is my code from c#
public String getNotification ()
{
string serverKey = "xxxx";
var result = "-1";
try
{
var webAddr = "https://fcm.googleapis.com/fcm/send";
var regID = "xxxx";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(webAddr);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Headers.Add("Authorization:key=" + serverKey);
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = "{\"to\": \"" + regID +
"\",\"notification\": {\"title\": \"Testing\",\"body\": \"Hi Testing\"}" +
"," + "\"data:\"" + "{\"mymsg\":" + "\"h\" }}";
streamWriter.Write(json);
streamWriter.Flush();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
result = streamReader.ReadToEnd();
}
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return "Can't Send";
}
}
}
And this is my launcher activity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("test" , "in main");
if (getIntent().getStringExtra("mymsg") != null) {
Log.d("test" , "has extra");
Intent intent = new Intent(this, Main2Activity.class);
startActivity(intent);
finish();
} else {
Log.d("test" , "no extra");
}
It looks like you have the wrong JSON:
," + "\"data:\"" + "{\"mymsg\":" + "\"h\" }} will be:
"data:" {
"mymsg":"h"
}
just correct your JSON. But I recommend using c# classes and serialization. Look at this simple example:
var payload = new {
to = "XXXX",
notification = new
{
body = "Test",
title = "Test"
},
data = new {
mymsg = "h"
}
}
// Using Newtonsoft.Json
string postbody = JsonConvert.SerializeObject(payload).ToString();
But its just example. You should create classes instead of anonym objects and using JsonProperty or another way to serialize the object. Something like that:
/// <summary>
/// Data for sending push-messages.
/// </summary>
public class PushData
{
/// <summary>
/// [IOS] Displaying message
/// </summary>
[JsonProperty("alert")]
public Alert Alert { get; set; }
/// <summary>
/// [IOS] badge value (can accept string representaion of number or "Increment")
/// </summary>
[JsonProperty("badge")]
public Int32? Badge { get; set; }
/// <summary>
/// [IOS] The name of sound to play
/// </summary>
[JsonProperty("sound")]
public String Sound { get; set; }
/// <summary>
/// [IOS>=7] Content to download in background
/// </summary>
/// <remarks>
/// Set 1 for silent mode
/// </remarks>
[JsonProperty("content-available")]
public Int32? ContentAvailable { get; set; }
/// <summary>
/// [IOS>=8] Category of interactive push with additional actions
/// </summary>
[JsonProperty("category")]
public String Category { get; set; }
/// <summary>
/// [Android] Used for collapsing some messages with same collapse_key
/// </summary>
[JsonProperty(PropertyName = "collapse_key")]
public String CollapseKey { get; set; }
/// <summary>
/// [Android] This parameter specifies how long (in seconds) the message should be kept in GCM storage if the device is offline.
/// The maximum time to live supported is 4 weeks, and the default value is 4 weeks.
/// </summary>
/// <value>
/// Time_to_live value of 0 means messages that can't be delivered immediately will be discarded
/// </value>
[JsonProperty("time_to_live")]
public Int32 TimeToLive { get; set; }
/// <summary>
/// [Android] Uri of activity to open when push activated by user
/// </summary>
[JsonProperty("url")]
public String Url { get; set; }
/// <summary>
/// Payload for push
/// </summary>
[JsonProperty("data")]
public Payload Payload { get; set; }
}
with message builder which serialize your message body to correct json string.
As i read readme.txt for latest RestSharp:
*** IMPORTANT CHANGE IN RESTSHARP VERSION 103 ***
In 103.0, JSON.NET was removed as a dependency.
If this is still installed in your project and no other libraries depend on
it you may remove it from your installed packages.
There is one breaking change: the default Json*Serializer* is no longer
compatible with Json.NET. To use Json.NET for serialization, copy the code
from https://github.com/restsharp/RestSharp/blob/86b31f9adf049d7fb821de8279154f41a17b36f7/RestSharp/Serializers/JsonSerializer.cs
and register it with your client:
var client = new RestClient();
client.JsonSerializer = new YourCustomSerializer();
The default Json*Deserializer* is mostly compatible, but it does not support
all features which Json.NET has (like the ability to support a custom [JsonConverter]
by decorating a certain property with an attribute). If you need these features, you
must take care of the deserialization yourself to get it working.
Im already installed Newtonsoft.Json with nu package manager, and im trying to register Json.NET to client variable, but didn't work. here is my code:
private void Form1_Load(object sender, EventArgs e)
{
var client = new RestClient("http://homestead.app/vendor");
client.JsonSerializer = new JsonSerializer(); <-- HERE IS THE ERROR
var request = new RestRequest("", Method.GET);
IRestResponse response = client.Execute(request);
var content = response.Content; // raw content as string
textBox1.Text = content;
}
the client.JsonSerializer property is not available.
Please kindly help me. thanks
The serializers for RestSharp must implement two interfaces:
RestSharp.Serializers.ISerializer
RestSharp.Serializers.IDeserializer
You must wrap the serializer from Newtonsoft to type with these interfaces.
There is working code from one project I worked on:
/// <summary>
/// Default JSON serializer for request bodies
/// Doesn't currently use the SerializeAs attribute, defers to Newtonsoft's attributes
/// </summary>
/// <remarks>
/// Based on http://blog.patrickmriley.net/2014/02/restsharp-using-jsonnet-serializer.html
/// </remarks>
public class RestSharpJsonNetSerializer : RestSharp.Serializers.ISerializer, RestSharp.Deserializers.IDeserializer
{
private readonly JsonSerializer serializer;
/// <summary>
/// Default serializer
/// </summary>
public RestSharpJsonNetSerializer()
{
this.ContentType = "application/json";
this.serializer = new JsonSerializer
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public RestSharpJsonNetSerializer(JsonSerializer serializer)
{
this.ContentType = "application/json";
this.serializer = serializer;
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize></param>
/// <returns>JSON as String</returns>
public string Serialize(object obj)
{
using (var stringWriter = new StringWriter())
{
using (var jsonTextWriter = new JsonTextWriter(stringWriter))
{
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
this.serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
public T Deserialize<T>(RestSharp.IRestResponse response)
{
using (var strReader = new StringReader(response.Content))
{
using (var jsonReader = new JsonTextReader(strReader))
{
var data = this.serializer.Deserialize<T>(jsonReader);
return data;
}
}
}
}
The new readme.txt is updated at least in GitHub but the package still contains the old code.
So the answer of #TcKs is correct:
var request = new RestRequest();
request.JsonSerializer = new Shared.JsonSerializer();
var client = new RestClient();
client.Post(request);
It will be easy if you using mapper object when return
public YourResultBase Login(string username, string password)
{
var client = new RestClient("apiUrl");
var request = new RestRequest(Method.GET)
{
OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; }
};
request.AddHeader("Cache-Control", "no-cache");
IRestResponse<YourResultBase> response = client.Execute<YourResultBase>(request);
var result = response.Data;
return result;
}
I want to use sms gateway in my app. that's why I've contact with an operator and the operator give me a api format.
URL: https://ideabiz.lk/apicall/smsmessaging/v2/outbound/3313/requests
Request header
Content-Type: application/json
Authorization: Bearer [access token]
Accept: application/json
Body
{
"outboundSMSMessageRequest": {
"address": [
"tel:+94771234567"
],
"senderAddress": "tel:12345678",
"outboundSMSTextMessage": {
"message": "Test Message"
},
"clientCorrelator": "123456",
"receiptRequest": {
"notifyURL": "http://128.199.174.220:1080/sms/report",
"callbackData": "some-data-useful-to-the-requester"
},
"senderName": "ACME Inc."
}
}
Now, I've code it :
RestClient client = new RestClient(#"https://ideabiz.lk/");
RestRequest req = new RestRequest(#"apicall/smsmessaging/v2/outbound/3313/requests", Method.POST);
req.AddHeader("Content-Type", #"application/json");
req.AddHeader("Authorization", #"Bearer " + accessToken.ToString());
req.AddHeader("Accept", #"application/json");
string jSon_Data = #"{'outboundSMSMessageRequest': {'address': ['tel:+94768769027'],'senderAddress': 'tel:3313','outboundSMSTextMessage': {'message': 'Test Message : " + System.DateTime.Now.ToString() + "'},'clientCorrelator': '123456','receiptRequest': {'notifyURL': 'http://128.199.174.220:1080/sms/report','callbackData': 'some-data-useful-to-the-requester'},'senderName': ''}}";
JObject json = JObject.Parse(jSon_Data);
req.AddBody(json);
IRestResponse response = client.Execute(req);
string x = response.Content.ToString();
Console.WriteLine(x.ToString());
When i execute this program, in the line
req.AddBody(json);
my system crash and give error message that:
An unhandled exception of type 'System.StackOverflowException' occurred in System.Windows.Forms.dll
How can i post complex JSON by using C#.NET ?
You have two problems here:
You need to set RequestFormat = DataFormat.Json before the call to AddBody:
req.RequestFormat = DataFormat.Json;
req.AddBody(json);
Without setting the parameter, RestSharp tries to serialize the JObject to XML and falls into an infinite recursion somewhere -- most likely trying to serialize JToken.Parent.
The more recent versions of RestSharp no longer use Json.NET as their JSON serializer:
There is one breaking change: the default Json*Serializer* is no longer
compatible with Json.NET. To use Json.NET for serialization, copy the code
from https://github.com/restsharp/RestSharp/blob/86b31f9adf049d7fb821de8279154f41a17b36f7/RestSharp/Serializers/JsonSerializer.cs
and register it with your client:
var client = new RestClient();
client.JsonSerializer = new YourCustomSerializer();
RestSharp's new built-in JSON serializer doesn't understand JObject so you need to follow the instructions above if you are using one of these more recent versions, Create:
public class JsonDotNetSerializer : ISerializer
{
private readonly Newtonsoft.Json.JsonSerializer _serializer;
/// <summary>
/// Default serializer
/// </summary>
public JsonDotNetSerializer() {
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer {
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
/// <summary>
/// Default serializer with overload for allowing custom Json.NET settings
/// </summary>
public JsonDotNetSerializer(Newtonsoft.Json.JsonSerializer serializer){
ContentType = "application/json";
_serializer = serializer;
}
/// <summary>
/// Serialize the object as JSON
/// </summary>
/// <param name="obj">Object to serialize</param>
/// <returns>JSON as String</returns>
public string Serialize(object obj) {
using (var stringWriter = new StringWriter()) {
using (var jsonTextWriter = new JsonTextWriter(stringWriter)) {
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string DateFormat { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string RootElement { get; set; }
/// <summary>
/// Unused for JSON Serialization
/// </summary>
public string Namespace { get; set; }
/// <summary>
/// Content type for serialized content
/// </summary>
public string ContentType { get; set; }
}
And then do:
RestRequest req = new RestRequest(#"apicall/smsmessaging/v2/outbound/3313/requests", Method.POST);
req.JsonSerializer = new JsonDotNetSerializer();