ASP.NET Core Web API Bad Request - c#

I'm a tad confused - I've created an ASP.NET Core Web API MVC Project but when i try to make a request i am getting the following response:
I am posting to https://localhost:44337/api/Solve with Postman the following body:
{
"examId":"0f537776-1acf-478f-82ee-c8476bc3e005",
"selectedAnswers":
[
{
"id":"9163fd1c-ec0f-4f1f-8ead-05ffeac36426",
"answerText":"Yes",
"isCorrect":true
},
{
"id":"00545a13-212b-46a5-9d06-3f6abbb9f1d8",
"answerText":"Yes",
"isCorrect":true
}
]
}
and receive this as a response:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "Bad Request",
"status": 400,
"traceId": "8000005f-0001-ff00-b63f-84710c7967bb"
}
I've already included the Content-Type.
GlobalConstants.RouteConstants.ApiRoute = "api/" GlobalConstants.RouteConstants.PostSolve = "Solve"
Here is my controller:
[Route(GlobalConstants.RouteConstants.ApiRoute)]
[ApiController]
public class ESchoolController : ControllerBase
{
protected IApiService ApiService { get; set; }
public ESchoolController(IApiService apiService)
{
this.ApiService = apiService;
}
//POST: api/Solve
[HttpPost]
[Route(GlobalConstants.RouteConstants.PostSolve)]
public IActionResult Solve([FromBody]ExamApiSolveInputModel model)
{
if (!this.ModelState.IsValid)
{
return this.BadRequest();
}
try
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var result = this.ApiService.SolveExam(model, userId);
return this.Ok(result);
}
catch (Exception e)
{
return this.BadRequest(e.Message);
}
}
}
Here are my input models:
public class ExamApiSolveInputModel
{
public ExamApiSolveInputModel()
{
this.SelectedAnswers = new List<AnswerApiInputModel>();
}
public string ExamId { get; set; }
public ICollection<AnswerApiInputModel> SelectedAnswers { get; set; }
}
public class AnswerApiInputModel
{
public string Id { get; set; }
public string AnswerText { get; set; }
public bool IsCorrect { get; set; }
}
I've been searching for solution, but unsuccessfully. I've tried some things like:
When i make request it's not entering the controller. I've checked
it with the debugger.
Making another post method but again it's not entering the code via
debugger, so I think the problem isn't in the code.
Any ideas how to solve this problem? Thanks alot and happy holidays!

I removed the services.AddMvc(options => services.AddMvc(options => options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute())); from the Startup.cs file and the problem disappeared!

You need to provide more information how do you make your request.
Make sure you do include your JSON in body and set Content-Type header as "application/json"

Related

JsonIgnore attribute is not working in asp.net

My model is
using Newtonsoft.Json;
...
public class User
{
public int Id { get; set; }
public string Login { get; set; }
public string Password { get; set; }
[JsonIgnore]
public string Email { get; set; }
public User()
{
}
}
and controller method is
[HttpPost]
public async Task<ActionResult<User>> PostUser(User user)
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
return CreatedAtAction("GetUser", new { id = user.Id }, user);
}
I am using adding newtonsoft to a project in Program.cs like that:
builder.Services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
And when i try to post user in postman, i getting validation error neither i am including email field or not:
"errors": {
"Email": [
"The Email field is required."
]
},
I tried different ways of including newtonsoft in Program.cs, also i tried to make my model with [DataContract] excluding email field. If i remove builder.Services.AddMvc().AddNewtonsoftJson() then with email field validation is passing, but without it still failing. using System.Text.Json.Serialization not working too. What can i do?
I fixed validation problem after making string nullable,
public string? Email { get; set; }

C# How to Properly Consume Yelp GraphQL api

