I'm using the local.settings.json file to store application settings for my Azure Function, as suggested here. I can access the Values of the application settings in the following example
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"AzureWebJobsDashboard": ""
},
"ConnectionStrings": {
"SQLConnectionString": "myConnectionString"
}
}
by using ConfigurationManager.ApplicationSettings["someValue"] or the connection string by using ConfigurationManager.ConnectionStrings["SQLConnectionString"].
However, when I try to insert an array as one of the Values:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"AzureWebJobsDashboard": "",
"myArray": [
{
"key1": "value1",
"key2": "value2"
},
{
"key1": "value3",
"key2": "value4"
}
]
},
"ConnectionStrings": {
"SQLConnectionString": "myConnectionString"
}
}
I start getting exceptions (when I try to access the connection string, for instance). So my guess is that I'm not using the correct format for the array.
Can arrays be used in the local.settings.json file? And if they can, what's the correct format?
According to the azure function source code Azure.Functions.Cli/Common/SecretsManager.cs, you could find it has a AppSettingsFile class, which used to read the setting from the local.settings.json file.
Some part of the AppSettingsFile class:
public AppSettingsFile(string filePath)
{
_filePath = filePath;
try
{
var content = FileSystemHelpers.ReadAllTextFromFile(_filePath);
var appSettings = JsonConvert.DeserializeObject<AppSettingsFile>(content);
IsEncrypted = appSettings.IsEncrypted;
Values = appSettings.Values;
ConnectionStrings = appSettings.ConnectionStrings;
Host = appSettings.Host;
}
catch
{
Values = new Dictionary<string, string>();
ConnectionStrings = new Dictionary<string, string>();
IsEncrypted = true;
}
}
public bool IsEncrypted { get; set; }
public Dictionary<string, string> Values { get; set; } = new Dictionary<string, string>();
public Dictionary<string, string> ConnectionStrings { get; set; } = new Dictionary<string, string>();
According to the code, it use JsonConvert.DeserializeObject method to convert the json file to appSettings object.
But the appSettings.Values property is directory type, it doesn't support array. So I don't suggest you use array as its setting.
I suggest you could try to convert the array as two string value. This will work well.
Like this:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"AzureWebJobsDashboard": "",
"arrary1-key1": "value1",
"arrary1-key2": "value2",
"arrary2-key1": "value3",
"arrary2-key2": "value4"
},
"ConnectionStrings": {
"SQLConnectionString": "myConnectionString"
}
}
Related
I have an endpoint that returns an IAsyncEnumerable
[HttpPost("GetByDates")]
[ProducesResponseType(typeof(IAsyncEnumerable<DayModel>), StatusCodes.Status200OK)]
public async IAsyncEnumerable<DayModel> GetByDates([FromBody] DayModelGetByDatesRequest request)
{
await foreach (var dayModel in _dayService.GetAsync(request.channelGuid, request.dates.ToArray(), request.onlyPublished, request.IncludeDiscardedScheduledItems))
{
yield return dayModel;
};
}
The generated .json schema looks like this:
"/Private/Days/GetByDates": {
"post": {
"tags": [
"Days"
],
"operationId": "Days_GetByDates",
"requestBody": {
"x-name": "request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DayModelGetByDatesRequest"
}
}
},
"required": true,
"x-position": 1
},
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Day"
}
}
}
}
}
}
}
}
and the Nswag is configured like this:
services.AddOpenApiDocument(configure =>
{
configure.Title = "MyAppName (Private)";
configure.DocumentName = "private";
configure.SchemaType = SchemaType.OpenApi3;
configure.SchemaNameGenerator = new CustomNameGenerator();
configure.AddOperationFilter(new RequireUserHeaderParameterFilter().Process);
configure.AddSecurity("Bearer", new OpenApiSecurityScheme()
{
In = OpenApiSecurityApiKeyLocation.Header,
Description = "Please enter the word \"Bearer\" followed by space and token",
Name = "Authorization",
Type = OpenApiSecuritySchemeType.ApiKey,
});
configure.ApiGroupNames = new string[] { "Private" };
});
And another project uses the .json schema to genereate a client of its own that seems to use Newtonsoft Json instead of System.Text.Json
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.18.0.0 (NJsonSchema v10.8.0.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class TablaApiClient
{
private string _baseUrl = "https://localhost:5102";
private System.Net.Http.HttpClient _httpClient;
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
public TablaApiClient(System.Net.Http.HttpClient httpClient)
{
_httpClient = httpClient;
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
}
private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
{
var settings = new Newtonsoft.Json.JsonSerializerSettings();
UpdateJsonSerializerSettings(settings);
return settings;
}
public string BaseUrl
{
get { return _baseUrl; }
set { _baseUrl = value; }
}
protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }
partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
The endpoint doesn't serialize the IAsyncEnumerable and return an ICollection instead:
The Swagger is configured like so:
services.AddOpenApiDocument(configure =>
{
configure.Title = "My App";
configure.SchemaType = NJsonSchema.SchemaType.OpenApi3;
configure.AddSecurity("AzureAsIdentityProvider", new OpenApiSecurityScheme
{
Type = OpenApiSecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = $"{settings.Instance}/{settings.TenantId}/oauth2/v2.0/authorize",
TokenUrl = $"{settings.Instance}/{settings.TenantId}/oauth2/v2.0/token",
}
}
});
configure.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("AzureAsIdentityProvider"));
});
Is there a way for the generated client to properly serialize and understand it is working towards an IAsyncEnumerable endpoint so that I can work with the stream instead of fully buffered collection?
I read that System.Text.Json serializes IAsyncEnumerable out of the box. Is there a way to get Swagger to use that instead of Newtonsoft?
config.GetValue<SubscriberKind?> is throwing the following exception if Kind in appsettings.json is set to something which cannot be found in the enum. How do I fix it?
System.InvalidOperationException: Failed to convert configuration value at 'Subscriber:Kind' to type 'Subscriber.Kinds.SubscriberKind'.
How do I fix it or is there a better solution which doesn't fail in such way, I don't mind if it's not enum?
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ProtoActor": {
"AdvertisedHost": "localhost"
},
"Subscriber": {
"Exchange": "ftx",
"Kind": "DropCopy",
}
}
internal static class ConfiguratorFactory
{
public static ISubscriptionConfigurator Create(IConfiguration config)
{
var subscriberKind = config.GetValue<SubscriberKind?>("Subscriber:Kind");
return subscriberKind switch
{
SubscriberKind.UserTrades => new UserTrades.FtxConfigurator(config),
_ => throw new ArgumentOutOfRangeException(nameof(subscriberKind))
};
}
}
public enum SubscriberKind
{
UserTrades
}
You can read value as string and then try processing it as enum:
var value = config.GetValue<string>("Subscriber:Kind");
if (Enum.TryParse(value, out SubscriberKind kind))
{
...
}
I want to use an external API which has Swagger. In Swagger I am calling this url:
PATCH /rest/inventory/item/{id}
with parameters: X-Auth-Token, id and patchOperations which looks like this:
[
{
"op": "replace",
"path": "price",
"value": 6.2
}
]
And when I call this method with those parameters, it works. I get success code 200 and afterwards when I call the GET method I see that the price of the item has been updated to 6.2.
Now I want to do this in C#. I am already calling some GET methods from the same API successfully. This is my code for the PATCH method:
var model = new Dictionary<string, object>
{
{"op", "replace"},
{"path", "price"},
{"value", 6}
};
var blabla = await _httpProvider.PatchAsync($"https://{url}/server/rest/inventory/item/{id}", model, null, null, null, connection.Request.HeaderParameters);
public async Task<HttpResponseModel> PatchAsync<T>(string uri, T data, HttpClientHandler httpClientHandler = null, TimeSpan? timeout = null, string contentTypes = null, Dictionary<string, string> headerParameters = null)
{
using (var client = CreateHttpClient(httpClientHandler, timeout, contentTypes, headerParameters))
{
var requestContent = new StringContent(JsonConvert.SerializeObject(data));
var response = await client.PatchAsync(new Uri(uri), requestContent);
var result = new HttpResponseModel
{
Success = response.IsSuccessStatusCode,
ResponseContent = await response.Content.ReadAsStringAsync(),
ResponseTime = sw.Elapsed
};
return result;
}
}
Where is my mistake? I am getting error StatusCode: 500, ReasonPhrase: 'Server Error', Version: 1.1, Content: System.Net.Http.StreamContent
The mistake is that you're not pasting the same content, not quite anyway.
Your PATCH example is an array of a objects that has 3 properties, in your example there is only 1 element in the array, but it is still an array. Your C# is serialized into single object.
It's subtle but your JSON that you are sending is actually:
{
"op": "replace",
"path": "price",
"value": 6
}
So instead you need to send your dictionary or other object inside an array:
var model = new List<object> {
{
new Dictionary<string, object>
{
{ "op", "replace"},
{"path", "price"},
{"value", 6}
}
};
Ideally, in c# you would create a class to represent this DTO (Data Transfer Object), it can work with anonymous types or with dictionaries (a Dictionary<string,object> serializes into a single JSON object) but the code gets harder to manage over time.
public class DTO
{
public string op { get; set; }
public string path { get; set; }
public object value { get; set; }
}
...
var model = new List<DTO>
{
new DTO {
op = "replace",
path = "price",
value = 6
}
};
Hi We are facing issue while querying . The document exists in the database.
"Message: {\"Errors\":[\"Resource Not Found\"]}\r\nActivityId:
03866338-6596-49b6-8704-1726cb373bfb, Request URI:
/apps/ab277caf-ee90-4cc3-96cb-4d4ec5ae2b13/services/17e48284-a3a0-40c5-b5ec-40bd3f207472/partitions/27cb7777-5add-4f72-8a73-1fc8fe34e7bf/replicas/131603393672093060p/,
RequestStats: , SDK: Microsoft.Azure.Documents.Common/1.19.162.2"
Document in Database
{
"consumername": "testconsumer",
"tablename": "Table1",
"securityaccount": "v-naagga",
"logtime": "2018-01-13T21:42:21.3040338-08:00",
"securitydefinition": {
"tablename": "table1",
"ColumnList": {
"columnname": "name",
"columndatatype": "string"
},
"RowSecurity": {
"columnname": "address",
"operator": "operator",
"condition": "somecondition"
}
},
"id": "15554839-096d-4072-8f38-af2e9c64b452",
"_rid": "LmUiAONSDQQBAAAAAAAAAA==",
"_self": "dbs/LmUiAA==/colls/LmUiAONSDQQ=/docs/LmUiAONSDQQBAAAAAAAAAA==/",
"_etag": "\"00002e04-0000-0000-0000-5a5aedd60000\"",
"_attachments": "attachments/",
"_ts": 1515908566
}
Below is the update method code which is throwing this Error
{
try
{
RequestOptions options = new RequestOptions();
options.PartitionKey = new PartitionKey(id);
options.ConsistencyLevel = ConsistencyLevel.Session;
return await client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, SecurityCollectionId, id), item,options).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.Log(ErrorLevel.Error, ex.Message);
throw ex;
}
}
According to my observations, I think your issue should be partition key settings error.
Please refer to this official document.You need to provide the value of the partition key, not the name of the field which stores the partition key.
For example , my container is created like this:
The partition key is "name" for my collection here. You could check your collection's partition key.
And my documents as below :
{
"id": "1",
"name": "jay"
}
{
"id": "2",
"name": "jay2"
}
My partitionkey is 'name', so here I have two paritions : 'jay' and 'jay1'.
So, here you should set the partitionkey property to 'jay' or 'jay2',not 'name'.
try
{
RequestOptions options = new RequestOptions();
options.PartitionKey = new PartitionKey("jay");
options.ConsistencyLevel = ConsistencyLevel.Session;
return await client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, SecurityCollectionId, id), item,options).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.Log(ErrorLevel.Error, ex.Message);
throw ex;
}
Hope it helps you.
Update Answer :
I created a sample document as same as yours and replaced it successfully.
Please refer to my test code.
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using System;
using System.Linq;
namespace ConsoleApp2
{
class Program
{
private static DocumentClient client;
static string endpoint = "***";
static string key = "***";
static string database = "***";
static string collection = "***";
static void Main(string[] args)
{
client = new DocumentClient(new Uri(endpoint), key);
try
{
Sample querysample = client.CreateDocumentQuery<Sample>(
UriFactory.CreateDocumentCollectionUri(database, collection))
.Where(so => so.id == "1")
.AsEnumerable()
.First();
Console.WriteLine(querysample.tablename);
querysample.tablename = "Table2";
RequestOptions options = new RequestOptions();
options.PartitionKey = new PartitionKey("1");
options.ConsistencyLevel = ConsistencyLevel.Session;
var result = client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(database, collection, "1"), querysample, options).Result;
}
catch (Exception ex)
{
throw ex;
}
Console.ReadLine();
}
}
public class Sample
{
public string id { get; set; }
public string tablename { get; set; }
}
}
id is my partition key and the value is '1'. Would you please check the differences between our codes?
If any concern , please let me know.
In my case it was something basic, I had the container name spelt incorrectly. If you are getting a 404 worth checking you are indeed calling the right database and container
I've been making a Minecraft launcher. I have a long JSON file that contains all the libraries required to launch one version. A sample of this code:
{
"id": "1.6.2",
"time": "2013-08-06T14:00:00+03:00",
"releaseTime": "2013-07-05T16:09:02+03:00",
"type": "release",
"minecraftArguments": "--username ${auth_player_name} --session ${auth_session} --version ${version_name} --gameDir ${game_directory} --assetsDir ${game_assets}",
"libraries": [
{
"name": "net.sf.jopt-simple:jopt-simple:4.5"
},
{
"name": "com.paulscode:codecjorbis:20101023"
},
So you can see that there is an array called libraries. I can query the values in "name" using a foreach quite fine, but sometimes this occurs in the json:
{
"name": "org.lwjgl.lwjgl:lwjgl:2.9.0",
"rules": [
{
"action": "allow"
},
{
"action": "disallow",
"os": {
"name": "osx",
"version": "^10\\.5\\.\\d$"
}
}
]
},
So as you can see, there is an array inside called rules. I need to find the value of name inside os inside rules. Anything I've tried came up with an exception and wouldn't work. This is my code at the moment for parsing the code:
foreach (JToken lib in profilejsono["libraries"])
{
if ((lib["rules"][1]["os"].ToString() == "osx") || (lib["rules"][1]["os"].ToString() == "linux"))
{
availableLibs.Add(lib["name"].ToString());
}
}
The point of the code is not to add the library if it's for OSX or Linux (I'll add that functionality later). How do I do this?
One possible solution to your problem is to generalize the check for the OS name (and make it independant of the rules node depth). I suppose you have some library objects, because you need to deserialize the JSON input string to some object. The following code gives you the library names only for library nodes that have rule nodes with os nodes with a specified name:
JSON Test-Input file lib1.json:
{
"name": "lib1",
"rules": [
{
"action": "allow"
},
{
"action": "disallow",
"os": {
"name": "windows",
"version": "^10\\.5\\.\\d$"
}
}
]
}
JSON Test-Input file lib2.json:
{
"name": "lib2",
"rules": [
{
"action": "allow"
},
{
"action": "disallow",
"os": {
"name": "osx",
"version": "^10\\.5\\.\\d$"
}
}
]
}
JSON Test-Input file lib3.json:
{
"name": "lib3",
"rules": [
{
"action": "allow"
},
{
"action": "disallow",
"os": {
"name": "linux",
"version": "^10\\.5\\.\\d$"
}
}
]
}
JSON Test-Input file lib4.json:
{
"name": "lib4",
"rules": [
{
"action": "allow"
},
{
"action": "disallow"
}
]
}
JSON helper objects describing the library node, needed to deserialize the JSON inputs:
// helper classes describing the library object (for the JSON deserialization)
public class Library
{
public String Name { get; set; }
public Rules[] Rules { get; set; }
}
public class Rules
{
public String Action { get; set; }
public Os Os { get; set; }
}
public class Os
{
public String Name { get; set; }
public String Version { get; set; }
}
Code for fetching only matching libraries:
var jsonInput = #"d:\temp\json.net\lib{0}.json";
try
{
// load libraries / deserialize json
var libraries = new List<Library>();
Enumerable.Range(1, 4).ToList().ForEach(index =>
{
var json = File.ReadAllText(String.Format(jsonInput, index));
libraries.Add(JsonConvert.DeserializeObject<Library>(json));
});
// OS names to check if present in the current rules
var osNames = new List<String> { "osx", "linux" };
// check each library
foreach (var library in libraries)
{
// do we have rules?
if (library.Rules.Length > 0)
{
// get all non-empty OS nodes
var existingOsNodes = library.Rules.Where (r => r.Os != null).Select (r => r.Os).ToList();
// check for allowed OS name
var osIsPresent = existingOsNodes.Where (node => osNames.Contains(node.Name.ToLower())).Select (node => node.Name);
if (osIsPresent.Any())
{
Console.WriteLine(library.Name);
}
}
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
The output for the four given input files is:
lib2
lib3