C# AWS Lambda API Gateway from browser - parameters not working - c#

With a simple C# AWS Lambda
public string FunctionHandler(string myParam1, ILambdaContext context)
{
return myParam1;
}
How should I pass parameters to an AWS Lambda function + API Gateway, via a browser GET request?
I'd like something like this for example:
https://[API ID].execute-api.[REGION].amazonaws.com/myFunc?myParam1=myValue1
In the browser it says {"message":"Internal Server Error"}
In the logs it says Error converting the Lambda event JSON payload to a string. JSON strings must be quoted, for example "Hello World" in order to be converted to a string: The JSON value could not be converted to System.String.
Without parameters it works, for example:
public string FunctionHandler(ILambdaContext context)
{
return Utf8Json.JsonSerializer.ToJsonString(context);
}
When sending a GET request in the browser https://[API ID].execute-api.[REGION].amazonaws.com/myFunc returns successfully {"AwsRequestId":"86ca2da9-438c-4865-8a0b-29d3ced37176","FunctionName":....

Ok I found a solution, instead of using the built-in parsing of parameters, it's possible to read a full JSON of parameters by reading a stream instead:
public string FunctionHandler(Stream requestStream, ILambdaContext context) { ... }
The requestStream here will have the parameters of GET/POST inside or a larger JSON, but have to be manually parsed. Note that the parameters may be sent b64 encoded (or probably also compressed). A good way would be to find a library which does this parsing.
In my case, I also write the consumer JS code, so I can ensure the parameters will always come in the same fashion so my problem is solved but if someone has knows a good library for this, please tell.
Example of a manual POST request data extraction which also supports b64 encoding:
public class StreamBody
{
public string body;
public bool isBase64Encoded;
}
public string FunctionHandler(Stream requestStream, ILambdaContext context)
{
using var sr = new StreamReader(requestStream);
var input = sr.ReadToEnd();
var sbody = Utf8Json.JsonSerializer.Deserialize<StreamBody>(input);
var body = !sbody.isBase64Encoded ? sbody.body : Encoding.UTF8.GetString(Convert.FromBase64String(sbody.body));

Related

Unescape json response in REST API

I have a REST API written in C #. Method DoQuery send query to the database, and get json as response
public async Task<QueryResponseDto> DoQuery(string request)
{
return await _dbConnection.QuerySingleAsync<QueryResponseDto>("select web.json_request('" + request + "') as response;",commandTimeout:600);
}
...
[HttpPost]
[Route("DoQuery")]
public async Task<string> DoQuery([FromBody] object body)
{
var r = await _dataService.DoQuery(JsonSerializer.Serialize(body));
return r.response;
}
When I run query in database, I get correct json answer like this
[
{
"contractor":{
"ek2id":"91707d21-50f3-4aa4-8209-e80b963da99d",
"externalids":[
{
"externalsource":"SBL",
"externalid":"1-4OB8C75"
}
]
}
}
]
But when I run method DoQuery from PostMan I get escaped response like this
"[{\"contractor\":{\"ek2id\":\"91707d21-50f3-4aa4-8209-e80b963da99d\",\"externalids\":[{\"externalsource\":\"SBL\",\"externalid\":\"1-4OB8C75\"}]}}]"
How can I get normal unescaped response from REST API?
When you query the database it formats the JSON for you when it displays the output. The actual value in the database could be some UTF8, or similar encoding.
If escape is causing issue you could try storing actual UTF8 value for quotes in the string. But in both the cases you need to encode the response.

C# POST request with Json containing an array

I have been using some simple requests in past to send JSON and they have been working fine. I basically take simple string, convert it to UTF-8 using
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
and send this using HttpWebRequest.
Now I have received a task to send a new POST request which will have an array of Ids and be of type
{
"GroupID": "Named_ID",
"ChildIds": [
"76197272-24E4-4DD2-90B8-46FDDCC0D6CA",
"D2B3A1AC-ACF6-EA11-A815-000D3A49E4F3",
"ED53D968-00F4-EA11-A815-000D3A49E4F3"
]
}
The ChildIds are available in a List(String []) with me. I could loop through the records and create a similar long string and then convert to byteArray in UTF8 but I feel this can be done in a simpler way. Examples that I have seen on the forum of using array in JSON always appear to use JsonConvert.SerializeObject but I am not clear if I should use this and if I do then do I convert the serialised object to UTF8 like I did earlier and is it then ok to use in the HttpWebRequest.
Can someone please advice on the approach I should take.
Create a class structure to match your json,
public class Data
{
public string GroupId {get;set;}
public List<string> ChildIds {get;set;}
}
serialize the data with JsonConvert or any other.
var json = JsonConvert.SerializeObject<Data>(str) ;
Post the serialized data with httpclient assuming you have it injected in your class already.
var data = new StringContent(json, Encoding.UTF8, "application/json");
var post = await client.PostAsync(url, data);

No MediaTypeFormatter is available to read an object of type 'HttpRequestMessage' from content with media type 'multipart/form-data'

I'm adapting a project recently started in .NET Standard to use Azure Functions. I have an HTTP trigger that I am posting directly to from a form. There are only 2 fields: a number input, and a file upload.
When using the function without referencing any other libraries, I cannot use ReadAsFormDataAsync on the HttpRequestMessage. I receive a:
System.Net.Http.UnsupportedMediaTypeException: No MediaTypeFormatter is
available to read an object of type 'FormDataCollection' from content with
media type 'multipart/form-data'.
I can use ReadAsMultipartAsync and manage to get the post values.
When I reference a .NET Standard library though, I can't even enter the function as it gets completely rejected:
System.Net.Http.UnsupportedMediaTypeException: No MediaTypeFormatter is
available to read an object of type 'HttpRequestMessage' from content with
media type 'multipart/form-data'
I tried creating a brand new skeleton .NET Standard library and referencing that and same thing.
As an additional reference I found this post, but I don't seem to be having the same issue.
Was going to file an issue but decided to try here first. Any ideas?
EDIT: This also happens when the enctype is application/x-www-form-urlencoded.
As far as I know, the "ReadAsFormDataAsync" method only accepts "application/x-www-form-urlencoded" type of contents. It doesn't support get the 'multipart/form-data' type of contents.
So if you want to send multiple part of contests, you need use "ReadAsMultipartAsync" method.
More details about how to use "ReadAsMultipartAsync" method in azure function, you could refer to this codes:
using System.Net;
using System.Net.Http;
using System.IO;
using System.Collections.Specialized;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
string result = "- -";
if (req.Content.IsMimeMultipartContent())
{
var provider = new MultipartMemoryStreamProvider();
req.Content.ReadAsMultipartAsync(provider).Wait();
foreach (HttpContent ctnt in provider.Contents)
{
//now read individual part into STREAM
var stream = ctnt.ReadAsStreamAsync();
return req.CreateResponse(HttpStatusCode.OK, "Stream Length " + stream.Result.Length);
using (var ms = new MemoryStream())
{
//do something with the stream
}
}
}
if (req.Content.IsFormData())
{
NameValueCollection col = req.Content.ReadAsFormDataAsync().Result;
return req.CreateResponse(HttpStatusCode.OK, $" {col[0]}");
}
// dynamic data = await req.Content.ReadAsFormDataAsync();
// Fetching the name from the path parameter in the request URL
return req.CreateResponse(HttpStatusCode.OK, "Doesn't get anything " + result);
}
Result:
Have you tried setting the content type as JSON on your HTTP request headers?
Content-Type=application/json

