Unreliable response from action - c#

I'm making a .Net Core 3.1 API and I'm having unreliable responses. Most of the time they are blank and very occasionally works as expected. I am using NewtonsoftJson and have added .AddNewtonsoftJson(); to the appropriate places in Startup.cs. No exceptions and a response of Ok for each.
Here is the full code of the action.
[HttpPost("/lobby/new")]
public IActionResult NewLobby([FromQuery] string name)
{
string playerGUID = Guid.NewGuid().ToString();
var lobby = new Models.Lobby
{
LobbyMembers = new List<Models.Lobby.LobbyMember>
{
new Models.Lobby.LobbyMember
{
PlayerID = playerGUID,
Name = name
}
},
HostID = playerGUID,
State = Models.Lobby.LobbyState.Open
};
_lobbies.InsertOne(lobby);
var response = new Models.JsonOut.LobbyInfo
{
LobbyID = lobby.MongoID,
PlayerID = playerGUID
};
return Json(response);
}
Here is an example of an expected and working body result:
{"playerID":"7183f34b-3524-45d0-a760-bcf62b1f4313","lobbyID":"5e8ae31b844735202ceb62c3"}
The unexpected result is 0B of Json in the body of the response.
Strangely it is working occasionally rather than not at all. Here is my network tab of Firefox developer tools. Highlighted are correct.
UPDATE: I made a mcve and the issue persists. I created a ASP.NET WebApi project with .NET Core 3.1 and disabled Configure for HTTPS.
public class TestController : Controller
{
[HttpPost("/lobby/new")]
public IActionResult NewLobby([FromQuery] string name)
{
string playerGUID = Guid.NewGuid().ToString();
var response = new Models.JsonOut.LobbyInfo
{
LobbyID = "test",
PlayerID = playerGUID
};
return Json(response);
}
}
I'll also attach the class being returned.
public class LobbyInfo
{
[JsonProperty("playerID")]
public string PlayerID { get; set; }
[JsonProperty("lobbyID")]
public string LobbyID { get; set; }
}

Really bizzare behaviour.. but it was CORS. Once I added CORS to my Startup.cs it worked.
It remains a mystery why there was no exceptions, no warnings in the browser, a response of OK from the server and even sometimes the correct body being given.

Related

C# .NET Core 3.1 Web API Post parameter is Null

