AWS Lambda C# - Accessing custom context - c#

I have a simple Lambda function written in .NET Core (C#) that uses the APIGatewayProxyRequest object to go through all the request properties.
If I test this lambda function (from AWS Lambda), and pass it a sample event config that contains basic information:
I can get this information like so:
public string FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context)
logger.Logger.Log($"Body: {request.Body} \n");
logger.Logger.Log($"Path: {request.Path} \n");
logger.Logger.Log($"Resource: {request.Resource} \n");
How is it that I can access custom context or authorizer values from the same data:
I have tried:
logger.Logger.Log($"RequestContext Authorizor: {request.RequestContext.Authorizer} \n");
Including it's different properties (StringKey, PrincipleId etc.)
It seems from Node.js, this would be simply achieved by using this:
event.requestContext.authorizer.customKey
There is no such thing in C#?

So, after spending 3 days troubleshooting this and with the help of the AWS engineers, this is what I've found;
There is a limitation with accessing the $context, $authorizer or any other custom variables from a Lambda function, written through .Net Core in C#
A new service request is being created for the AWS team for this.
To explain:
Currently, in node.js you have access to the entire payload of data being passed through to the Lambda function (within the event parameter), which includes all custom variables (you could access it directly - for the question example, like this: event.requestContext.authorizer.customKey.
This is not the same for the C# equivalent - which uses the APIGatewayProxyRequest request object within a Lambda function. So although you have access to the entire payload (including all the custom variables) within node, within C#, you only have access to the APIGatewayProxyRequest object. Properties of which can be found here:
Or in short:
public string Body { get; set; }
public IDictionary<string, string> Headers { get; set; }
public string HttpMethod { get; set; }
public bool IsBase64Encoded { get; set; }
public string Path { get; set; }
public IDictionary<string, string> PathParameters { get; set; }
public IDictionary<string, string> QueryStringParameters { get; set; }
public ProxyRequestContext RequestContext { get; set; }
public string Resource { get; set; }
public IDictionary<string, string> StageVariables { get; set; }
Being based off an object, this will not allow access to custom or "unknown" properties, even though they are part of the payload.
Long story short, as of right now: if you wish you work with custom variables of any sort, you would either need to code it through node(event) / python, or possibly overwrite an existing property within the APIGatewayProxyRequest object.
UPDATE:
There is a work around to accessing the entire payload of the data coming in:
A work around till then is have your Lambda function take in a System.IO.Stream instead of APIGatewayProxyRequest. Then you have access to the original JSON which you can parse yourself. You can grab the information you need from that JSON and then deserialize JSON to APIGatewayProxyRequest as well.

I don't know if people are wondering about this even today (after almost 3 years - I was), I have figured out a way. Could be helpful.
Note: This is only for a lambda built using the DotNet SDK.
In the AWS API G/W mapping template, you need to create a JSON template which matches the ApiGatewayProxyRequest class so that it can be deserialized. In the example below, I am trying to extract the body from the request and the source ip and user agent from the context of the request.
{
"Body": "$util.escapeJavaScript($input.body)",
"RequestContext" : {
"Identity" : {
"SourceIp" : "$context.identity.sourceIp",
"UserAgent" : "$context.identity.userAgent"
},
"RequestId" : "$context.requestId"
}
}
Now, for accessing these values in your lambda, you can do the following:
public async Task<SomeResponse> FunctionHandler(APIGatewayProxyRequest gatewayProxyRequest, ILambdaContext context)
{
var requestContext = gatewayProxyRequest.RequestContext;
var sourceIP = requestContext?.Identity?.SourceIp;
var body = gatewayProxyRequest.Body;
.
.
.
}

Related

How do I use the #odata.nextlink parameter returned in the Reporting API response in .net C# application

I am retrieving data from an odata service. The response contains a link for loading more data i.e. odata.nextLink which I need to retrieve to load more data(odata.nextLink). How do I do this using C# code? The server's response is something like this:
{
"odata.metadata":"http://localhost:60497/odata/$metadata#tables","value":[
{"id":001,"name":"abc" }
.
.
],
"odata.nextLink":"http://localhost:60497/odata/tables?$skip=10"
}
The presence of the odata.nextLink element is used to indicate that there are more results available.
If you simply want to fetch all results then you would keep fetching additional results from each successive nextLink URI until you receive a response that does not contain a nextLink element.
In the most simple case you could simply have a while loop, however in most situations if a query is returning a large set of results you would want to retrieve some and then offer the user the means to request more rows.
In C# you might be using the OData Client library from Microsoft which allows you to perform LINQ queries on OData services.
Microsoft's documentation has a comprehensive example at https://learn.microsoft.com/en-gb/odata/client/pagination
Create a class for the Json Object which is returned, Like this
public class Value
{
public int id { get; set; }
public string name { get; set; }
}
public class Root
{
[JsonProperty("odata.metadata")]
public string OdataMetadata { get; set; }
public List<Value> value { get; set; }
[JsonProperty("odata.nextLink")]
public string OdataNextLink { get; set; }
}
Try to deserialize the Json object returned, here data is going to be the Json Object in String Format.
var RootObj = JsonConvert.DeserializeObject<Root>(data);
From this RootObj, you will be able to get the OdataNextLink value and other values you need, which you can store in your local variable.
Let me know if you have more questions on this.

Managing multiple versions of object in JSON

I have a class in C#, that has a number of variables. Let's call it "QuestionItem".
I have a list of this object, which the user modifies, and then sends it via JSON serialization (with Newtonsoft JSON library) to the server.
To do so, I deserialize the objects that are already in the server, as a List<QuestionItem>, then add this new modified object to the list, and then serialize it back to the server.
In order to display this list of QuestionItems to the user, I deserialize the JSON as my object, and display it somewhere.
Now, the problem is - that I want to change this QuestionItem and add some variables to it.
But I can't send this NewQuestionItem to the server, because the items in the server are of type OldQuestionItem.
How do I merge these two types, or convert the old type to the new one, while the users with the old version will still be able to use the app?
You are using an Object Oriented Language, so you might aswell use inheritance if possible.
Assuming your old QuestionItem to be:
[JsonObject(MemberSerialization.OptOut)]
public class QuestionItem
{
[JsonConstructor]
public QuestionItem(int Id, int Variant)
{
this.Id = Id;
this.Variant = Variant;
}
public int Id { get; }
public int Variant { get; }
public string Name { get; set; }
}
you can extend it by creating a child class:
[JsonObject(MemberSerialization.OptOut)]
public class NewQuestionItem : QuestionItem
{
private DateTime _firstAccess;
[JsonConstructor]
public NewQuestionItem(int Id, int Variant, DateTime FirstAccess) : base(Id, Variant)
{
this.FirstAccess = FirstAccess;
}
public DateTime FirstAccess { get; }
}
Note that using anything different than the default constructor for a class requires you to use the [JsonConstructor] Attribute on this constructor and every argument of said constructor must be named exactly like the corresponding JSON properties. Otherwise you will get an exception, because there is no default constructor available.
Your WebAPI will now send serialized NewQuestionItems, which can be deserialized to QuestionItems. In fact: By default, JSON.NET as with most Json libraries, will deserialize it to any object if they have at least one property in common. Just make sure that any member of the object you want to serialize/desreialize can actually be serialized.
You can test the example above with the following three lines of code:
var newQuestionItem = new NewQuestionItem(1337, 42, DateTime.Now) {Name = "Hello World!"};
var jsonString = JsonConvert.SerializeObject(newQuestionItem);
var oldQuestionItem = JsonConvert.DeserializeObject<QuestionItem>(jsonString);
and simply looking at the property values of the oldQuestionItem in the debugger.
So, this is possible as long as your NewQuestionItem only adds properties to an object and does neither remove nor modify them.
If that is the case, then your objects are different and thus, requiring completely different objects with a different URI in your API, as long as you still need to maintain the old instance on the existing URI.
Which brings us to the general architecture:
The most clean and streamline approach to what you are trying to achieve is to properly version your API.
For the purpose of this link I am assuming an Asp.NET WebApi, since you are handling the JSON in C#/.NET. This allows different controller methods to be called upon different versions and thus, making structural changes the resources your API is providing depending on the time of the implementation. Other API will provide equal or at least similar features or they can be implemented manually.
Depending on the amount and size of the actual objects and potential complexity of the request- and resultsets it might also be worth looking into wrapping requests or responses with additional information. So instead of asking for an object of type T, you ask for an Object of type QueryResult<T> with it being defined along the lines of:
[JsonObject(MemberSerialization.OptOut)]
public class QueryResult<T>
{
[JsonConstructor]
public QueryResult(T Result, ResultState State,
Dictionary<string, string> AdditionalInformation)
{
this.Result = result;
this.State = state;
this.AdditionalInformation = AdditionalInformation;
}
public T Result { get; }
public ResultState State { get; }
public Dictionary<string, string> AdditionalInformation { get; }
}
public enum ResultState : byte
{
0 = Success,
1 = Obsolete,
2 = AuthenticationError,
4 = DatabaseError,
8 = ....
}
which will allow you to ship additional information, such as api version number, api version release, links to different API endpoints, error information without changing the object type, etc.
The alternative to using a wrapper with a custom header is to fully implement the HATEOAS constraint, which is also widely used. Both can, together with proper versioning, save you most of the trouble with API changes.
How about you wrapping your OldQuestionItem as a property of QuestionItem? For example:
public class NewQuestionItem
{
public OldQuestionItem OldItem { get; set; }
public string Property1 {get; set; }
public string Property2 {get; set; }
...
}
This way you can maintain the previous version of the item, yet define new information to be returned.
Koda
You can use something like
public class OldQuestionItem
{
public DateTime UploadTimeStamp {get; set;} //if less then DateTime.Now then it QuestionItem
public string Property1 {get; set; }
public string Property2 {get; set; }
...
public OldQuestionItem(NewQuestionItem newItem)
{
//logic to convert new in old
}
}
public class NewQuestionItem : OldQuestionItem
{
}
and use UploadTimeStamp as marker to understand, what Question is it.

ServiceStack Json deserializing incorrectely

I've got a RequestDto, let's say Class A Dto, it contains a self defined type property:
// C# code
public Class MyObject
{
public string A { get; set; }
public string B { get; set; }
}
public Class ADto
{
public List<MyObject> MO { get; set;}
}
When I am trying to send the Dto using Json, the Json object looks like this:
{"MO":[{"A":"String","B":"a"},{"A":"String","B":"b"}]}
but the object I am receiving will be null.
However if I change the Json string into:
{MO:[{A:"String",B:"a"},{A:"String",B:"b"}]}
I lose the quotation marks on the objects' names and it works.
The correct format of Json should include those quotation marks right?
Why is this happening?
ServiceStack does serializes and deserializes valid JSON which requires every property name to be quoted however you're saying that the text below works:
{MO:[{A:"String",B:"a"},{A:"String",B:"b"}]}
However this isn't valid JSON, it instead looks like ServiceStack's JSV Format
You haven't mentioned where you're sending the JSV Format or have provided the Raw HTTP Request (for us to work it out), but I'm assuming if you're using Postman (mentioned in your comments) than you're trying to send JSON in the ?QueryString which isn't allowed.
But ServiceStack does support sending complex object graphs on the QueryString using JSV.
Since you're sending a complex type you'd either POST the request as either JSON or www-form-urlencoded Form Data or if you want to pass it in the QueryString you need to convert it to JSV.
In future please include the Raw HTTP Request as this question lacks any context on where you're changing the JSON string, how you're trying to use it or what's actually being sent, where you're sending it to, etc - making it impossible to guess what the issue is.
Change your class to
public Class MyObject
{
public string Mobile { get; set; }
public string Name { get; set; }
}
public Class ADto
{
public List<MyObject> MO { get; set;}
}
Then your json should be
{MO:[{Mobile:"0556604",Name:"Peter"},{Mobile:"4565466",Name:"John"}]}

Dictionary<string, object> serialization in OData using ASP.Net MVC 5 and Web API 2.0

I am using OData services over the new Web API for MVC 5 and I am experiencing some problems.
I have followed this tutorial to start develop OData services and almost everything is right. The project builds, the routes work, and the URLs is working great.
One of my classes is called Post and have a property of type Dictionary<string, object> named Parameters, like below:
public partial class Post : IPost
{
public Guid Id { get; set; }
public string Author{ get; set; }
public string Content{ get; set; }
public DateTime PostedDate{ get; set; }
public List<Photo> Pictures { get; set; }
public Dictionary<string, object> Parameters { get; set; }
}
The URL to list all the Post objects (http://localhost/odata/Posts) is working fine when I perform a get request in browser. All the objects are successfully serialized.
But importing the OData Metadata as a Service Reference in another application, when I get the list of Post entities, I get the error below:
An exception of type 'System.InvalidOperationException' occurred in
Microsoft.Data.OData.dll but was not handled in user code
Additional information: The type
'WebApplication1.ServiceReference2.System.Collections.Generic.KeyValuePair_2OfString_Object'
has no settable properties.
How to resolve this problem for import Service References with success?
Some days later I tried a lot of different tests.
Dictionary<string, object> => throw the original error
Dictionary<string, string> => same error
List<KeyValuePair<string, object>> => same error
List<KeyValuePair<string, string>> => same error
I realized the KeyValuePair class was the problem, but I didn't know why.
So, I created a class named Parameter like below:
public class Parameter
{
public string Key { get; set; }
public string Value { get; set; }
}
...I changed the list type to List<Parameter> and it worked!
After thinking more about the problem, I realized one important sentence in the error:
'WebApplication1.ServiceReference2.System.Collections.Generic.KeyValuePair_2OfString_Object'
has no settable properties
The dictionaries are nothing more than lists of KeyValue pairs, and the KeyValuePair<TKey, TValue> struct have two properties, named "Key" and "Value", which have only get accessors. So, they are readonly.
We may conclude the OData Service Reference can't deserialize the readonly properties, by some reason I don't know yet.
My workaround worked, but I am wondering if the Service Reference may have someday a reflection way to set this properties.
For anyone who is reading this since > 2015: OData V4 supports open types: https://learn.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/use-open-types-in-odata-v4
Your dictionary should be of type:
IDictionary<string, object>.
Odata will then create an Open Type for you.
web API OData doesn't support open types yet which I think is what you are looking for. Dictionary<string, object> doesn't model well in EDM.
Could you explain further what Post.Parameters is supposed to contain to see if there is a better way to model the Post type?
OData now supports Open Types where you can use an IDictionary and OData creates the Open Type for you. But the example provided in the documentation is a straight forward case, where the class with Open Type has a dedicated controller.
In my case, I have a class that is a field of a bigger class being returned. The bigger class is an entity set with its own controller. The smaller class doesn't have a controller, and has a dictionary.
The set up look like this:
// Has its own controller and is an entity set
public class BiggerClass
{
... // fields that show up in response
[Contained]
public SmallerClass smallClass; // Data was partially available in the response
}
public class SmallerClass
{
... // fields that show up in response
[Contained]
public Dictionary<string, Dictionary<string, InnerClass>> Mapping; // Didn't show up in the response, but verified it was being set properly
}
// Since the above mapping didn't show up, all of the objects of this class were missing
public class InnerClass
{
... // Only has primitives
}
The dictionary's content was not showing up. In addition to the Contained attributes, I had to add the following to my Odata builder:
builder.AddComplexType(typeof(SmallerClass));
builder.AddComplexType(typeof(Dictionary<string, Dictionary<string, InnerClass>>));
Note that adding InnerClass as a complex type didn't do anything, and the dictionary itself (the data structure not showing up) had to be added.

Getting json string programmatically C#

I am trying to get json string from a url by using following line of code. When I enter the url in Google Chrome I get whole string and data. but when I use my code it returns only this line of string {"expand":"projects","projects":[]} it is exact what I get when I enter the url in IE 10. How can I get same data that I get when I enter the url in Chrome? here is my code to get the json data. var jsonStr = new WebClient().DownloadString("https_my_url");
You will need to authenticate the request via WebClient.
See this answer for how to do that if the site uses Forms Authentication.
WebClient accessing page with credentials
You need to use a JSON parser to turn it in to something useful. ServiceStack.Text (available via NuGet or download) has an excellent one that can turn json strings in to first-class POCOs.
using ServiceStack.Text;
public sealed class SomeObject
{
public string expand { get; set; }
public List<string> projects {get; set; }
}
And convert thusly:
SomeObject object = jsonString.FromJson<SomeObject>();
Note, I would make my POCOs a little more c# friendly and alias away the lower-case:
using ServiceStack.Text;
using ServiceStack.DataAnnotations;
public sealed class SomeObject
{
[Alias("expand")]
public string Expand { get; set; }
[Alias("projects")]
public List<string> Projects {get; set; }
}

Categories

Resources