Serialization Exception: unexpected character '<'

I have this simple method which suppose to get weather data, when I call it this error occur:
System.Runtime.Serialization.SerializationException was unhandled by user code
HResult=-2146233076
Message=There was an error deserializing the object of type UWpWeather.RootObject. Encountered unexpected character '<'.
public async static Task <RootObject> GetWeather(double lat, double lng) {
var http = new HttpClient();
var response = await http.GetAsync("http://api.openweathermap.org/data/2.5/forecast/daily?q=leeds&type=accurate&mode=xml&units=metric&cnt=3&appid= MY AIP-KEY");
string result = await response.Content.ReadAsStringAsync();
var serializer = new DataContractJsonSerializer(typeof (RootObject));
var ms = new MemoryStream(Encoding.UTF8.GetBytes(result));
var data = (RootObject) serializer.ReadObject(ms);
return data;
}
The API does not honour any of the HTTP content or Accept headers you pass through on the request, but rather it sets the content-type of the response based on the query string parameter.
Your initial URL:
http://api.openweathermap.org/data/2.5/forecast/daily?q=leeds&type=accurate&mode=xml&units=metric&cnt=3&appid=
MY AIP-KEY"
What it should be:
http://api.openweathermap.org/data/2.5/forecast/daily?q=leeds&type=accurate&mode=json&units=metric&cnt=3&appid=
MY AIP-KEY"
That should allow you to deserialize it into your RootObject correctly.
Caveat: I don't have your root object implementation, so I could only verify up until getting a JSON-formatted response back.
I found the answer, my first mistake was using Xml instead of Json when calling my data. second, when I used this website (json2csharp) to convert Json to series of classes that represent my Json it created it fine except one which created as a list public List<List> list { get; set; }
I simply removed that one and my code now is working just fine. thanks all for your support.