I am trying to make a post request from WPF to Web API using the following code but the request parameter is always null.
Request Model
public class Document
{
public string FileName { get; set; }
public byte[] Buffer { get; set; }
}
public class Request
{
public string Uploader { get; set; }
public List<Document> Documents { get; set; }
}
WPF Client
var obj = new Request()
{
Uploader = "John Doe",
Documents = new List<Document>
{
new Document()
{
FileName ="I Love Coding.pdf",
Buffer = System.IO.File.ReadAllBytes(#"C:\Users\john.doe\Downloads\I Love Coding.pdf.pdf")
}
}
};
using (var http = new HttpClient())
{
var encodedJson = JsonConvert.SerializeObject(obj);
var conent = new StringContent(encodedJson, Encoding.UTF8, "application/json");
HttpResponseMessage response = await http.PostAsync("https://my-app.com/api/upload", conent);
response.EnsureSuccessStatusCode();
}
Web API
[Route("")]
public class AppController : ControllerBase
{
[HttpPost]
[Route("api/upload")]
public async Task<IActionResult> UploadDocumentsAsync([FromBody] Request request)
{
// request is always null when app is running in production
// https://my-app.com/api/upload
//request is not null when running on https://localhost:8080/api/upload
}
}
Please what am I missing in the above implementation?
The request parameter is not null on localhost but always null in production.
Please what am I missing in the above implementation? The request
parameter is not null on localhost but always null in production.
Well, not sure how are getting data on local server becuse, you are sending MultipartFormData means your POCO object and file buffer. As you may know we can send json object in FromBody but not the files as json. Thus, I am not sure how it working in local and getting null data is logical in IIS Or Azure.
what am I missing in the above implementation?
As explained above, for sending both POCO object and Files as byte or steam we need to use FromForm and beside that, we need to bind our request object as MultipartFormDataContent to resolve your null data on your UploadDocumentsAsync API action.
Required Change For Solution:
WPF:
In your WPF http request please update your request code snippet as following:
var obj = new Request()
{
Uploader = "John Doe",
Documents = new List<Document>
{
new Document()
{
FileName ="I Love Coding.pdf",
Buffer = System.IO.File.ReadAllBytes(#"YourFilePath")
}
}
};
var httpClient = new HttpClient
{
BaseAddress = new("https://YourServerURL")
};
var formContent = new MultipartFormDataContent();
formContent.Add(new StringContent(obj.Uploader), "Uploader");
formContent.Add(new StringContent(obj.Documents[0].FileName), "Documents[0].FileName");
formContent.Add(new StreamContent(new MemoryStream(obj.Documents[0].Buffer)), "Documents[0].Buffer", obj.Documents[0].FileName);
var response = await httpClient.PostAsync("/api/upload", formContent);
if (response.IsSuccessStatusCode)
{
var responseFromAzureIIS = await response.Content.ReadAsStringAsync();
}
Note: Class in WPF side would remain same as before. No changes required.
Asp.net Core Web API:
In asp.net core web API side you should use [FromForm] instead of [FromBody]
So your controller Action would as following:
[Route("")]
public class AppController : ControllerBase
{
[HttpPost]
[Route("api/upload")]
public async Task<IActionResult> UploadDocumentsAsync([FromForm] Request file)
{
if (file.Documents[0].Buffer == null)
{
return Ok("Null File");
}
return Ok("File Received");
}
}
Note: For remote debugging I have checked the logs and for double check I have used a simple conditionals whether file.Documents[0].Buffer == null. I have tested both in local, IIS and Azure and working accordingly.
Update POCO Class in API Project:
For buffer you have used byte for your WPF project but for Web API project update that to IFormFile instead of byte. It should be as following:
public class Document
{
public string FileName { get; set; }
public IFormFile Buffer { get; set; }
}
public class Request
{
public string Uploader { get; set; }
public List<Document> Documents { get; set; }
}
Output:
If you would like to know more details on it you could check our official document here

HttpClient.GetAsync() hangs until timeout

I want to get movie image link form TMDB website. I'm using Entity Framework Core 5, MVC
But when I am making API call in getImageLink() method and it will not go past this line:
var task = await httpClient.GetAsync(url).ConfigureAwait(false);
Im assuming the problem might be related to deadlocking or having HttpClient in using
using (var httpClient = new HttpClient())
It worked fine few days ago, but when I launched it today, it was not working (I didn't change anything)
Movie proporties:
public int id { get; set; }
//Movie info properties
[Column("title")]
public string title { get; set; }
[Required]
[Column("year")]
public int year { get; set; }
[Column("overview")]
public string overview { get; set; }
//NotMapped properties
[NotMapped]
public string imageLink { get { return getImageLink().Result; } }
id, title, year and overview are stored in postgre database.
getMovieLink method:
private async Task<string> getImageLink()
{
using (var httpClient = new HttpClient())
{
string key = Data.APIKeys.TheMovieDB_api_key;
string url = $"https://api.themoviedb.org/3/search/movie?api_key={key}&query={title}";
string imageLink = "https://www.themoviedb.org/t/p/w600_and_h900_bestv2";
var task = await httpClient.GetAsync(url).ConfigureAwait(false);
if (task.IsSuccessStatusCode)
{
var content = task.Content.ReadAsStringAsync();
var jsonString = content.Result;
var parsedObject = JObject.Parse(jsonString);
var linqList = parsedObject.SelectToken("results").ToArray();
foreach (var linq in linqList)
{
JObject obj = JObject.Parse(linq.ToString());
if (obj["title"].ToString() == title && obj["release_date"].ToString().Substring(0, 4) == year.ToString())
{
imageLink += obj["poster_path"].ToString();
return imageLink;
}
}
return "";
}
return "";
}
}
Error screenshot:error
You're unlucky as you've unfortunately hit an ongoing AWS outage, which is also affecting TMDB which most likely runs on AWS's infrastructure.
The Wappalyzer report lists PaaS as Amazon Web Services & CDN as Amazon CloudFront.
AWS powers a lot of the internet as a quick Google search for AWS outage will show.
Considering the same code was working previously, I'd check AWS's status page regularly for the latest updates until all issues are resolved. Even if they don't definitely use AWS, something that they use probably will.
P.S. task.IsSuccessStatusCode won't always be true even for the most stable of APIs.
Write an else statement to accompany your if statement and actually handle errors gracefully.

DateTime property value is not correctly received in Asp.Net Core WebApi Controller

I am using Asp.Net Core 2.2 WebApi and WinForm Client for sending requests. I am using RestSharp library as Restclient. It seems Property values with DateTime type is not correctly handled in WebApi Controller.
Source DateTime:
Target DateTime:
Client side code:
internal virtual void Add(T1 businessObject)
{
RestRequest request = new RestRequest(ManagementControllerName + #"/" + AddActionPrefix, Method.POST);
request.RequestFormat = DataFormat.Json;
request.AddJsonBody(businessObject);
var response = Globals.ServiceStackClient.Execute(request);
if (!response.IsSuccessful)
{
var ex = JsonConvert.DeserializeObject<AppExceptionMessage>(response.Content);
throw new ClientSideException(ex);
};
}
Business object:
public class DmPresentationBo : CatalogBo
{
public DmPresentationBo()
{
}
public string DocItemNumber { get; set; }
public DateTime DocItemDt { get; set; }
}
WebApi Controller:
[HttpPost]
[Route("Add")]
public ActionResult<T1> Add([FromBody] T1 businessObject)
{
try
{
CurrentDbStorage.StartTransaction();
var mainCmd = CurrentDbStorage.GenerateCrudInsertCommand(businessObject);
mainCmd.ExecuteNonQuery();
ProcessPostAdd(businessObject);
CurrentDbStorage.CommitTransaction();
}
catch (Exception ex)
{
CurrentDbStorage.RollbackTransaction();
var parsedEx = CurrentExceptionMgr.ParseErrorMsg(ex.Message);
return new BadRequestObjectResult(parsedEx);
};
return Ok(businessObject);
}
Setting Breakpoint within controller, it showed me that BusinessObject deserialized incorrectly, like DocItemDt property value is less than actual one which sent by Client. Surfing google, there are some problems with Json DateTime handling, however none of them helped me.
Thanks for your help.

HttpResponseMessage post API json format with C#

I used the HttpResponseMessage Post method to let the mobile terminal verify the account password. I used the following CODE to run successfully, but the POST format must be run like this.
'{"ID":"xxx","Password":"xxx"}'
It need two ' can run, I don't know why.
I can't request a service using the normal POST format on iOS or Android.
The format I want is {"ID":"xxx","Password":"xxx"},without '
[HttpPost]
public HttpResponseMessage Post([FromBody] string DATA)
{
using (appapidataEntities entities = new appapidataEntities())
{
//string controllerName = ControllerContext.RouteData.Values["controller"].ToString();
JObject jo = JObject.Parse(DATA);
string id = jo["ID"].ToString();
string password = jo["Password"].ToString();
var user = entities.USERs.Where(x => x.ID == id && x.Password == password).FirstOrDefault();
var result = new
{
message = "failure"
};
var result2 = new
{
message = "success"
};
if (user == null)
{
return Request.CreateResponse(HttpStatusCode.OK, result);
}
else
{
return Request.CreateResponse(HttpStatusCode.OK, result2);
}
}
}
public partial class USER
{
public string ID { get; set; }
public string Password { get; set; }
}
}
Please have someone with experience to help me, thank you very much.
As #Nkosi said, the correct way to receive a complex object is using a class/object (also called 'model binding')
In your models add this class. This class will be the 'contract' between the service with any external application who calls the service. Usually, any client (service, app or frontend) also has a class with this contract to call the service.
public class LoginViewModel {
public string ID { get; set; }
public string Password { get; set; }
}
Now, modify the controller as follow
[HttpPost]
public HttpResponseMessage Post([FromBody] LoginViewModel DATA) {
using (appapidataEntities entities = new appapidataEntities())
string id = DATA.ID;
string password = DATA.Password;
// rest of the code
}
}
Make sure the device is sending the data the service is waiting (maybe adding a breakpoint if you are debugging from Android Studio before to make the request) and add a breakpoint in your controller to verify that the variable DATA has the correct values.

Adobe Sign (echo sign) API sending document using C#

Okay I have limited understanding of working with API's
Im trying to get to grips with Adobe Sign API and hit a dead end, on there test page i have enterd this and it works
But i have no idea on how then do that in C#
I have tried the following, but know its missing the OAuth stuff and I'm just not sure what to try next.
by the way foo.GetAgreementCreationInfo() just gets the string that is in the screen shot, I just moved it out cus it was big and ugly
var foo = new Models();
var client = new RestClient("https://api.na1.echosign.com/api/rest/v5");
// client.Authenticator = new HttpBasicAuthenticator(username, password);
var request = new RestRequest("agreements/{AgreementCreationInfo}", Method.POST);
request.AddParameter("name", "value"); // adds to POST or URL querystring based on Method
request.AddUrlSegment("AgreementCreationInfo", foo.GetAgreementCreationInfo()); // replaces matching token in request.Resource
IRestResponse response = client.Execute(request);
var content = response.Content; // raw content as string
You are misinterpreting the API documentation. The Access-Token parameter needed in your API is clearly an HTTP header, while the AgreementCreationInfo is simply the request body in JSON format. There is no URI segment, so rewrite your code as follows:
var foo = new Models();
//populate foo
var client = new RestClient("https://api.na1.echosign.com/api/rest/v5");
var request = new RestRequest("agreements", Method.POST);
request.AddHeader("Access-Token", "access_token_here!");
// request.AddHeader("x-api-user", "userid:jondoe"); //if you want to add the second header
request.AddParameter("application/json", foo.GetAgreementCreationInfo(), ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
var content = response.Content;
Please also be aware that in RESTSharp you do not need to manually serialize your body into JSON at all. If you create a strongly typed object (or just an anonymous object could be enough) that has the same structure of your final JSON, RESTSharp will serialize it for you.
For a better approach I strongly suggest you to replace this line:
request.AddParameter("application/json", foo.GetAgreementCreationInfo(), ParameterType.RequestBody);
With those:
request.RequestFormat = DataFormat.Json;
request.AddBody(foo);
Assuming your foo object is of type Models and has the following structure along with its properties:
public class Models
{
public DocumentCreationInfo documentCreationInfo { get; set; }
}
public class DocumentCreationInfo
{
public List<FileInfo> fileInfos { get; set; }
public string name { get; set; }
public List<RecipientSetInfo> recipientSetInfos { get; set; }
public string signatureType { get; set; }
public string signatureFlow { get; set; }
}
public class FileInfo
{
public string transientDocumentId { get; set; }
}
public class RecipientSetInfo
{
public List<RecipientSetMemberInfo> recipientSetMemberInfos { get; set; }
public string recipientSetRole { get; set; }
}
public class RecipientSetMemberInfo
{
public string email { get; set; }
public string fax { get; set; }
}
Link to AdobeSign Repository:
ADOBE SIGN SDK C# SHARP API Ver. 6
Adobe Sign API integrators - this is kind of hidden away in AdobeSigns GIT repositories. The link to all the generated SWAGGER classes (models/methods) for C# and REST client integrated C# project in a GIT project you can compile and use right inside your project as a project reference or compiled DLL. This project has been updated to use version 6 of the API. This was a huge time saver for me. I have provided a quick example below on how to use it. I hope this helps others save time as well.
Note you might have to switch out BasePath in the configuration.cs so you can retrieve the initial Adobe URI "BaseURI" call if you get 404 error.
Change BasePath = "http://localhost/api/rest/v6";
To:
BasePath = "https://api.echosign.com/api/rest/v6";
//include namespaces:
using IO.Swagger.Api;
using IO.Swagger.model.agreements;
using IO.Swagger.model.baseUris;
using IO.Swagger.model.transientDocuments;
using System.IO;
Then this quick minimal demonstrates BaseUri, Upload PDF a.k.a. Transient Document, then Create Agreement (Example 1 Basic Signer Minimal Options)
string transientDocumentId = "";
string adobesignDocKey = "";
string baseURI = "";
var apiInstanceBase = new BaseUrisApi();
var authorization = "Bearer " + apiKey; //Example as Integration Key, see adobesign docs For OAuth.
try
{
//___________________GET BASEURI ADOBE SIGN_________________________
BaseUriInfo resultBase = apiInstanceBase.GetBaseUris(authorization);
baseURI = resultBase.ApiAccessPoint; //return base uri
//___________________UPLOAD YOUR PDF THEN REF ADOBE SIGN_________________________
var apiInstanceFileUpload = new TransientDocumentsApi(baseURI + "api/rest/v6/");
TransientDocumentResponse resultTransientID = apiInstanceFileUpload.CreateTransientDocument(authorization, File.OpenRead([ENTER YOUR LOCAL FILE PATH]), null, null, _filename, null);
if (!String.IsNullOrEmpty(resultTransientID.TransientDocumentId))
{
transientDocumentId = resultTransientID.TransientDocumentId; //returns the transient doc id to use below as reference
}
var apiInstance = new AgreementsApi(baseURI + "api/rest/v6/");
//___________________CREATE ADOBE SIGN_________________________
var agreementId = ""; // string | The agreement identifier, as returned by the agreement creation API or retrieved from the API to fetch agreements.
var agreementInfo = new AgreementCreationInfo();
//transientDocument, libraryDocument or a URL (note the full namespace/conflicts with System.IO
List<IO.Swagger.model.agreements.FileInfo> useFile = new List<IO.Swagger.model.agreements.FileInfo>();
useFile.Add(new IO.Swagger.model.agreements.FileInfo { TransientDocumentId = transientDocumentId });
agreementInfo.FileInfos = useFile;
//Add Email To Send To:
List<ParticipantSetMemberInfo> partSigners = new List<ParticipantSetMemberInfo>();
partSigners.Add( new ParticipantSetMemberInfo { Email = "[ENTER VALID EMAIL SIGNER]", SecurityOption=null });
//Add Signer To Participant
List<ParticipantSetInfo> partSetInfo = new List<ParticipantSetInfo>();
partSetInfo.Add(new ParticipantSetInfo { Name = "signer1", MemberInfos = partSigners, Role = ParticipantSetInfo.RoleEnum.SIGNER, Order=1, Label="" });
agreementInfo.ParticipantSetsInfo = partSetInfo;
agreementInfo.SignatureType = AgreementCreationInfo.SignatureTypeEnum.ESIGN;
agreementInfo.Name = "Example Esign For API";
agreementInfo.Message = "Some sample Message To Use Signing";
agreementInfo.State = AgreementCreationInfo.StateEnum.INPROCESS;
AgreementCreationResponse result = apiInstance.CreateAgreement(authorization, agreementInfo, null, null);
adobesignDocKey = result.Id; //returns the document Id to reference later to get status/info on GET
}
catch (Exception ex)
{
//Capture and write errors to debug or display to user
System.Diagnostics.Debug.Write(ex.Message.ToString());
}

Categories