I'm trying to call the business endpoint of Yelp's GraphQL api with my asp.net core mvc application using GraphQLHttpClient. I have the api and bearer token configured in my client instance. I followed the query structure here using business as the endpoint and I just wanted Id and Name fields from the data. When I call SendQueryAsync(query), I get a GraphQL Error from the response. I'm not sure if I'm making an improper httprequest and/or my query is written wrong. I couldn't find any YouTube videos, stackoverflow questions, or github projects regarding consuming Yelp's GraphQL api using C#. Any help is appreciated. Thank you! Below is my source code and attached response.
[Update: Resolved Issued]
There were a collection of issues. Added additional required fields with variables to YelpGraphQL query for GraphQL request. More about query structure and variable declaration is explained in this thread. Overrided the casing of the fields (ty Neil). Fixed the responsetype class and added the missing classes (ty Neil). Added searchconsumer class to controller via dependency injection. Also I will post copied text of exceptions next time.
Classes
public class Business
{
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
public class Search
{
[JsonPropertyName("business")]
public List<Business> business { get; set; }
}
public class SearchResponseType
{
[JsonPropertyName("search")]
public Search Search { get; set; }
}
public interface ISearchConsumer
{
public Task<List<Business>> GetAllBusinesses();
}
public class SearchConsumer : ISearchConsumer
{
private readonly ApplicationDbContext _dbContext;
public SearchConsumer(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<List<Business>> GetAllBusinesses()
{
var authorization = _dbContext.Authorizations.FirstOrDefault().Token;
var _client = new GraphQLHttpClient("https://api.yelp.com/v3/graphql", new NewtonsoftJsonSerializer());
_client.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authorization);
var query = new GraphQLRequest
{
Query = #"
query($termId: String $locationId: String){
search(term:$termId location:$locationId) {
business {
id
name
}
}
}",
Variables = new
{
termId = "burrito",
locationId = "san francisco"
}
};
var response = await _client.SendQueryAsync<SearchResponseType>(query);
var businesses = response.Data.Search.business;
return businesses;
}
}
Controllers
public class YelpGraphQLController : Controller
{
private readonly ISearchConsumer _consumer;
public YelpGraphQLController(ISearchConsumer consumer)
{
_consumer = consumer;
}
public IActionResult Index()
{
return View();
}
[HttpGet]
public async Task<IActionResult> Get()
{
var businesses = await _consumer.GetAllBusinesses();
return Ok(businesses);
}
}
Program
ConfigureServices(builder.Services);
void ConfigureServices(IServiceCollection services)
{
services.AddTransient<ISearchConsumer, SearchConsumer>();
}
YelpGraphQL Json Data Example
{
"data": {
"search": {
"business": [
{
"id": "wGl_DyNxSv8KUtYgiuLhmA",
"name": "Bi-Rite Creamery"
},
{
"id": "lJAGnYzku5zSaLnQ_T6_GQ",
"name": "Brenda's French Soul Food"
}
]
}
}
}
Debug GraphQL Error
I'm guessing that the deserialization isn't working because of the casing of the fields vs your class, which you can override like so:
public class Business
{
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
}
public class ResponseBusinessCollectionType
{
[JsonPropertyName("businesses")]
public List<Business> Businesses { get; set; }
}

ASP.NET Core Web API & EF Core Models with Foreign Key relationship

I'm developing a basic Web API project for education purposes, and I am having trouble with my EF Model relationships. I have 2 models. Message and MessageBoard.
public class Message
{
public long Id { get; set; }
public string Text { get; set; }
public string User { get; set; }
public DateTime PostedDate { get; set; }
public long MessageBoardId { get; set; }
[ForeignKey("MessageBoardId")]
public MessageBoard MessageBoard { get; set; }
}
public class MessageBoard
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ICollection<Message> Messages { get; set; }
}
I've setup my DBContext and created a migration to configure the database. I generated two Web API controllers using EF Scaffolding. The migration appears to correctly detect the relationship between the two models:
modelBuilder.Entity("Asp.net_Core_Web_Api.Models.Message", b =>
{
b.HasOne("Asp.net_Core_Web_Api.Models.MessageBoard", "MessageBoard")
.WithMany("Messages")
.HasForeignKey("MessageBoardId")
.OnDelete(DeleteBehavior.Cascade);
});
But, when I create a MessageBoard and then create a Message with the ID of the MessageBoard, they don't appear to link correctly. In PostMan, I am doing the following:
1) Post a new MessageBoard
POST - https://localhost:44384/api/MessageBoards/
Body - Raw - Json
{
"Name":"Test Board",
"Description":"A Message board for testing purposes."
}
Returns
{
"id": 4,
"name": "Test Board",
"description": "A Message board for testing purposes.",
"messages": null
}
2) Post a new Message
POST - https://localhost:44384/api/Messages
Body - Raw - JSON
{
"Text":"Posting my first message!",
"User":"Jesse",
"PostedDate":"1/1/2019",
"MessageBoardId":4
}
Returns
{
"id": 2,
"text": "Posting my first message!",
"user": "Jesse",
"postedDate": "2019-01-01T00:00:00",
"messageBoardId": 4,
"messageBoard": null
}
I would expect that the messageBoard would not be null, and it would instead return the JSON for the messageBoard that was previously created. If I change to a GET method, it is also null. Why is it null?
EDIT: Here are my controllers. I removed actions except for GET and POST.
[Route("api/[controller]")]
[ApiController]
public class MessageBoardsController : ControllerBase
{
private readonly MessageBoardContext _context;
public MessageBoardsController(MessageBoardContext context)
{
_context = context;
}
// GET: api/MessageBoards/5
[HttpGet("{id}")]
public async Task<ActionResult<MessageBoard>> GetMessageBoard(long id)
{
var messageBoard = await _context.MessageBoards.FindAsync(id);
if (messageBoard == null)
{
return NotFound();
}
return messageBoard;
}
// POST: api/MessageBoards
[HttpPost]
public async Task<ActionResult<MessageBoard>> PostMessageBoard(MessageBoard messageBoard)
{
_context.MessageBoards.Add(messageBoard);
await _context.SaveChangesAsync();
return CreatedAtAction("GetMessageBoard", new { id = messageBoard.Id }, messageBoard);
}
}
[Route("api/[controller]")]
[ApiController]
public class MessagesController : ControllerBase
{
private readonly MessageBoardContext _context;
public MessagesController(MessageBoardContext context)
{
_context = context;
}
// GET: api/Messages/5
[HttpGet("{id}")]
public async Task<ActionResult<Message>> GetMessage(long id)
{
var message = await _context.Messages.FindAsync(id);
if (message == null)
{
return NotFound();
}
return message;
}
// POST: api/Messages
[HttpPost]
public async Task<ActionResult<Message>> PostMessage(Message message)
{
_context.Messages.Add(message);
await _context.SaveChangesAsync();
return CreatedAtAction("GetMessage", new { id = message.Id }, message);
}
}
You need to load related data
So for example, for your MessageBoard GET - // GET: api/MessageBoards/5
Change from:
var messageBoard = await _context.MessageBoards.FindAsync(id);
To
var messageBoard = await _context.MessageBoards
.Include(i=>i.Messages)
.FirstOrDefaultAsync(i => i.Id == id);
I would expect that the messageBoard would not be null, and it would instead return the JSON for the messageBoard that was previously created. If I change to a GET method, it is also null. Why is it null?
This is because you are returning the newly created message, here only MessageBoadId is exists, not MessageBoad object. So you have to load the related MessageBoad from database using Include for newly created message.
Your PostMessage method should be as follows:
// POST: api/Messages
[HttpPost]
public async Task<ActionResult<Message>> PostMessage(Message message)
{
_context.Messages.Add(message);
await _context.SaveChangesAsync();
var message = await _context.Messages
.Include(i=>i.MessageBoard)
.FirstOrDefaultAsync(i => i.Id == message.Id);
return Json(message);
}
You've given the the application output and what the database looks like, but not the middle bit on how it saves/retrieves the data.
Without knowing what's going on in the middle, my best stab in the dark is that you've neither set your lazy loading correctly nor used Include to include the MessageBoard entity.
More info here on what they are.

