RESTful API return conventions - c#

I'm still learning c# web API at the moment, and I've faced some problems.
so the code snippet below shows a portion of my codes that will create a new student in the database, what I am trying to do is to create the object and if it succeeded, it will return a HTTP-CREATED http response code and return the STUDENT OBJECT.
if it fails, it should return a HTTP-BADREQUEST response code and ALSO return the STUDENT OBJECT.
HOWEVER, in order to return the response code, I am unable to return a student object and vice-versa due to the return type set, hence, the dilemma.
// POST api/student
public HttpResponseMessage PostStudent(Models.Student student)
{
if (DBManager.createStudent(student) != null)
return new HttpResponseMessage(HttpStatusCode.Created);
// HOW TO RETURN STUDENT OBJECT?
else
return new HttpResponseMessage(HttpStatusCode.BadRequest);
// HOW TO RETURN STUDENT OBJECT?
}

The HttpRequestMessageExtensions.CreateResponse<T> Method has an optional formal paramater called value that can be used to create an HttpResponseMessage that contains both a status code and an object.
return Request.CreateResponse(HttpStatusCode.Created, student);

I'll tell you my own experience of building a web server for my company: DON'T use WebApi. There are so many limitations and in short, supports a very narrow usage scenario. Just stick to the traditional MVC controllers and life is much easier:
public ActionResult PostStudent(Models.Student s){
//do something
Response.StatusCode = 400;
return Json(s);
}

Related

C# Type object as payload to Post Method

