I've a problem for sending data to the server via POST/DELETE.
I always get a CORS error when I'm trying to send data to the WebAPI.
The client-side was implemented with AngularJS and the server-side with ASP.NET WebAPI by C#.
Here's the Code of server-side WebAPI:
//Model:
public class TestRepository : ITestRepository
{
private List<Person> persons = new List<Person>();
public TestRepository()
{
Add(new Fond { id = "DE001", fname = "John", age = 32 });
Add(new Fond { id = "DE002", fname = "Jeffrey", age = 23 });
}
public IEnumerable<Person> GetAll()
{
return persons;
}
public Person Add(Person item)
{
if (item == null) {
throw new ArgumentNullException();
}
persons.Add(item);
return item;
}
public bool Delete(string id)
{
fonds.RemoveAll(p => p.id == id);
return true;
}
}
//Controller
public class TestController : ApiController
{
static readonly ITestRepository rep = new TestRepository();
// GET api/fond
public IEnumerable<Person> GetAllPersons()
{
return rep.GetAll();
}
// POST api/fond
[HttpPost]
public Person PostPerson(Person item)
{
return rep.Add(item);
}
// DELETE api/fond/5
public bool DeletePerson(string id)
{
if (rep.Delete(id))
{
return true;
}
else
{
return false;
}
}
I've installed the Nuget Package Microsoft.AspNet.WebApi.Cors.
In the WebApiConfig file I've added following lines:
...
var cors = new EnableCorsAttribute("http://localhost:63831", "*", "*");
config.EnableCors(cors);
And the client-side Code:
//Get the list works!
$scope.list = ResService.person.query();
//Delete doesn't work
$scope.deletePerson = function (removePers) {
$scope.res = ResService.person.remove(function () {
$log.info('[Resource]', $scope.res);
$scope.res.$delete(removeItem);
});
};
//Post doesn't work
$scope.addPerson = function (newPers) {
var persObj = {
id: newPers.id,
fname: newPers.fname,
age: newPers.age
};
$http.post(baseUrl + '/api/person', persObj).success(function (persData) {
$scope.list.push(persData);
}).error(function (message) {
$log.error(message);
});
Only GetAll function works. When I'm using POST or DELETE then comes an error message Cross-Origin-Request blocked..
Related
So I have my endpoint defined like the following:
[ApiController]
[Route("load/stuff")]
public class SignUp : ControllerBase
{
IGoogleRecaptchaV3Service _gService { get; set; }
public SignUp(IGoogleRecaptchaV3Service gService)
{
_gService = gService;
}
[HttpPost]
public async Task<IActionResult> Post([FromQuery] SignUpModel SignUpData, GRequestModel.Factory grequestFactory)
{
GRequestModel grm = grequestFactory("resValue", "remipValue");
_gService.InitializeRequest(grm);
if (!await _gService.Execute())
{
//return error codes string.
return Ok(_gService.Response.error_codes);
}
//call Business layer
return base.Content("Content here", "text/html");
}
}
This should return the HTML content if the reCAPTCHA score is human-like.
Let me know how to debug this further and whether any further code is required.
UPDATE fetch JS Code
function loadStuff() {
if (location.pathname === "/test") {
grecaptcha.execute('recaptchasitekeyhere', { action: 'onloadafterdelay' }).then(function (token) {
console.log(token);
return fetch("/load/stuff?RecaptchaToken=" + token, {
method: "POST",
body: token,
})
}).then((response) => {
// console.log works here too
if (!response.ok) {
const errorBuild = {
type: "Error",
message: response.message || "Something went wrong",
data: response.data || "",
code: response.code || "",
};
console.log("Error: " + JSON.stringify(errorBuild));
}
response.text().then(body => {
//console.log(body);
document.getElementById("test1").innerHTML = body.split(' ')[0];
document.getElementById("test2").innerHTML = body.split(' ')[1];
});
}
)
}
}
I also added this in the program.cs file:
builder.Services.AddControllers();
// FIX TEST
builder.Services.AddTransient<GRequestModel.Factory>(serviceProvider =>
(string res, string remip) => new GRequestModel(serviceProvider.GetRequiredService<IConfiguration>(), res, remip));
//Register dependencies
builder.Services.AddRazorPages();
// REMOVE ME IN PRODUCTION, USE DI INSTEAD
// ....
Configuration = app.Configuration;
// ...
public partial class Program
{
internal static IConfiguration Configuration { get; private set; }
}
I added that code above as a temporary fix, but then I tried to implement dependency injection for the IConfiguration and my codebase got dirty. I'm still a beginner in C# and I'm learning by trial and error hence so many mistakes.
I have a very simple controller action that takes in a viewmodel. I simply want to check the model in code and if it's not valid, dump the modelstate back as a BadRequest.
[HttpPost]
[Route("SaveBraceStep1")]
[SwaggerOperation(OperationId = "SaveBraceStep1")]
[ProducesResponseType(200, Type = typeof(VM.ProjectBraceDataModelStep1))]
public async Task<IActionResult> SaveBraceStep1(VM.ProjectBraceDataModelStep1 model)
{
if (!ModelState.IsValid)
{
return new BadRequestObjectResult(ModelState.Errors());
}
var project = await bracingDataService.SaveBraceStep1(model);
return Ok(project);
}
When the result comes back to Chrome, it's as expected.
I have an http interceptor as follows:
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
export class HttpErrorInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.pipe(catchError(err => {
// how can I read the results of the ModelState here
console.log(err);
return throwError('An error has been thrown');
}));
}
}
Here is what is dumped to the console
I've scoured thru tons of google searches and user blobs, but I can't seem to find an accepted concise way to do this.
I'm really blown away since I would expect this to be pretty much standardized by now.
Any Thoughts? Thank you in advance. Happy Coding!
~Mike
OK, so I figured this out with the help from https://www.strathweb.com/2018/07/centralized-exception-handling-and-request-validation-in-asp-net-core/
You basically have to register a new Action Type, then insert it into the pipeline.
public Task ExecuteResultAsync(ActionContext context)
{
var modelStateEntries = context.ModelState.Where(e => e.Value.Errors.Count > 0).ToArray();
var errors = new List<ValidationError>();
var details = "See ValidationErrors for details";
if (modelStateEntries.Any())
{
if (modelStateEntries.Length == 1 && modelStateEntries[0].Value.Errors.Count == 1 && modelStateEntries[0].Key == string.Empty)
{
details = modelStateEntries[0].Value.Errors[0].ErrorMessage;
}
else
{
foreach (var modelStateEntry in modelStateEntries)
{
foreach (var modelStateError in modelStateEntry.Value.Errors)
{
var error = new ValidationError
{
Name = modelStateEntry.Key,
Description = modelStateError.ErrorMessage
};
errors.Add(error);
}
}
}
}
var problemDetails = new ValidationProblemDetails
{
Status = 400,
Title = "Request Validation Error",
Instance = $"urn:myorganization:badrequest:{Guid.NewGuid()}",
Detail = details,
ValidationErrors = errors
};
context.HttpContext.Response.StatusCode = 400;
var json = JsonConvert.SerializeObject(problemDetails);
context.HttpContext.Response.WriteAsync(json);
return Task.CompletedTask;
}
}
public class ValidationProblemDetails : ProblemDetails
{
public ICollection<ValidationError> ValidationErrors { get; set; }
}
public class ValidationError
{
public string Name { get; set; }
public string Description { get; set; }
}
Then in startup.cs register it.
services.Configure<ApiBehaviorOptions>(options =>
{
options.InvalidModelStateResponseFactory = ctx => new ValidationProblemDetailsResult();
});
Then wired up in the interceptor
#Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
constructor(private uiService: Services.UIService, private injector: Injector) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.pipe(catchError((err: HttpErrorResponse) => {
const reader = new FileReader();
reader.addEventListener('loadend', (e) => {
const result = this.buildResponse(JSON.parse(e.srcElement['result']));
this.uiService.HttpError(result);
});
reader.readAsText(err.error);
return throwError(err);
}));
}
buildResponse(e: any): ApplicationHttpErrorResponse {
const model: ApplicationHttpErrorResponse = { ValidationErrors: [] };
if (e.ValidationErrors && e.ValidationErrors.length > 0) {
for (let i = 0; i < e.ValidationErrors.length; i++) {
const validator: ApplicationHttpError = {
Name: e.ValidationErrors[i].Name,
Description: e.ValidationErrors[i].Description
};
model.ValidationErrors.push(validator);
}
}
return model;
}
}
It can then be used in the UI service to display issues to the end user.
I want to check if in the GET request (this is GET request) there is only customer id provided and not other parameters. How should I check this in the current context and which is the exception that I should return?
public JObject GetStatus(GetStatusRequest request)
{
var responseArr = new JArray();
var customers = GetCustomers(request.CustomerId);
foreach (var c in customers)
{
responseArr.Add(FormatRequest(customers));
}
return new JObject
{
["customers"] = responseArr
};
}
Using asp.net core 2.1 I'd do it like so...
public class CustomerListResponse {
public List<YourCustomerClassName> Customers {get;set;}
}
public ActionResult<CustomerListResponse> GetStatus(GetStatusRequest request)
{
// check request
if(doyourcheckhere == false) {
return BadRequest();
}
// load your data here. Do not think in JArray and JObject
// simply use POCOs
var customers = GetCustomers(request.CustomerId);
// if you need to reformat, create separate class and use e.g. automapper
return new CustomerListResponse {
Customers = customers
};
}
You can try this:
[HttpGet]
public HttpResponseMessage GetStatus(GetStatusRequest request)
{
try
{
if (request.CustomerId>0 && String.IsNullOrEmpty(request.Customername) /*&& other conditions*/)
{
var customers = GetCustomers(request.CustomerId);
return Request.CreateResponse(HttpStatusCode.OK, customers );
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Your custom error message here");
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, new HttpError(ex.Message));
}
}
Here is my code for Method in TypeScript
Ive been trying to pass an number[] array to the controller to send to a quickbooks api but all i really need right now is to get the values into the controller,
This is been done in Angular 5 .net core 2.0 latest version
The data hits the Post Method with no error and i have breakpoints everywhere it never reaches the controller.
import { Injectable } from '#angular/core';
import { Customer } from '../models/customer';
import { Vendor } from '../models/vendor';
import { Item } from '../models/item';
import { Invoice } from '../models/invoice';
import { CreditNote } from '../models/creditNote';
import { PPO } from '../models/ppo';
import { PO } from '../models/po';
import { AppSettings } from '../models/appSettings';
import { Http, Response, Headers, RequestOptions, RequestMethod, URLSearchParams } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { MsgResult } from '../models/msgResult';
import { Router, ActivatedRoute } from '#angular/router';
import { isPlatformBrowser, isPlatformServer } from '#angular/common';
#Injectable()
export class SyncDataService {
errorMessage: string = "";
baseURL: string = 'http://localhost:56199/api';
constructor(private _http: Http, private route: ActivatedRoute, private router: Router) {}
syncCustomers(ids: Array<number>) {
var headers = new Headers();
headers.append('Content-Type', 'application/json; charset=utf-8');
var localStorage1 = localStorage.getItem('access_token');
if (localSt
orage1 != undefined) {
var token = JSON.parse(localStorage1);
//headers.append('Authorization', 'bearer ' + token);
//return this._http.post(this.baseURL + '/customer', ids, options)
// .map((response: Response) => <string>response.json())
// .catch(err => {
// return this.handleError(err);
// });
var stringids = JSON.stringify({ customerIDs: ids });
this._http.post(this.baseURL + '/customer/PostCust',
stringids).subscribe(result => result.json()), err => {
return this.handleError(err);
}
}
}
}
Here is my controller
[Route("api/[controller]")]
public class CustomerController : Controller
{
private readonly SyncDbContext _dbContext;
public CustomerController(SyncDbContext dbContext)
{
_dbContext = dbContext;
}
[HttpGet]
public List<Customer> Get()
{
return new SyncDataManager().GetCustomers();
}
[HttpPost("[action]")]
[AllowAnonymous]
public JsonResult PostCust([FromBody]int[] customerIDs)
{
// call quicbooks api and pass them the customers
// once quickbooks verifys the customer and sends us back a reference
// pass the quickbooks customer to SyncDataManager
var sync = new SyncDataManager();
var results = sync.UpdateCustomers(customerIDs);
var failedResults = results.Where(m => m.Success == false).ToList();
if(failedResults.Count == 0)
{
var json = new JsonResult("Updated Successfully");
json.StatusCode = 200;
return json;
}
else
{
var error = new StringBuilder();
foreach (var errorMessage in failedResults)
{
//string output = errorMessage.ErrorMessage.Substring(errorMessage.ErrorMessage.IndexOf('.') + 1);
string output = errorMessage.ErrorMessage;
error.AppendLine(output);
}
var json = new JsonResult(error.ToString());
json.StatusCode = 400;
return json;
}
}
There is no error messages and when i use break points on my controller, It does not hit the break points, been at it for 3 days no break through Please help
try in your Controller
[Route("api/[controller]/[action]")] //<--include action
public class CustomerController : Controller
{
...
[HttpGet,ActionName("Get")] //<--I don't know if it's necesary
public List<Customer> Get() {..}
[HttpPost, ActionName("PostCust")] //<--give there the "actionName
[AllowAnonymous]
public JsonResult PostCust([FromBody]int[] customerIDs){...}
}
this._http.post(this.baseURL + '/customer/PostCust',
this.customerIDs).subscribe((data:Response) => { this.resu = (data.json() as string) },
error => {
alert(error.json());
},
() => {
alert("Completed");
} else {
this.RefreshCustomers();
}
});
this.resu is my results variable
and no headers or casts for this.customerIDs this worked for me
I have the following question:
Your application will respond to AJAX requests in JSON format. In
order to maximize control over serialization, you will implement a
custom ActionResult class.
You must override the behavior of the Json helper method in your base
controller so that all JSON responses will use the custom result
class. Which class should you inherit?
The response type is JsonResult. Code-wise I'm having a hard time visualizing the structure. When I read "implement" in the question, I thought of an interface so this is what I came up with:
public class CustAction:ActionResult
{
//max control over serialization
}
public interface ICustAction:CustAction
{
}
public controller MyController:ICustAction, JsonResult
{
//override Json() method in here
}
Would the code above be applicable to the question above?
You can override JsonResult, and return custom JsonResult. For example,
StandardJsonResult
public class StandardJsonResult : JsonResult
{
public IList<string> ErrorMessages { get; private set; }
public StandardJsonResult()
{
ErrorMessages = new List<string>();
}
public void AddError(string errorMessage)
{
ErrorMessages.Add(errorMessage);
}
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
/* If you do not want to serve JSON on HttpGet, uncomment this. */
/*if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet &&
string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("GET access is not allowed. Change the JsonRequestBehavior if you need GET access.");
}*/
var response = context.HttpContext.Response;
response.ContentType = string.IsNullOrEmpty(ContentType) ? "application/json" : ContentType;
if (ContentEncoding != null)
{
response.ContentEncoding = ContentEncoding;
}
SerializeData(response);
}
protected virtual void SerializeData(HttpResponseBase response)
{
if (ErrorMessages.Any())
{
var originalData = Data;
Data = new
{
Success = false,
OriginalData = originalData,
ErrorMessage = string.Join("\n", ErrorMessages),
ErrorMessages = ErrorMessages.ToArray()
};
response.StatusCode = 400;
}
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = new JsonConverter[]
{
new StringEnumConverter(),
},
};
response.Write(JsonConvert.SerializeObject(Data, settings));
}
}
public class StandardJsonResult<T> : StandardJsonResult
{
public new T Data
{
get { return (T)base.Data; }
set { base.Data = value; }
}
}
Base Controller
public abstract class BaseController : Controller
{
protected StandardJsonResult JsonValidationError()
{
var result = new StandardJsonResult();
foreach (var validationError in ModelState.Values.SelectMany(v => v.Errors))
{
result.AddError(validationError.ErrorMessage);
}
return result;
}
protected StandardJsonResult JsonError(string errorMessage)
{
var result = new StandardJsonResult();
result.AddError(errorMessage);
return result;
}
protected StandardJsonResult<T> JsonSuccess<T>(T data)
{
return new StandardJsonResult<T> { Data = data };
}
}
Usage
public class HomeController : BaseController
{
public ActionResult Index()
{
return JsonResult(null, JsonRequestBehavior.AllowGet)
// Uncomment each segment to test those feature.
/* --- JsonValidationError Result ---
{
"success": false,
"originalData": null,
"errorMessage": "Model state error test 1.\nModel state error test 2.",
"errorMessages": ["Model state error test 1.", "Model state error test 2."]
}
*/
ModelState.AddModelError("", "Model state error test 1.");
ModelState.AddModelError("", "Model state error test 2.");
return JsonValidationError();
/* --- JsonError Result ---
{
"success": false,
"originalData": null,
"errorMessage": "Json error Test.",
"errorMessages": ["Json error Test."]
}
*/
//return JsonError("Json error Test.");
/* --- JsonSuccess Result ---
{
"firstName": "John",
"lastName": "Doe"
}
*/
// return JsonSuccess(new { FirstName = "John", LastName = "Doe"});
}
}
Credit: Building Strongly-typed AngularJS Apps with ASP.NET MVC 5 by Matt Honeycutt
public class customJsonResult : JsonResult
{
//max control over serialization
}
//in the base controller override the Controller.Json helper method:
protected internal override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)
{
return new customJsonResult {
Data = data,
ContentType = contentType,
ContentEncoding = contentEncoding,
JsonRequestBehavior = behavior
};
}