For some reason my PUT request gives an BadRequest error. I have checked what the cause is and the cause is that "gebruikersnaam" when arriving in the .NET side is null, instead of its value.
[HttpPut("naam")]
public async Task<IActionResult> VeranderNaam(string gebruikersnaam)
{
IdentityUser user = await this._userManager.FindByNameAsync(User.Identity.Name);
Gebruiker gebruiker = this._gebruikerRepository.GetBy(user.UserName);
Brouwer brouwer = this._brouwerRepository.GetBy(user.UserName);
user.UserName = gebruikersnaam;
var result = await _userManager.UpdateAsync(user);
if (result.Succeeded)
{
if(brouwer != null)
{
brouwer.Naam = gebruikersnaam;
this._brouwerRepository.Update(brouwer);
this._brouwerRepository.SaveChanges();
return Ok();
}
else if(gebruiker != null)
{
gebruiker.Gebruikersnaam = gebruikersnaam;
this._gebruikerRepository.Update(gebruiker);
this._gebruikerRepository.SaveChanges();
return Ok();
}
}
return BadRequest();
}
angular code
onSubmitNaam() {
console.log(this.gebruikersnaam.value.gebruikersnaam);
this.authService.veranderNaam(this.gebruikersnaam.value.gebruikersnaam).subscribe(
() => {
this.success = "Uw naam is met success aangepast";
}, err => {
this.error = "Uw naam is niet aangepast";
}
)
this.gebruikersnaam.reset();
}
veranderNaam(gebruikersnaam: string) {
return this.http.put(`${environment.apiUrl}/gebruikers/naam`, gebruikersnaam);
}
I know my form gets the value, it is sent from a form through the request, but upon arriving on the request it "becomes" null.
In this line of code you are submit the body not in a query string
return this.http.put(`${environment.apiUrl}/gebruikers/naam`, gebruikersnaam);
So if you want your code work you can change into this
public async Task<IActionResult> VeranderNaam([FromBody] string gebruikersnaam)
Or you can use queryParams
Related
Actually, I am displaying the User data after the user has signed in, in the dashboard panel. But only getting the first and last name.
I debugged the code and it gives all other values null. But in the database all the columns are filled with record.
Error Image
Now here is the code, that I am using to get Data from database::
public static User User
{
get
{
return GetUser().Result;
}
set
{
if(value != _user)
{
_user = value;
}
}
}
public async static void Init(IJSRuntime js)
{
#if Debug
HostURL = "http://localhost:5001/"
#endif
jsRuntime = js;
Module = await js.InvokeAsync<IJSObjectReference>("import", "/js/bundle.js");
}
public async static Task<User> GetUser()
{
try
{
if (_user == null)
{
var response = await jsRuntime.InvokeAsync<string>("VerifyData");
var jsonResponse = JsonConvert.DeserializeObject<Response>(response);
var newuser = new User();
newuser.UID = jsonResponse.uid;
newuser.Token = jsonResponse.token;
response = await API.VerifyGetUser(jsonResponse.uid, jsonResponse.token);
jsonResponse = JsonConvert.DeserializeObject<Response>(response);
newuser.FirstName = jsonResponse.firstName;
newuser.LastName = jsonResponse.lastName;
newuser.ProfilePicURL = jsonResponse.profilePicURL;
newuser.Email = jsonResponse.email;
_user = newuser;
}
return _user;
}
catch (Exception ex)
{
Main.Log.Log($"GetUser: {ex}", "ERROR");
}
jsRuntime.InvokeAsync<string>("RemoveVerifyData");
return null;
}
I can't figure out how to edit the row after seeing the changes in DB.
I have an API-project and an MVC-project. I use CRUD in my API and call them with my MVC with HttpClient
I have a public byte[] RowVersion { get; set; } property with the attribute [Timestamp].
I have a clientFactory where I do CreateClient() to perform PutAsync("api.example.com/{id}") action.
The HttpResponseMessage variable on my putasync action returns StatusCode(409) because my API successfully detected a concurrency conflict.
I managed to display error messages before updating the concurrency; showing the newly updated rows in the database(newsDb) with help of a new client, clientFactory.CreateClient(), and comparing them with the inputs(news).
Then I set the news.RowVersion = newsDb.RowVersion and re-display View(news).
And after clicking Save again, nothing happens - no redirects, no changes - the concurrency errors are still there:
[HttpPost("edit/{id}")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditNewsArticle(int id, [Bind("NewsId,Author,Title,Content,CreatedDate,HashTags,RowVersion")] News news)
{
if (id != news.NewsId)
{
return NotFound();
}
if (ModelState.IsValid)
{
news.UpdatedDate = DateTime.Now;
string json = JsonConvert.SerializeObject(news);
HttpResponseMessage putTask = await clientFactory.CreateClient().PutAsync($"https://localhost:44331/api/News/{id}", new StringContent(json, Encoding.UTF8, "application/json"));
if (putTask.IsSuccessStatusCode)
{
return RedirectToAction(nameof(Index));
}
else if (putTask.StatusCode == HttpStatusCode.Conflict)
{
string jsonDb = await clientFactory.CreateClient().GetStringAsync($"https://localhost:44331/api/News/{id}");
News newsDb = JsonConvert.DeserializeObject<News>(jsonDb);
if (newsDb is null)
{
ModelState.AddModelError(string.Empty, $"Unfortunately, the news item you edited has already been deleted by another user.");
}
if (newsDb.Title != news.Title)
{
ModelState.AddModelError("Title", $"Title in database: {newsDb.Title}");
}
if (newsDb.Author != news.Author)
{
ModelState.AddModelError("Author", $"Author in database: {newsDb.Author}");
}
if (newsDb.Content != news.Content)
{
ModelState.AddModelError("Content", $"Content in database: {newsDb.Content}");
}
if (newsDb.HashTags != news.HashTags)
{
ModelState.AddModelError("HashTags", $"HashTags in database: {newsDb.HashTags}");
}
ModelState.AddModelError(string.Empty,
"Editing was canceled as the selected news item was changed by someone else in the meantime." +
"The values of the change are now shown below, which are derived from the database" +
"If you still want to edit the user, click Save again.");
news.RowVersion = newsDb.RowVersion;
}
else
{
ModelState.AddModelError(string.Empty, "Unknown error. Contact a support.");
return View(news);
}
}
return View(news);
}
API Put:
[HttpPut("{id}")]
public async Task<IActionResult> PutNews(int id, [FromBody] News news)
{
if (id != news.NewsId)
{
return BadRequest();
}
context.Entry(news).State = EntityState.Modified;
try
{
await context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!NewsExists(id))
{
return NotFound();
}
else
{
return StatusCode(409);
}
}
return CreatedAtAction("GetNews", new { id = news.NewsId }, news);
}
I found my issue. I needed to call ModelState.Clear(); after de-serializing the 'jsonDb', and also remove RowVersion from Bind in the attribute.
In my IdentityConfig.cs
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
In my Web MVC UI, this is my action method
HttpResponseMessage responsePost = GlobalVariables.WebApiClient.PostAsJsonAsync("Account/Register", registerBindingModel).Result;
if (responsePost.IsSuccessStatusCode)
{
ViewBag.Message = "Added";
}
else
{
ViewBag.Message = "Internal server Error: " + responsePost.ReasonPhrase;
}
The code works. I can post data if it passes validation logic for passwords. But if it fails, it just throw back Internal server Error: Bad Request to my ui. But when I test it using Postman, I can see the error message from identity. Example Passwords must have at least one lowercase ('a'-'z').
Where this error message stored in HttpResponseMessage?
The API method for Register
[AllowAnonymous]
[HttpPost]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (!result.Succeeded)
{
return GetErrorResult(result);
}
return Ok();
}
private IHttpActionResult GetErrorResult(IdentityResult result)
{
if (result == null)
{
return InternalServerError();
}
if (!result.Succeeded)
{
if (result.Errors != null)
{
foreach (string error in result.Errors)
{
ModelState.AddModelError("", error);
}
}
if (ModelState.IsValid)
{
// No ModelState errors are available to send, so just return an empty BadRequest.
return BadRequest();
}
return BadRequest(ModelState);
}
return null;
}
Based upon the comments, you need to parse the string content which returns a JSON string assuming your API is designed in that manner.
I am not sure about your API code but if you need to send your ModelState, you can send it in JSON form using:
return Json(ModelState);
You should not use IsSuccessStatusCode for a way to check for your logic. Basically it is a value that indicates if the HTTP response was successful. It is true if HttpStatusCode was in the Successful range (200-299); otherwise false. The algorithm for IsSuccessStatusCode is shown below:
public bool IsSuccessStatusCode
{
get { return ((int)statusCode >= 200) && ((int)statusCode <= 299); }
}
You can clearly see that the above function only depends on the statusCode received from the API.
Therefore you need to define your logic based on the content that you receive from the API. Create a model that will hold the response from the API based on the condition that you define in the API.
HttpResponseMessage responsePost = GlobalVariables.WebApiClient.PostAsJsonAsync("Account/Register", registerBindingModel).Result;
MyAPIResponse model=new MyAPIResponse();
string content=string.Empty;
if (responsePost.IsSuccessStatusCode)
{
content = response.Content.ReadAsStringAsync().Result;
model = JsonConvert.DeserializeObject<MyAPIResponse>(content);
//or if you do not want a model structure for the parsing
//var parsedmodel = JsonConvert.DeserializeObject<dynamic>(content);
}
ViewBag.Message=model.message;
I was not able to parse the content posted in your comment since it is not a valid JSON string. You would need to generate the correct JSON format for your logic.
EDIT:
So you can send the ModelState errors as JSON using a extension method:
public static class ModelStateHelper
{
public static IEnumerable Errors(this ModelStateDictionary modelState)
{
if (!modelState.IsValid)
{
return modelState.ToDictionary(kvp => kvp.Key,
kvp => kvp.Value.Errors
.Select(e => e.ErrorMessage).ToArray())
.Where(m => m.Value.Any());
}
return null;
}
}
And then call that extension method from the Controller action:
if (!ModelState.IsValid)
{
return Json(new { Errors = ModelState.Errors() }, JsonRequestBehavior.AllowGet);
}
I am running some Integration Tests and I wish to create a "fake" user in my ASPNETUsers table.
I am trying to do the following with no luck.
So I have the Setup as follows :-
[SetUp]
public void Setup()
{
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var projectPath = Path.GetFullPath(Path.Combine(basePath, "../../../../SportsStore2.Tests"));
var server = new TestServer(Utils.GetHostBuilder(new string[] { })
.UseContentRoot(projectPath)
.UseEnvironment("Development")
.UseStartup<Startup>());
_client = server.CreateClient();
}
and then the Insert is as follows :-
var registerViewModel = new RegisterViewModel
{
Email = "test#test.com",
ConfirmPassword = "testing12345?",
Password = "testing12345?"
};
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var postResponseASPNETUser = await _client.PostAsJsonAsync("http://localhost:49406/Account/Register", registerViewModel);
var createdASPNETUser = await postResponseASPNETUser.Content.ReadAsStringAsync();
However I am getting a 400 response, Bad Request.
Any ideas what I am doing wrong?
Thanks for your help and time.
Instead of calling the api directly, since I had no luck and could not find why it was not returning a correct response, I decided to go another route, and basically created another Create Action inside my UsersController as follows:-
[HttpPost("Create")]
public IActionResult Create([FromBody] User user)
{
if (user == null)
return BadRequest();
var result = _usersService.Add(user, m => m.Name == user.Name);
if (result)
{
return CreatedAtRoute("GetUsers", new { id = user.Id }, user);
}
return BadRequest("Item not added");
}
[HttpPost("CreateAspNetUsers")]
public IActionResult CreateAspNetUsers([FromBody] RegisterViewModel registerViewModel)
{
if (registerViewModel == null)
return BadRequest();
var result = _aspNetUsersService.Add(registerViewModel, m => m.Name == registerViewModel.Email);
if (result)
{
return CreatedAtRoute("GetUsers", registerViewModel);
}
return BadRequest("Item not added");
}
This works fine and is giving me the result I was after.
I have an ASP.NET Core 1.0 Web API application and trying to figure out how to pass the exception message to the client if a function that my controller is calling errors out.
I have tried so many things, but nothing implements IActionResult.
I don't understand why this isn't a common thing that people need. If there truthfully is no solution can someone tell me why?
I do see some documentation out there using HttpResponseException(HttpResponseMessage), but in order to use this, I have to install the compat shim. Is there a new way of doing these things in Core 1.0?
Here is something I have been trying with the shim but it isn't working:
// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
Customer c = _customersService.GetCustomerById(id);
if (c == null)
{
var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Customer doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
StatusCode = HttpStatusCode.NotFound
};
throw new HttpResponseException(response);
//return NotFound();
}
return new ObjectResult(c);
}
When the HttpResponseException is thrown, I look on the client and can't find the message I am sending anything in the content.
Here is an simple error DTO class
public class ErrorDto
{
public int Code {get;set;}
public string Message { get; set; }
// other fields
public override string ToString()
{
return JsonConvert.SerializeObject(this);
}
}
And then using the ExceptionHandler middleware:
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
context.Response.StatusCode = 500; // or another Status accordingly to Exception Type
context.Response.ContentType = "application/json";
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
var ex = error.Error;
await context.Response.WriteAsync(new ErrorDto()
{
Code = <your custom code based on Exception Type>,
Message = ex.Message // or your custom message
// other custom data
}.ToString(), Encoding.UTF8);
}
});
});
Yes it is possible to change the status code to whatever you need:
In your CustomExceptionFilterAttribute.cs file modify the code as follows:
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new ContentResult
{
Content = $"Error: {exception.Message}",
ContentType = "text/plain",
// change to whatever status code you want to send out
StatusCode = (int?)HttpStatusCode.BadRequest
};
}
}
That's pretty much it.
If you have custom exceptions, then you can also check for them when grabbing the thrown exception from the context. Following on from that you can then send out different HTTP Status Codes depdending on what has happened in your code.
Hope that helps.
You can create a custom Exception Filter like below
public class CustomExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
var exception = context.Exception;
context.Result = new JsonResult(exception.Message);
}
}
Then apply the above attribute to your controller.
[Route("api/[controller]")]
[CustomExceptionFilter]
public class ValuesController : Controller
{
// GET: api/values
[HttpGet]
public IEnumerable<string> Get()
{
throw new Exception("Suckers");
return new string[] { "value1", "value2" };
}
}
Rather than raising and catching an exception, how about you simplify your action to:
// GET: api/customers/{id}
[HttpGet("{id}", Name = "GetCustomer")]
public IActionResult GetById(int id)
{
var customer = _customersService.GetCustomerById(id);
if (customer == null)
{
return NotFound("Customer doesn't exist");
}
return Ok(customer);
}
I wrote a blog post with some more options such as returning a JSON object instead of text.
Maybe that is helpful. You can return just object and sent for example a BadRequest (HTTP CODE: 400) with your custom object as actual parameter (I just used an interpolated string here) but you can put in anything.
In your client side you can catch that error situation for example with an AJAX error handler.
// GET: api/TruckFahrerGeoData
[HttpGet]
public object GetTruckFahrerGeoData()
{
var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();
var geodataItems = _context.TruckFahrerGeoData;
foreach (var truckFahrerGeoData in geodataItems)
{
GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);
if (geoTelemetryData == null)
{
return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
}
TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
{
Speed = geoTelemetryData.Speed,
Accuracy = geoTelemetryData.Accuracy,
TruckAppId = geoTelemetryData.Activity.TruckAppId,
TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
ClId = geoTelemetryData.Activity.ClId,
TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
TaskId = geoTelemetryData.Activity.TaskId,
TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
};
truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
}
return truckFahrerGeoDataItems;
}
Or an even more cleaner way with IActionResult like that way:
// GET: api/TruckFahrerGeoData
[HttpGet]
public IActionResult GetTruckFahrerGeoData()
{
var truckFahrerGeoDataItems = new List<TruckFahrerGeoDataViewModel>();
var geodataItems = _context.TruckFahrerGeoData;
foreach (var truckFahrerGeoData in geodataItems)
{
GeoTelemetryData geoTelemetryData = JsonConvert.DeserializeObject<GeoTelemetryData>(truckFahrerGeoData.TelemetryData);
if (geoTelemetryData == null)
{
return BadRequest($"geoTelemetryData null for id: {truckFahrerGeoData.Id}");
}
TruckFahrerGeoDataViewModel truckFahrerGeoDataViewModel = new TruckFahrerGeoDataViewModel
{
Speed = geoTelemetryData.Speed,
Accuracy = geoTelemetryData.Accuracy,
TruckAppId = geoTelemetryData.Activity.TruckAppId,
TruckAuftragStatusId = geoTelemetryData.Activity.TruckAuftragStatusId,
ClId = geoTelemetryData.Activity.ClId,
TruckAuftragLaufStatusId = geoTelemetryData.Activity.TruckAuftragLaufStatusId,
TaskId = geoTelemetryData.Activity.TaskId,
TruckAuftragWorkflowStatusId = geoTelemetryData.Activity.TruckAuftragWorkflowStatusId
};
truckFahrerGeoDataItems.Add(truckFahrerGeoDataViewModel);
}
return Ok(truckFahrerGeoDataItems);
}
Late to the party but refining the answer .
Define your error response class with minimum below attributes
using Microsoft.AspNetCore.Http;
public class ErrorResponse
{
private readonly RequestDelegate next;
public ErrorResponse(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context )
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception ex)
{
var code = HttpStatusCode.InternalServerError;
string result = string.Empty;
object data = new object();
if (ex is ForbiddenException)
{
code = HttpStatusCode.Forbidden;
result = JsonConvert.SerializeObject(new Response<object>(Status.Forbidden(ex.Message), data));
}
else if(ex is BadRequestException){
code = HttpStatusCode.BadRequest;
result = JsonConvert.SerializeObject(new Response<object>(Status.BadRequest(ex.Message), data));
}
else if (ex is NotFoundException)
{
code = HttpStatusCode.NotFound;
result = JsonConvert.SerializeObject(new Response<object>(Status.NotFound(ex.Message), data));
}
else if (ex is UnauthorizedException)
{
code = HttpStatusCode.Unauthorized;
result = JsonConvert.SerializeObject(new Response<object>(Status.Unauthorized(ex.Message), data));
}
else
{
result = JsonConvert.SerializeObject(new Response<object>(Status.InternalServerError(ex.Message), data));
}
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)code;
return context.Response.WriteAsync(result);
}
}
Next use this class as middleware in startup.cs class
app.UseHttpsRedirection();
app.UseMiddleware(typeof(ErrorResponse));
Now each request and response will go through this class,if an error occurs then error code will be set to true with error code. A sample response like below
data: {}
status: {
code: 404
error: true
message: "No employee data found"
type: "Not Found"
}
I had the same problem and after some research, I found out I could use HttpClient to call my API and read the response easily. HttpClient does not throw any error when the HTTP response contains an error code, but it sets the IsSuccessStatusCode property to false.
This is my function using the HttpClient. I call this from my controller.
public static async Task<HttpResponseMessage> HttpClientPost(string header, string postdata, string url)
{
string uri = apiUrl + url;
using (var client = new HttpClient())
{
//client.BaseAddress = new Uri(uri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", header);
HttpResponseMessage response = await client.PostAsync(uri, new StringContent(postdata));
return response;
}
}
This is my controller code, where I call the function and read the response and determine whether I have an error or not and respond accordingly. Note that I am checking the IsSuccessStatusCode.
HttpResponseMessage response;
string url = $"Setup/AddDonor";
var postdata = JsonConvert.SerializeObject(donor);
response = await ApiHandler.HttpClientPost(HttpContext.Session.GetString(tokenName), postdata, url);
//var headers = response.Headers.Concat(response.Content.Headers);
var responseBody = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
tnxresult = JsonConvert.DeserializeObject<TnxResult>(AppFunctions.CleanResponse(responseBody));
return Json(new
{
ok = true,
message = tnxresult.Message,
statusCode = tnxresult.StatusCode
});
}
else
{
ApiError rs = JsonConvert.DeserializeObject<ApiError>(AppFunctions.CleanResponse(responseBody));
return Json(new
{
ok = false,
message = rs.Message,
statusCode = rs.StatusCode
});
}
My API returns error messages in JSON. If the call is successful, I am packing the response in JSON too.
The crucial line of code is this one...
var responseBody = await response.Content.ReadAsStringAsync();
It serializes the HTTP content to a string as an asynchronous operation.
After that I can convert my JSON string to an object and access the error/success message and the Status Code too.