Asp net core bad model in parameter at post action is not set to null

I have this problem I'm following the Api course on pluralsight and I've been trying to understand why when I pass an invalid Dto in a post request it doesn't get set to null.
Here is my Dto
public class AuthorCreateDto
{
public string Name { get; set; }
public string LastName { get; set; }
public int GenreId { get; set; }
public int CountryId { get; set; }
}
and action
[Route("")]
[HttpPost]
public ActionResult<AuthorDto> CreateAuthor([FromBody]AuthorCreateDto authorCreateDto)
{
if (authorCreateDto == null)
return BadRequest();
var author = Mapper.Map<Author>(authorCreateDto);
if (TryValidateModel(author))
return BadRequest();
var newAuthor = _authorService.CreateAuthor(author);
var newAuthorDto = Mapper.Map<AuthorDto>(newAuthor);
return CreatedAtRoute("GetAuthor", new { id = newAuthor.Id }, newAuthorDto);
}
so when I post an invalid json as
{
"epa": 2,
"wdawd": "awdawd"
}
authorCreateDto does not get set to null while on the course it does. Idk whats going on thank you
For Asp.Net Core, its built-in serializer is Newtonsoft.Json and for FromBody, it will use JsonInputFormatter to bind the request body to model.
By default, SerializerSettings.MissingMemberHandling is Ignore which return default value for the properties which is missing in the request body.
If you prefer null for authorCreateDto, you could configure it with Error by
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddJsonOptions(options => {
options.SerializerSettings.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Error;
});
}