How do I get Web API 2 to return JSON and no other content type?

In the latest Web API 2, how do I configure it so that it will only return a reply if the Accept header is application/json? This API will only support json, if any other accept header is sent then an error must be thrown. There will be no xml and even no html interface.
If the client asks for xml or html or anything, we need to throw an error to let them know they used the wrong accept type. We must not mask this problem by replying with the correct json when they have requested a type that is not actually supported.
var request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/json";
var response = request.GetResponse();
And the json result is returned successfully. But if there is any other Accept then an error is returned
var request = (HttpWebRequest)WebRequest.Create(url);
request.Accept = "application/xml"; // or text/html or text/plain or anything
var response = request.GetResponse();
Returns HTTP 501 Not Implemented or similar http error code.
This question is not a duplicate of How do I get ASP.NET Web API to return JSON instead of XML using Chrome? - that question asks how to also return json. My question is how to only return json, and only if the client asks for json. If the client asks for any other type like xml or html, then an error is returned.
This page shows how to access content negotiation directly. You could conceivably instead pass some filtered subset of this.Configuration.Formatters containing only the desired formatters to IContentNegotiator.negotiate, like so:
ContentNegotiationResult result = negotiator.Negotiate(
typeof(Product), this.Request, this.Configuration.Formatters.Where(/* some expression that excludes all but the json formatter */);
This looks quite clumsy and would be a lot of dull boilerplate, so Javad_Amiry's answer is probably better, but this is another option that might be useful in specific cases.
You can clear all formatters except JSON:
configuration.Formatters.Clear();
configuration.Formatters.Add(new JsonMediaTypeFormatter());
Or you can change the default Web API’s content negotiation mechanism:
public class JsonContentNegotiator : IContentNegotiator
{
private readonly JsonMediaTypeFormatter _jsonFormatter;
public JsonContentNegotiator(JsonMediaTypeFormatter formatter)
{
_jsonFormatter = formatter;
}
public ContentNegotiationResult Negotiate(Type type, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
{
var result = new ContentNegotiationResult(_jsonFormatter, new MediaTypeHeaderValue("application/json"));
return result;
}
}
// in app_start:
var jsonFormatter = new JsonMediaTypeFormatter();
config.Services.Replace(typeof(IContentNegotiator), new JsonContentNegotiator(jsonFormatter));
See the article.
UPDATE:
Well, if you want to return a HTTP error on non-json request, you can do it by implementing a custom IHttpModule for checking header. But, for self-host apps it won't work. So, it's better to use extend a custom DelegatingHandler. For example, you can use this one:
public class FilterJsonHeaderHandler : DelegatingHandler {
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken){
if (request.Headers.Accept.All(a => a.MediaType == "application/json")){
// if we have only application/json, so the pipeline continues
return base.SendAsync(request, cancellationToken);
}
// otherwise, do whatever you want:
var response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
var completionSource = new TaskCompletionSource<HttpResponseMessage>();
completionSource.SetResult(response);
return completionSource.Task;
}
}
and register it in app_start:
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.MessageHandlers.Add(new FilterJsonHeaderHandler());
// your other settings...
}
}
NOTE: the code is not tested. Please let me know if there is any error.

Categories