Can we upload a C# Type object to a Post Method ?
[HttpPost]
[Route("SendModelPayload")]
public async Task<IActionResult> SendModelPayload([FromBody] Type type)
{
// Do some stuff
return Ok("test");
}
But, Here type is null.
client code:
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(#"http://localhost:5000");
var res = httpClient.PostAsJsonAsync(#"/api/ModelPayload/SendModelPayload",
typeof(Student)).Result;
Any ideas, Thanks in advance !!
When transmitting data to a REST API, you need to keep in mind that client and server are basically unrelated in terms of technology and therefore use a common ground that a lot of programming languages support. In your case, there might be a .NET client and a .NET server, but it could well be that a Javascript client calls your API that does not know of the Type class. Even if you have .NET client and a .NET server, the client could send a type that the server is unaware of.
So you need to find a common ground that is supported by various programming environments and is flexible enough to also cope with the situation that the client sends data that the server cannot map to a type.
One way would be to not transmit a Type directly, but transmit the type name as a string. The server could then try to load the type directly. If the type can be found, it could work with the type, if not, it would return a BadRequestResult to notify the client of the invalid data.
[HttpPost]
[Route("SendModelPayload")]
public async Task<IActionResult> SendModelPayload([FromBody] string typeName)
{
// Try to load type
var type = Type.GetType(typeName);
if (type == null)
return BadRequest();
// Do some stuff
return Ok("test");
}
The client would send a type name instead of a real type e.g.
var res = httpClient.PostAsJsonAsync(#"/api/ModelPayload/SendModelPayload",
typeof(Student).AssemblyQualifiedName).Result;
By transmitting the AssemblyQualifiedName, the type name is very specific, so the server needs to be able to load the exact same type, but fail if it cannot laod the type.
If I understand your question, you want to upload a Student instance to the SendModelPayload endpoint?
If that is correct, then a couple of changes are necessary to get what you have working:
POST Method
// changed to accept Student object in body
[HttpPost]
[Route("SendModelPayload")]
public async Task<IActionResult> SendModelPayload([FromBody] Student type)
{
// Do some stuff
return Ok("test");
}
Client Code
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri(#"http://localhost:5000");
// create instance and populate it with data
var studentInstance = new Student() { Name = "Student Name" };
// then call the post method and pass the instance of the Student class
// that will get serialized and sent as the body
var res = httpClient.PostAsJsonAsync(#"/api/ModelPayload/SendModelPayload",
studentInstance).Result;
Instead of Expecting TYPE from body. Convert string input to Type
[HttpPost]
[Route("SendModelPayload")]
public async Task<IActionResult> SendModelPayload([FromBody] string TypeName)
{
var ty = Type.GetType(TypeName);
if(ty != null)
Suggesting as I am not exactly aware with requirement

Trying to setup web hook for stripe using ASP.NET MVC and C#

I have implemented some front end code which when a user clicks the checkout button they are redirected to a stripe page where they can input their card payment details. the code has a successful URL and failed URL. if the customer enter valid payment details - they are redirected to the successful URL, i need to update my database to ensure that my backend knows that this specific user has paid and can now view subscribed content. I am trying to setup web hooks in order to do this, so I know if the user has paid, cancelled etc.
using System;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using Stripe;
namespace workspace.Controllers
{
[Route("api/[controller]")]
public class StripeWebHook : Controller
{
// You can find your endpoint's secret in your webhook settings
const string secret = "whsec_...";
[HttpPost]
public async Task<IActionResult> Index()
{
var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
try
{
var stripeEvent = EventUtility.ConstructEvent(json,
Request.Headers["Stripe-Signature"], secret);
// Handle the checkout.session.completed event
if (stripeEvent.Type == Events.CheckoutSessionCompleted)
{
var session = stripeEvent.Data.Object as Checkout.Session;
// Fulfill the purchase...
HandleCheckoutSession(session);
}
else
{
return Ok()
}
}
catch (StripeException e)
{
return BadRequest();
}
}
}
}
However when trying to implement this I get errors because I think the custom code provided above uses .NET Core and I am using the full .NET framework.
Is there a way around this or what am I doing wrong?
This may help someone so I'm posting even although it's a bit late to the table as I couldn't find a relevant answer anywhere.
I had this same issue on a dotNet Core MVC web application (so not an exact answer for the question which is .Net Framework) where the Stripe Webhook was constantly giving a 400 Bad Request response. I just couldn't hit it no matter what I tried.
Eventually, and probably obviously the solution for me was to add the [IgnoreAntiforgeryToken] attribute to the Index() method as you have in your question above. As .dotNet Core enables the Validation Token on forms I had to explicitly ignore it. The Webhooks worked as soon as I did that.
So the solution for me was:
[HttpPost]
[IgnoreAntiforgeryToken]
public async Task<IActionResult> Index()
This apparently applies to dot Net Core versions: see Microsofts Documentation
Hope this helps someone.
That's works in my Asp.net Framework 4.7, try below code for the webhook
[HttpPost]
[Route("api/[controller]/webhook")]
public async Task<HttpResponseMessage> ProcessRequest()
{
var json = await new StreamReader(HttpContext.Current.Request.InputStream).ReadToEndAsync();
try
{
var stripeEvent = EventUtility.ParseEvent(json);
// Handle the event
if (stripeEvent.Type == Events.PaymentIntentSucceeded)
{
var paymentIntent = stripeEvent.Data.Object as PaymentIntent;
// Then define and call a method to handle the successful payment intent.
// handlePaymentIntentSucceeded(paymentIntent);
}
else if (stripeEvent.Type == Events.PaymentMethodAttached)
{
var paymentMethod = stripeEvent.Data.Object as PaymentMethod;
// Then define and call a method to handle the successful attachment of a PaymentMethod.
// handlePaymentMethodAttached(paymentMethod);
}
// ... handle other event types
else
{
// Unexpected event type
Console.WriteLine("Unhandled event type: {0}", stripeEvent.Type);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (StripeException e)
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
//Modification and Saving Data
}
After adding this webhook , you can test on locally from https://stripe.com/docs/webhooks/test this link

How do I use the response from a postasync call?

I have a webApi set up that has a PostStudent method. The API call works fine and creates a new student in the DB, but I can't get it to return the value in the response body. I have tried returning Ok(newStudent) and Created("~/api/poststudent", newStudent) but neither of them have returned the newStudent value that I need.
I have gone through all of the response and can't find the actual newStudent object. Is it there and I am just missing it or do I have a problem with my code?
This is the PostStudent method from the API;
var newStudent = new Student
{
studentId = nextStudentId,
studentFirstName = studentEntry.StudentFirstName,
studentLastName = studentEntry.StudentLastName,
studentDOB = studentEntry.StudentDob,
studentEmergencyContactName = studentEntry.StudentEmergencyContactName,
studentEmergencyContactNum = studentEntry.StudentEmergencyContactNum,
ticketNumber = studentEntry.TicketNumber
};
db.Student.Add(newStudent);
try
{
db.SaveChanges();
}
catch (DbUpdateException)
{
if (StudentExists(newStudent.studentId))
return BadRequest("That student id already exists");
throw;
}
return Ok(newStudent.studentId);
// return Created("~/api/poststudent", newStudent);
}
This is where I call postasync and try to save the response body;
var response = client.PostAsync("api/poststudent", content);
return response.Result.Content.ReadAsStringAsync().ToString();
And this is where I want to use the value;
var newStudentId = controller.PostStudent(studentFirstName, studentLastName, studentDob, ticketNumber);
var url = "~/AddGuardian/AddGuardian/" + newStudentId;
Response.Redirect(url);
I hope someone can help me. I never thought redirecting to another page would be so damn hard!
Cheers.
You're not awaiting the async calls:
var response = await client.PostAsync("api/poststudent", content);
return (await response.Result.Content.ReadAsStringAsync()).ToString();
There are 2 official tutorials for quickstarting a ASP.NET WebApi project.
Using Web API 2 with Entity Framework 6
Calling a Web API From a .NET Client (C#)
I want to recommend to work through these. If you find your application is doing things wrong, fix your application towards these samples. If your requirements differ from what is given in the samples, you can emphasize on this in your question.

Web API and C# out Values

I have a service that contains a lot of functions with out parameters to return a few extra parameters.
I was wondering if it's possible to call a regular asp.NET web api service with out parameters and receive a value(in the form of out parameters, separate from the return value) from the service.
If it is possible, could you elaborate on what I need to do to achieve this?
Any help will be well appreciated.
No, this is not possible. The response from WebAPI will be a normal HTTP response with a body where the serialized returned data will be.
Of course, as usual, your response can be a complex object to serialize and you can include those out returns as members of it. For example:
public IHttpActionResult GetResponse(int id)
{
int outputInt;
string outputString;
YourMethodWithOutParameters(id, out outputInt, out outputString);
return Ok(new
{
Id = id,
OutputInt = outputInt,
OutputString = outputString,
});
}

MVC5 app doesn't hit my WebAPI2 POST method for creating new records

I have an MVC5 application, ASP.NET, that, when creating a new record and clicking submit, it calls my WebAPI (version 2 - the new one) to insert the record into the database. Problem is, it's not hitting the POST method in my WebAPI. Anyways, here's my MVC5, front end application code for "Create":
[HttpPost]
public ActionResult Create(BulletinBoard bulletinBoard)
{
bulletinBoard.CreatedDate = DateTime.Now;
bulletinBoard.CreatedBy = HttpContext.User.Identity.Name;
response = client.PostAsJsonAsync("api/bulletinboard", bulletinBoard).Result;
if (response.IsSuccessStatusCode)
{
return View("Index");
}
else
{
LoggerHelper.GetLogger().InsertError(new Exception(string.Format(
"Cannot create a new feedback record due to HTTP Response Status Code not being successful: {0}", response.StatusCode)));
return View("Problem");
}
}
I already defined "client" in my constructor and gave it the base URL for my WebAPI - keep in mind that GET works - so it's not a problem with my URL. I can also manually go to my WebAPI URL and get data back in my browser.
Here's my WebAPI code:
// POST api/bulletinboard
public HttpResponseMessage PostBulletinBoard(BulletinBoard bulletinBoard)
{
if (ModelState.IsValid)
{
db.BulletinBoards.Add(bulletinBoard);
db.SaveChanges();
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, bulletinBoard);
return response;
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
This worked when I was using WebAPI version 1 which had a different naming convention for the GET and POST and PUT methods.
So, when the URL for the POST request is called (the line that's response = client.PostAsJsonAsync...), the request never hits my POST method in my WebAPI and consequently, no records are inserted into my database. What am I doing wrong?
According to the comments it appears that you have POSTed invalid data (according to the validation rules you defined in your BulletinBoard model) and this validation simply fails. So to fix the issue make sure you are sending valid data.
I think there might be a few reasons why it doesn't hit your post method. Here is my example of Post method. The things you should note is method name and FromBody attribute
public async Task<HttpResponseMessage> Post([FromBody]FoodProduct foodProduct)
{
UnitOfWork.FoodRepository.Edit(foodProduct);
await UnitOfWork.SaveAsync();
return Request.CreateResponse(HttpStatusCode.OK);
}
I also like to use this new RoutePrefix Attribute on my controller, it works perfectly and looks good.
[RoutePrefix("api/Food")]
public class FoodController : BaseApiController
{
///some code here
}

Categories

Resources