I am currently writing a NET 6 web API. I have to implement a method which saves a list of items. I wrote the following POST-Method to do that:
[HttpPost]
public IActionResult PostCustomer(List<Customer> customers)
{
foreach (var customer in customers)
{
SaveCustomer(customer);
}
return Ok();
}
The SaveCustomer() method makes a lot of validation and could throw an error. So it is possible, that a customer cannot be saved. If I am adding a try-catch around SaveCustomer(), all other customers are saved. But the response is not telling me, that one customer couldn't be saved because of an error. How can I create a correct response, like a warning?
Something like this: Warning: Customer x is not saved
you can return a list of failed customers in the response and add the failed customer object details to this (like id or name).
List<string> failedCustomers { get; set; }
foreach (var customer in customers)
{
try
{
SaveCustomer(customer);
}
catch (Exception ex)
{
failedCustomers.Add(customer.Id);
}
}
if (failedCustomers.Any())
{
return StatusCode(StatusCodes.Status207MultiStatus, failedCustomers);
}
else
{
return Ok();
}
Pay attention to the response code Status207MultiStatus, refer to this question.
Related
I am currently developing an API and need to return some objects for the api or an error in case of failure somewhere, mainly because I am dependent on database calls.
Here is some of my code:
public Student GetStudent(string parametr)
{
try
{
// Database call 1
// Database call 2
return new Student();
}
catch (Exception ex)
{
// return new ErrorDetails(ex.message); -- example
return null;
}
}
One of my constraints is that I need to put this API in swagger. I tried with HttpResponse which fits perfectly my needs regarding the coding part, but that does not work with swagger. My web application is not asp.net core.
Any ideas or suggestions on what should I do?
Thanks in advance,
You can use Swagger DataAnnotations and encapusulate the return data to achieve that
First of all create a class to encapsulate the error messages like that
public class Errors
{
public List<string> ErrorMessages { get; set; } = new List<string>();
}
Then use the annotaions like that
For .NET 4.5+ (Fullframework)
[SwaggerResponse(HttpStatusCode.OK, Type = typeof(Student))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(Errors))];
public IHttpActionResult GetStudent(string parametr)
{
try
{
// Database call 1
// Database call 2
return Ok(new Student());
}
catch (Exception ex)
{
Errors errors = new Errors();
errors.ErrorMessages.Add(ex.Message);
return Content(HttpStatusCode.BadRequest, errors);
}
}
For .NET Core
[ProducesResponseType(200, Type = typeof(Student))]
[ProducesResponseType(400, Type = typeof(Errors))]
public IActionResult GetStudent(string parametr)
{
try
{
// Database call 1
// Database call 2
return Ok(new Student());
}
catch (Exception ex)
{
Errors errors = new Errors();
errors.ErrorMessages.Add(ex.Message);
return BadRequest(errors);
}
}
Note that the BadRequest is just an example of return, you should always return the correct Http Status Code message, like 404 to not found, 401 to forbidden and so on
I'm trying to count Order from Database with API but it doesn't seem to work.
There's a message from Swagger that said "Failed to load API definition. Fetch error undefined swagger/v1/swagger.json"
This is in OrderController:
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(int))]
public async Task<ActionResult<int>> CountOrders()
{
int result = await _orderService.CountOrders();
return Ok(result);
}
This is in OrderService:
public async Task<int> CountOrders()
{
return _orderRepository.CountOrder();
}
This is in OrderRepository:
public int CountOrder()
{
IQueryable<DataProvider.Entities.Order> orderList;
orderList = GetAll();
return orderList.Count();
}
I'm new to this so please point out if there's anything makes you feel uncomfortable. Thanks.
I solved it. I was kind of stupid and didn't realise I haven't change the route, so it got duplicated.
It works after adding [Route("count")]
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I am using Angular on client-side. I am using .NET CORE WEB API on server-side and i'm using SQL Server for database. My WEB API has layers. Business Logic Layer does database dml operations for me and i was able to do get and post http client operations. But i have to check data in Business Logic Layer if data is wrong formatted i want to show an error on client like "This phone number is wrong formatted. Please enter correct format". I did DML (INSERT,SELECT) operations correctly. I need help for returning error message and i want to show this error message to users.
This is .NET Core Web API PersonController.cs
[Route("api/[controller]")]
[ApiController]
[EnableCors(origins: "http://example.com", headers: "*", methods: "*")]
public class PersonController : ControllerBase
{
FihristBLL bll = new FihristBLL();
// GET: api/Person
[HttpGet]
public List<Person> Get()
{
return bll.GetNumbers();
}
// POST: api/Person
[HttpPost]
public void Post([FromBody] Person person)
{
bll.AddNumber(person);
}
}
}
This is Business Logic Layer doing database operations
public class FihristBLL
{
public void AddNumber(Person person)
{
if (person.PhoneNumber.Substring(0, 1) != "5" || 0 % Convert.ToInt64(person.PhoneNumber) != 0)
{
// WRONG FORMAT
}
else
{
// CORRECT FORMAT
FihristDAL db = new FihristDAL();
SqlConnection conn = db.OpenConnection();
string command = "INSERT INTO Persons (PhoneNumber,FirstName,LastName) VALUES (#phoneNumber,#firstName,#lastName)";
SqlCommand sqlCommand = db.CreateConnection(command);
sqlCommand.Parameters.AddWithValue("#phoneNumber", person.PhoneNumber);
sqlCommand.Parameters.AddWithValue("#firstName", person.FirstName);
sqlCommand.Parameters.AddWithValue("#lastName", person.LastName);
sqlCommand.ExecuteNonQuery();
conn.Close();
}
}
}
}
Operations perfectly work fine. But i need to return error messages like "You just entered wrong formatted phone number." and display this message on Angular.
The quick response would be to throw an ArgumentException with the description of the validation error, catch it at controller level and map it into an error response with the exception´s message as content:
var validPhoneNumber = //your validation logic stuff
if (!validPhoneNumber ){
throw new ArgumentException("Invalid phone number");
}
[HttpPost]
public IActionResult Post([FromBody] Person person)
{
try{
bll.AddNumber(person);
}catch(ArgumentException argumentEx){
return BadRequest(new {Message = argumentEx.Message});
}
// A better approach would be to return the created resource at this point
return this.NoContent();
}
On the angular side, the error payload should be available in an instance of HttpResponseError.
If you want to implement a cleaner/better approach, I would recommend you to start by reading the msc docs about the topic. You might then opt in to use a validation library other than the built-in one, eg. FluentValidation. After that, you might as well go with more complex stuff as described in this blog entry
I would try to encapsulate validation logic in an object, serialize that object and send it to angular, then in angular check if everything is ok move onward, otherwise show some error messages.
You can use the model and return it.
public class Response<T>
{
public Response(bool defalut = false)
{
IsSuccess = defalut;
Message = "";
}
public Response(string message)
{
IsSuccess = false;
Message = message;
}
public bool IsSuccess { get; set; }
public string Message { get; set; }
public T Data { get; set; }
}
When the format is wrong you can throw an exception and simply return a HttpResponseMessage like so:
[HttpPost]
public void Post([FromBody] Person person)
{
try
{
bll.AddNumber(person);
}
// Replace with own exception
catch (FormatException e)
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
ReasonPhrase = "Phone number is not correctly formatted"
};
}
return new HttpResponseMessage(HttpStatusCode.OK);
}
I need to make an API Post method where I insert data into the database.
The database has 3 fields: One field has default values given by the database, one I have to insert with a new Guid.NewGuid() method, and one is inserted by the user.
How can I do this? Never seen anything like this in a tutorial.
If you can give me an example I will appreciate.
I'm new in web APIs and have seen a bunch of tutorials since Wednesday and can't reach a solution.
EDIT:
Here is my code:
public HttpResponseMessage Post(Company value)
{
try
{
if(ModelState.IsValid)
{
bd.Companies.Add(value);
bd.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, "Invalid Model");
}
}
catch (Exception ex )
{
return Request.CreateResponse(HttpStatusCode.InternalServerError, ex.Message);
}
}
How can i put a Guid.NewGuid()in this code to give a value to one of my fields?
EDIT2: My class to receive the values from Post
public class CompanyPostViewModel : Company
{
public static Guid Guid.NewGuid(); -->how can i do this?
public string Name { get; set; }
public DateTime? CreatedDate { get; set; }
}
If you are looking for an example, that fully goes from the front-end, to using WEB API [Post], to writing to the database, please see the following site. It should provide you enough context to complete what you are trying to accomplish. If this is insufficient, please post your current code, and where you are having any issues.
I am probably using the wrong term here (would love the proper name if someone knows it) but I regularly run into the issue of wanting to save something to the database + something else. In my current scenario I have the following code.
public class Stock
{
public Guid StockID { get; set; }
public string Name { get; set; }
public byte[] Image { get; set; }
}
IEnumerable<Stock> stock = new StockService().Get();
using (Database db = DBFactory.Create())
{
try
{
db.BeginTransaction();
db.InsertAll(stock);
foreach (Stock item in stock)
IsolatedStorage.SaveFile(item.Name, item.Image);
db.Commit();
}
catch
{
db.Rollback();
throw;
}
}
As you can hopefully tell what I'm doing is saving something to a database (in my case Sqlite) and to the IsolatedStorage on a Windows Phone device.
Now if the code as shown above fails it obviously leaves the IsolatedStorage in an inconsistant state. I can modify this code and delete any images from the IsolatedStorage in the catch block as such:
catch (Exception ex)
{
db.Rollback();
foreach (Stock item in stock)
IsolatedStorage.Delete(item.Name);
throw;
}
but I have run into this problem so many times and I can't help but feel there must be a better way. So is there some pattern that applies when you want to do something with a database in a transaction + do something else?
If you put the db.Commit(); before the foreach loop then any errors with the Commit() will be caught before the foreach gets executed. Only when Commit() is successful will the foreach loop run.