I have a TwiML app with this code in the Connect action of the CallController. This code is taken straight from the Twilio demos.
[HttpPost]
public virtual ActionResult Connect(string phoneNumber, string called)
{
var response = new VoiceResponse();
var dial = new Dial(callerId: "+6138595????");
if (phoneNumber != null)
{
dial.Number(phoneNumber);
}
else
{
dial.Client("support_agent");
}
response.Dial(dial);
return TwiML(response);
}
When this is called it raises the error "Data at the root level is invalid. Line 1, position 1."
The XML this generates is
<?xml version="1.0" encoding="utf-8"?>
<Response>
<Dial callerId="+6138595????">
<Client>support_agent</Client>
</Dial>
</Response>
Twilio Evangelist here.
A quick question - is this happening every time the method is invoked, or only when specific inputs are provided? Needing to build the string manually is of course not desired. So I would like to get to the bottom of what triggered this result.
I have found I can fix it by replacing
return TwiML(response);
with
return new TwiMLResult(response.ToString(), new UTF8Encoding());
Appears to be some kind of encoding issue using the first method.
I had the same problem we solved it in our WebApi by skipping the Twilio sdk and generating the xml by ourselves.
I hope this will work for you too:
[HttpPost]
public virtual HttpResponseMessage Connect(string phoneNumber, string called)
{
string twiml = $"<?xml version=\"1.0\" encoding=\"utf-8\"?><Response><Dial callerId=\"{phoneNumber}\"><Client>support_agent</Client></Dial></Response>";
var xmlResponse = new HttpResponseMessage();
xmlResponse.Content = new StringContent(twiml, Encoding.UTF8, "text/xml");
return xmlResponse;
}
Please notice that there are no end of lines - "\n", \r", etc.
Related
I have built a Web API that is connected to a database for persons. I am now trying to call this Web API from a separate MVC-application which is supposed to have full CRUD. So far i have managed to do so with the Get and Post-methods to create a new person and see a list of the persons currently in the database.
When trying to do a similar call for the Put-method, i get the following error:
This is how my method UpdatePerson is written in my API-application:
[HttpPut]
[Route("{id:guid}")]
public async Task<IActionResult> UpdatePerson([FromRoute] Guid id, UpdatePersonRequest updatePersonRequest)
{
var person = await dbContext.Persons.FindAsync(id);
if (person != null)
{
person.Name = updatePersonRequest.Name;
person.Email = updatePersonRequest.Email;
person.Phone = updatePersonRequest.Phone;
person.Address = updatePersonRequest.Address;
await dbContext.SaveChangesAsync();
return Ok(person);
}
And this is how i am trying to consume the API in my separate MVC-project:
[HttpGet]
public IActionResult Edit()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Edit(PersonViewModel pvm)
{
HttpClient client = new();
StringContent sContent = new StringContent(JsonConvert.SerializeObject(pvm), Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PutAsync("https://localhost:7281/api/Persons/", sContent);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
return RedirectToAction("Get");
}
else
{
return NotFound();
}
}
Everything is working fine when i try to update the database through the API-app so i am not really sure what is wrong with my request. I hope that someone here can spot the issue right away or at least help me out as i am quite a beginner with WEB APIs.
I have mostly tried changing the URL in my MVC-project but the issue remains.
Are you sure you are receiving the request? It seems that your URI is
"https://localhost:7281/api/Persons/"
and your API is expecting
"https://localhost:7281/api/Persons/{id}" -> where {id} should be the guid
you need to append the guid in the URI
Looks like the request doesn't receive the correct the correct parameters, because the URI that appears in your picture seems a generic method.
I am creating a webhook in a .Net Core 3 Web API for DocuSign Connect to invoke and provide me status updates + signed documents from envelopes my app has created. The C# example at https://www.docusign.com/blog/dsdev-adding-webhooks-application was very helpful in getting me almost to my goal. The code from the example is:
[HttpPost("api/[controller]/ConnectWebHook")]
public void ConnectWebHook(HttpRequestMessage request)
{
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(request.Content.ReadAsStreamAsync().Result);
var mgr = new XmlNamespaceManager(xmldoc.NameTable);
mgr.AddNamespace("a", "http://www.docusign.net/API/3.0");
XmlNode envelopeStatus = xmldoc.SelectSingleNode("//a:EnvelopeStatus", mgr);
XmlNode envelopeId = envelopeStatus.SelectSingleNode("//a:EnvelopeID", mgr);
XmlNode status = envelopeStatus.SelectSingleNode("./a:Status", mgr);
var targetFileDirectory = #"\\my-network-share\";
if (envelopeId != null)
{
System.IO.File.WriteAllText($"{targetFileDirectory}{envelopeId.InnerText}_{status.InnerText}_.xml", xmldoc.OuterXml);
}
if (status.InnerText == "Completed")
{
// Loop through the DocumentPDFs element, storing each document.
XmlNode docs = xmldoc.SelectSingleNode("//a:DocumentPDFs", mgr);
foreach (XmlNode doc in docs.ChildNodes)
{
string documentName = doc.ChildNodes[0].InnerText; // pdf.SelectSingleNode("//a:Name", mgr).InnerText;
string documentId = doc.ChildNodes[2].InnerText; // pdf.SelectSingleNode("//a:DocumentID", mgr).InnerText;
string byteStr = doc.ChildNodes[1].InnerText; // pdf.SelectSingleNode("//a:PDFBytes", mgr).InnerText;
System.IO.File.WriteAllText($"{targetFileDirectory}{envelopeId.InnerText}_{documentId}_{documentName}", byteStr);
}
}
}
For testing purposes, my Web API is allowing all origins and exposed to the outside world via NGROK, and I can hit other test endpoints (both GET and POST), but for some reason this webhook is not being hit by Connect when there is a notification-worthy event on my envelope.
I can see in the DocuSign Admin portal logs that Connect invoked my webhook but got The remote server returned an error: (415) Unsupported Media Type.. This led me to add the [FromBody] attribute to my method signature like so but I still get the same error when my webhook is invoked by Connect.
[HttpPost("api/[controller]/ConnectWebHook")]
public void ConnectWebHook([FromBody] HttpRequestMessage request)
{
// ... rest of the method was unchanged, removed for brevity
}
I have never used HttpRequestMessage before but it looks straightforward enough. I noticed in the DocuSign Admin portal logs that the data that Connect tried to send to the webhook is just XML. I could try to change the webhook's signature to look for an XmlDocument instead of an HttpRequestMessage but I am not sure what, if anything, I will be missing out on.
Has anyone else integrated with Connect via a webhook recently? And were you able to make the HttpRequestMessage work for you?
Added on 10/18/2019:
DocuSign mentions that the content type is XML. Here is what the content looks like:
<DocuSignEnvelopeInformation
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.docusign.net/API/3.0">
<EnvelopeStatus>...</EnvelopeStatus>
<DocumentPDFs>...</DocumentPDFs>
</DocuSignEnvelopeInformation>
I have added AddXmlSerializerFormatters() to the ConfigureServices method in Startup.cs. This being .Net Core 3, I had to set it up like services.AddControllers().AddXmlSerializerFormatters() instead of services.AddMVC().AddXmlSerializerFormatters() per https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio.
With that change, I have now tried using [FromForm] like so and my webhook IS being hit, but the request input parameter is essentially empty ... request.Content = null:
[HttpPost("api/[controller]/ConnectWebHook")]
public void ConnectWebHook([FromForm] HttpRequestMessage request)
{
// ... rest of the method was unchanged, removed for brevity
}
Since the request is being sent from DocuSign Connect, I have no control over the headers/format/content. As far as I can tell, they are not submitting an XML object, not a form, so [FromForm] is probably not the way to go.
That linked example is not for .net core. HttpRequestMessage is no longer a first class citizen in asp.net-core framework and will treated as a normal model.
Just extract the content directly from the Request's body and the rest should be able to remain the same as in the example.
[HttpPost("api/[controller]/ConnectWebHook")]
public IActionResult ConnectWebHook() {
Stream stream = Request.Body;
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(stream);
var mgr = new XmlNamespaceManager(xmldoc.NameTable);
mgr.AddNamespace("a", "http://www.docusign.net/API/3.0");
XmlNode envelopeStatus = xmldoc.SelectSingleNode("//a:EnvelopeStatus", mgr);
XmlNode envelopeId = envelopeStatus.SelectSingleNode("//a:EnvelopeID", mgr);
XmlNode status = envelopeStatus.SelectSingleNode("./a:Status", mgr);
var targetFileDirectory = #"\\my-network-share\";
if (envelopeId != null) {
System.IO.File.WriteAllText($"{targetFileDirectory}{envelopeId.InnerText}_{status.InnerText}_.xml", xmldoc.OuterXml);
}
if (status.InnerText == "Completed") {
// Loop through the DocumentPDFs element, storing each document.
XmlNode docs = xmldoc.SelectSingleNode("//a:DocumentPDFs", mgr);
foreach (XmlNode doc in docs.ChildNodes) {
string documentName = doc.ChildNodes[0].InnerText; // pdf.SelectSingleNode("//a:Name", mgr).InnerText;
string documentId = doc.ChildNodes[2].InnerText; // pdf.SelectSingleNode("//a:DocumentID", mgr).InnerText;
string byteStr = doc.ChildNodes[1].InnerText; // pdf.SelectSingleNode("//a:PDFBytes", mgr).InnerText;
System.IO.File.WriteAllText($"{targetFileDirectory}{envelopeId.InnerText}_{documentId}_{documentName}", byteStr);
}
}
return Ok();
}
I got a problem that my asp.net mvc solution isn't reading my json, and I need some help with this.
First of all, let me introduce you guys to the problem.
I need just to put a GET METHOD, read some information and that's all.
But, the problem is, when i am using a simple j-son it works. As you guys can see above:
.
https://i.imgur.com/d3DyCiB.jpg "The Postman"
.
https://i.imgur.com/e5b6DYA.jpg "The View showing the data"
.
Here what I'm using on my controller/mycodes
https://i.imgur.com/LEYDDUF.jpg "The Code"
.
https://i.imgur.com/wRjYPet.jpg "The Code2"
.
And here what i really need to receive, but comes null:
.
https://i.imgur.com/mbj5lB8.jpg "The J-Son"
.
https://i.imgur.com/vitUQvr.jpg "Returns null"
-- FindAll Clients:
public class ProductClient
{
private string BASE_URL = "http://localhost:50166/api/Clientes";
public IEnumerable<Cliente> findAll()
{
try
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(BASE_URL);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync(BASE_URL).Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<IEnumerable<Cliente>>().Result;
}
return null;
}
catch
{
return null;
}
}
}
My Index
public ActionResult Index()
{
ProductClient pc = new ProductClient();
ViewBag.Clientes = pc.findAll();
return View();
}
You have to specify [FromUri] in the method's parameter. Web API expecting it to come from the Request body by default. You can add the [FromUri] like this:
public IEnumerable<string> findAll([FromUri]List<string> stringColumnNames)
{
return Ok(pc.findAll(stringColumnNames));
}
add call like this
http://localhost:59511/api/Values?str[]="abc"&str[]="xyz"
I have seen a few questions posted with this same problem. I think I have all the pieces, but I am still getting the "Empty Payload" error. Does anyone see what is missing?
I want to update the category of some mail messages. I am using the beta endpoint because of this post: Microsoft Graph Client SDK vs. Requesting JSONs
Here is my code:
public async void UpdateMailCategory(GraphServiceClient graphClient, string messageId, string inbox)
{
string newCategory = #"{'categories': ['Processed']}";
try
{
// Get the request URL for adding a page.
string requestUrl = graphClient
.Users[inbox]
.Messages[messageId]
.Request()
.RequestUrl;
HttpRequestMessage hrm =
new HttpRequestMessage(new HttpMethod("PATCH"), requestUrl);
hrm.Content =
new StringContent(newCategory, Encoding.UTF8, "application/json");
// Authenticate (add access token) our HttpRequestMessage
await graphClient.AuthenticationProvider
.AuthenticateRequestAsync(hrm);
// Send the request and get the response.
HttpResponseMessage response =
await graphClient.HttpProvider.SendAsync(hrm);
}
catch (ServiceException Servex)
{
throw Servex;
}
}
When I look at the hrm.content, it shows this:
{ System.Net.Http.StringContent }
Headers:
{
Content - Type : application / json;charset = utf - 8
Content - Length : 35
}
You're using the Graph Client SDK is a rather roundabout way which is more likely going to cause you headaches than anything else. It also leads to more complicated code than necessary.
The SDK includes everything you need for the entire request. With the exception of an edge case, you should never need to deal with the HttpProvider or manage HttpRequestMessage and HttpResponseMessage instances.
The following will accomplish the same thing (setting the Categories property of a message) with a lot less complexity:
public async void UpdateMailCategory(GraphServiceClient graphClient, string messageId, string inbox)
{
try
{
await graphClient
.Users[inbox]
.Messages[messageId]
.Request()
.UpdateAsync(new Message()
{
Categories = new List<string>() { "Processed" }
});
}
catch (ServiceException Servex)
{
throw Servex;
}
}
You also shouldn't use the /beta version for this operation as it is supported by the /v1.0 version. You should only need to leverage the /beta version to manage the user's Categories since this isn't generally available yet.
I am developing (at least attempting anyways...) a xamarin android app with connection to API. Below is a basic example of what I am trying to do with a Post. I am not sure how to return the error message (not just code) back to client? I am open to a better way of doing this as long as a complete example can be provided.
API code:
[HttpPost("Consume")]//*
public IActionResult ConsumeInventory([FromBody] Part part)
{
try
{
_repo.ConsumeInventory(part);
}
catch (Exception ex)
{
//ModelState.AddModelError("Error", ex.Message);
return NotFound("Not Enough Inventory");
}
return Ok(part);
}
Client side call:
public async Task<HttpResponseMessage> UpdatePartAsync(Part part, string post_url)
{
string jsonString = JsonConvert.SerializeObject(part);
client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsync("invcount/" + post_url, new StringContent(jsonString, Encoding.UTF8, "application/json"));
if (response.StatusCode == HttpStatusCode.NotFound)
{...//Would like to display "Not enough Inventory" in here}
return response;
}
Even when the API call is unsuccessful, you can read the content of the response by using HttpResponseMessage.Content.ReadAsStringAsync()
if (response.StatusCode == HttpStatusCode.NotFound)
{
// Would like to display "Not enough Inventory" in here}
var resposeContent = response.Content?.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<ServerResponse>(resposeContent);
// this guy will have your human-readable description or in your case, you can display the whole `result` string
Debug.WriteLine(result.Description);
}
And define you ServerResponse class as you designed your API. I regularly inherit all my API responses from the same base class so on the client side I can parse them first to check for custom directives from server (custom codes which are not always errors) message:
public class ServerResponse
{
public string Code {get;set;}
public string Description {get;set;}
}
ps: beware, HttpResponseMessage.Content could be null in certain cases.
Your question has nothing to do with Xamarin. In this case Xamarin app is just a front-end that consumes your API.
Generally speaking, you should generalise your error responses and if needed log the exception to DB for a further analysis. JSON example:
{
"Code": 1000,
"Description": "Human readable description",
"Id": "6a154fd3-4142-4a9c-80b5-c635e84123fa"
}
Code for internal error code in the system and Id for identifying the stack trace in the DB. So your end-user will get a nice human readable description and an Id that he could use to contact the support.
Technically, use a ExceptionFilterAttribute in order to catch and return a generalised error response to your clients. Reading Global Error Handling in ASP.NET Web API 2 may also give you a better overview.
In your client app after you get a response from the API you can check if the response has a success status code:
if (!response.IsSuccessStatusCode)
{
// Deserialize error message
}
P.S.: Stackoverflow is not a coding service, so there will be no a complete example. Hope you will get some ideas from my answer on how to handle it in a better way.
Good luck!