Angular POST request received in NET Core Api as Null

Im POSTing some data via Angular 6, but my Core API keeps returning nulls:
Request:
{"id":0,"name":"test","weight":2,"frequency":2,"activityTypeModelId":3}
Response:
{id: 0, name: null, weight: 0, frequency: 0, activityTypeModelId: 0}
Controller:
[HttpPost("[action]")]
public IActionResult Add([FromForm]Model model)
{
return new JsonResult(model);
}
Angular, using HttpClient:
add(Model: model) {
return this.http.post(this.addUrl, model);
}
API Model:
public class Model
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int Weight { get; set; }
public int Frequency { get; set; }
public int ActivityTypeModelId { get; set; }
}
TS Model:
export class Model{
id?: number;
name?: string;
weight?: number;
frequency?: number;
activityTypeModelId?: number;
}
Everything works fine when I'm using Postman. I already tried with [FromBody]. Where is the problem?
I dont know why, but this fixed my issue:
I created a header:
const header = new HttpHeaders()
.set('Content-type', 'application/json');
Changed the POST function by adding a header and JSON.Stringyfy the object:
add(model: Model): Observable<Model> {
const body = JSON.stringify(c);
return this.http.post<Model>(this.addUrl, body, { headers: header} );
}
Changed [FromForm] to [FromBody].
Adding JSON.stringify(model) in the parameters of the http.post was not working.
JSON that is working with the CORE Api:
{"name":"test","weight":2,"activityTypeModelId":15}
JSON that is not working with the CORE Api:
{name:"test",weight:2,activityTypeModelId:15}
Without the header I encountered a 415 error from the API.
Try
return this.http.post(this.addUrl, JSON.stringify(model) );
I think that, in .NET Core 2.1 is (see https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-2.1)
HttpPost("[action]")]
//see that I put [FromBody]
public IActionResult Add([FromBody]Model model)
{
//OK is one of several IActionResult
return OK(model);
}
I had this issue and the problem was that I was trying to bind to an interface:
[HttpPost("[action]")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK]
public bool SaveResponse([FromBody]ISaveRequestViewModel request) =>
Service.SaveResponse(request.Responses);
I fixed it by changing it to a class:
[HttpPost("[action]")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK]
public bool SaveResponse([FromBody]SaveRequestViewModel request) =>
Service.SaveResponse(request.Responses);
This model also had to use classes instead of interfaces:
public class SaveRequestViewModel
{
public List<IQuestionResponseViewModel> Responses { get; set; }
}
Became
public class SaveRequestViewModel
{
public List<QuestionResponseViewModel> Responses { get; set; }
}
I guess the model binder just doesn't work with interfaces.

Categories