Angular Posting to .net Web API - c#

I am trying to execute a POST from my angular application to a .net Web API instance but the server returns null.
server
[HttpPost]
public string callBcknd([FromBody]string body)
{
try
{
Log.Info(string.Format("{0}", body));
}
catch(Exception ex)
{
return "error";
}
}
}
angular *note that I am using HttpClient instead of Http.. not sure if this is also the problem
callServer(){
var test = { "name": "John" }
let data = JSON.stringify(test);
let headers = new HttpHeaders();
headers.set('Content-Type', 'application/json');
this.appService.http.post('http://localhost:3000/api/WebApI/callBcknd',
test,
{headers: headers})
.subscribe(data => {console.log(data);}}}
config
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {action = "GET", id = RouteParameter.Optional}
);
}
}
With above setting, I don't gen any 404 server error in client side(by checking chrome's console) but it returns null in backend. But as I tried with Postman, it sends values properly with the same url. If I don't include [FromBody] inside of method in backend, I get an 404 server error in client side. Furthermore, messages says "NO HTTP resource was found that matches the request URI". Similar question to this seems solving the problem by having [FromBody] but I still get a null... I also suspected maybe my web config file(not the one in above) should contain some headers, so when I added some headers like content type to be json and etc then I get 500 server error in client side. At this point i am really confused and not sure what to proceed.
UPDATE1
Following server code returns the message but I am still getting the body as null.. no errors have been observed
[HttpPost]
public IHttpActionResult Callbcknd([FromBody] string body)
{
try
{
Log.Info(string.Format("called with data {0}", body));
return Ok(new { Message = "It worked!" });
}
catch(Exception ex)
{
return base.Content(HttpStatusCode.InternalServerError, ex.ToString());
}
}

I see multiple reasons why you would get unexpected errors and null values in your code:
(error) Your .net method callBcknd should not even compile as it can only return something if there is an Exception.
(error) You should send json when sending data to your api controller the message body and the api controller method should accept an complex object and not a primitive type like string/int/bool etc.
(warning) Your angular service should expose functionality and return either observables or promises that the component can then subscribe to. Do not expose the HttpClient directly.
(warning) Your web api should return interface IHttpActionResult instead of the type directly. Then you can use the built in methods like Ok and Content and BadRequest to return status information along with data. See also Action Results in Web API 2
(suggestion) Use Route and RoutePrefix as attributes instead of relying on the route config. This is more flexible and will allow you to also specify parameters to be included in the URL which will make for a more RESTful design. See also Attribute Routing in ASP.NET Web API 2
(suggestion) Add CamelCasePropertyNamesContractResolver to resolve between camel and pascal casing between your front end and backend. See also Serialization using ContractResolver
This is a good example of how make calls to a Web API and how to structure your code.
Note that these code samples only show the relevant parts that were added or modified
WebApiConfig.cs
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
// add this to ensure that casing is converted between camel case (front end) and pascal case (c#/backend)
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.MapHttpAttributeRoutes();
}
}
ApiModel.cs
public class ApiModel {
public string Content {get;set;}
}
WebApIController.cs
[RoutePrefix("api/WebApI")]
public class WebApIController : ApiController {
[HttpPost]
[Route("callBcknd")]
public IHttpActionResult CallBcknd([FromBody] ApiModel body)
{
try
{
Log.Info(string.Format("{0}", body.Content));
return Ok(new {Message = "It worked!"});
}
catch(Exception ex)
{
// example of how to return error with content. I would not recommend actually returning the exception details to the client in a production setting
return base.Content(HttpStatusCode.InternalServerError, ex.ToString());
}
}
}
application.service.ts
constructor(private httpClient: HttpClient){}
callServer(data: {content: string}) : Observable<any> {
return this.httpClient.post('http://localhost:3000/api/WebApI/callBcknd', data);
}
application.component.ts
constructor(private myService: MyService){}
onDoSomething(){
this.myService.callServer({content: 'This is what I have sent'})
.subscribe(data => console.log("Succeeded, result = " + data), (err)=> console.error("Failed! " + err));
}
Notice the following:
ApiModel represents the incoming object in the request. The angular call then sends {content: 'This is what I have sent'} which mirrors this type.
IHttpActionResult is the response type for your Web API method
You can return different types along with status information in the method CallBcknd
Route and RoutePrefix were added to give more control over the uri path.
The angular component and service have been split into 2 methods, the service returns an observable and the component calls the service methods and subcribes to the returning observable. When you extend this example you want to replace any with defined expected results using interfaces and the same is true for any incoming parameters you want to send.

A Tipical call to a API from "Angular"
update(data: string): Observable<IResponse> {
console.log(data);
let url = '...';
let headers = new Headers({
'Content-Type': 'application/json; charset=utf-8',
});
let options = new RequestOptions({ headers: headers })
return this._http.post(url, data, options)
.map((res: any) => {
return res.json();
})
.catch(this.handleError);
}
The code in API
[HttpPost]
public string callBcknd([FromBody]string body)
{
try
{
Log.Info(string.Format("{0}", body));
//You must return something
return "Post Realized";
}
catch(Exception ex)
{
return "error";
}
}
//I like call async
[HttpPost]
public async Task<IActionResult>callBcknd([FromBody]string body)
{
try
{
Log.Info(string.Format("{0}", body));
//await "some action"
//You can return OK("success") or an object
return Ok(new { success = true, description = "callback sucesfully" });;
}
catch(Exception ex)
{
//You can return OK("error") or an object
return Ok(new { success = false, description = ex.InnerException });;
}
}

Well, what you're posting would look something like
{"body":{// something here }}
Whereas your controller expects:
"valuehere" (which is a valid json for string).
You need to change the c# code to have a model for your DTO:
public class PostedObject{
public object Data {get;set;}
}
[HttpPost]
public string callBcknd([FromBody]PostedObject body)
{
// do things
}

Related

How to make a PATCH request to am API with ASP.NET Core 2.2?

I have an application that is written using C# on the top of ASP.NET Core 2.2. I created a controller that responds to a PATCH request called Update. Here is my controller
[Route("api/[controller]"), ApiController, Produces("application/json")]
public class CategoriesController : ControllerBase
{
[HttpPatch("{propertyId}/{listId}/{isOn}"), Authorize]
public ActionResult<bool> Update(int propertyId, int listId, bool isOn)
{
// Do something
return true;
}
}
also, to return a 401 error when the user isn't authorized instead of a redirect, I added the following code to my Startup class
services.ConfigureApplicationCookie(config =>
{
config.Events = new CookieAuthenticationEvents
{
OnRedirectToLogin = ctx =>
{
if (ctx.Request.Path.StartsWithSegments("/api", StringComparison.CurrentCultureIgnoreCase))
{
ctx.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
ctx.Response.Redirect(ctx.RedirectUri);
}
return Task.FromResult(0);
}
};
});
Now to call this API using jQuery I did the following
$('.update-property').click(function (e) {
e.preventDefault();
var obj = $(this);
$.ajax({
url: '/api/Categories/Update',
type: 'PATCH',
data: {
'propertyId': obj.data('property-id'),
'listId': obj.data('list-id'),
'isOn': obj.data('is-on') === 'True' ? 'false' : 'true'
},
success: function (response) {
console.log(response);
// if response unauthorized, redirect to the login page with the ReturnUrl
// else if response.data is true then change the icon
}
});
});
But this request keeps returning a 404 http error code. I inspected this question in the developer tools and I see that the parameter are being set correctly and the URL is valid.
How can I correctly handle this PATCH request?
[Route("api/[controller]"), ApiController, Produces("application/json")]
public class CategoriesController : ControllerBase
{
[HttpPatch("{propertyId}/{listId}/{isOn}"), Authorize]
public ActionResult<bool> Update(int propertyId, int listId, bool isOn)
The route templates here determine the URL for your Update endpoint. In this case, the resulting URL template would be this:
api/Categories/{propertyId}/{listId}/{isOn}
So for example, a valid URL for this endpoint would be /api/Categories/12/34/true.
If you don’t want to pass the values as parameters (since you already pass them in the body), you have to change the route template. For example, you could just remove the route template on the action method:
[HttpPatch, Authorize]
public ActionResult<bool> Update(int propertyId, int listId, bool isOn)
// …
Then, the URL would be just api/Categories.
Of course, you could also make api/Categories/Update the URL for the endpoint, but with REST, it’s usually not recommended to have method names in the URL. A PATCH request usually means “update this resource” and then you would be pointing to the URL of the resource.
you need to send request with localhost:44313/api/Categories url. Don't put update prefix. You just need to send Patch request to your localhost:44313/api/Categories endpoint.
with Parameters
localhost:44313/api/Categories/{propertyId}/{listId}/{isOn}

ASP.NET Core return JSON with status code

I'm looking for the correct way to return JSON with a HTTP status code in my .NET Core Web API controller. I use to use it like this:
public IHttpActionResult GetResourceData()
{
return this.Content(HttpStatusCode.OK, new { response = "Hello"});
}
This was in a 4.6 MVC application but now with .NET Core I don't seem to have this IHttpActionResult I have ActionResult and using like this:
public ActionResult IsAuthenticated()
{
return Ok(Json("123"));
}
But the response from the server is weird, as in the image below:
I just want the Web API controller to return JSON with a HTTP status code like I did in Web API 2.
The most basic version responding with a JsonResult is:
// GET: api/authors
[HttpGet]
public JsonResult Get()
{
return Json(_authorRepository.List());
}
However, this isn't going to help with your issue because you can't explicitly deal with your own response code.
The way to get control over the status results, is you need to return a ActionResult which is where you can then take advantage of the StatusCodeResult type.
for example:
// GET: api/authors/search?namelike=foo
[HttpGet("Search")]
public IActionResult Search(string namelike)
{
var result = _authorRepository.GetByNameSubstring(namelike);
if (!result.Any())
{
return NotFound(namelike);
}
return Ok(result);
}
Note both of these above examples came from a great guide available from Microsoft Documentation: Formatting Response Data
Extra Stuff
The issue I come across quite often is that I wanted more granular control over my WebAPI rather than just go with the defaults configuration from the "New Project" template in VS.
Let's make sure you have some of the basics down...
Step 1: Configure your Service
In order to get your ASP.NET Core WebAPI to respond with a JSON Serialized Object along full control of the status code, you should start off by making sure that you have included the AddMvc() service in your ConfigureServices method usually found in Startup.cs.
It's important to note thatAddMvc() will automatically include the Input/Output Formatter for JSON along with responding to other request types.
If your project requires full control and you want to strictly define your services, such as how your WebAPI will behave to various request types including application/json and not respond to other request types (such as a standard browser request), you can define it manually with the following code:
public void ConfigureServices(IServiceCollection services)
{
// Build a customized MVC implementation, without using the default AddMvc(), instead use AddMvcCore().
// https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc/MvcServiceCollectionExtensions.cs
services
.AddMvcCore(options =>
{
options.RequireHttpsPermanent = true; // does not affect api requests
options.RespectBrowserAcceptHeader = true; // false by default
//options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
//remove these two below, but added so you know where to place them...
options.OutputFormatters.Add(new YourCustomOutputFormatter());
options.InputFormatters.Add(new YourCustomInputFormatter());
})
//.AddApiExplorer()
//.AddAuthorization()
.AddFormatterMappings()
//.AddCacheTagHelper()
//.AddDataAnnotations()
//.AddCors()
.AddJsonFormatters(); // JSON, or you can build your own custom one (above)
}
You will notice that I have also included a way for you to add your own custom Input/Output formatters, in the event you may want to respond to another serialization format (protobuf, thrift, etc).
The chunk of code above is mostly a duplicate of the AddMvc() method. However, we are implementing each "default" service on our own by defining each and every service instead of going with the pre-shipped one with the template. I have added the repository link in the code block, or you can check out AddMvc() from the GitHub repository..
Note that there are some guides that will try to solve this by "undoing" the defaults, rather than just not implementing it in the first place... If you factor in that we're now working with Open Source, this is redundant work, bad code and frankly an old habit that will disappear soon.
Step 2: Create a Controller
I'm going to show you a really straight-forward one just to get your question sorted.
public class FooController
{
[HttpPost]
public async Task<IActionResult> Create([FromBody] Object item)
{
if (item == null) return BadRequest();
var newItem = new Object(); // create the object to return
if (newItem != null) return Ok(newItem);
else return NotFound();
}
}
Step 3: Check your Content-Type and Accept
You need to make sure that your Content-Type and Accept headers in your request are set properly. In your case (JSON), you will want to set it up to be application/json.
If you want your WebAPI to respond as JSON as default, regardless of what the request header is specifying you can do that in a couple ways.
Way 1
As shown in the article I recommended earlier (Formatting Response Data) you could force a particular format at the Controller/Action level. I personally don't like this approach... but here it is for completeness:
Forcing a Particular Format If you would like to restrict the response formats for a specific action you can, you can apply the
[Produces] filter. The [Produces] filter specifies the response
formats for a specific action (or controller). Like most Filters, this
can be applied at the action, controller, or global scope.
[Produces("application/json")]
public class AuthorsController
The [Produces] filter will force all actions within the
AuthorsController to return JSON-formatted responses, even if other
formatters were configured for the application and the client provided
an Accept header requesting a different, available format.
Way 2
My preferred method is for the WebAPI to respond to all requests with the format requested. However, in the event that it doesn't accept the requested format, then fall-back to a default (ie. JSON)
First, you'll need to register that in your options (we need to rework the default behavior, as noted earlier)
options.RespectBrowserAcceptHeader = true; // false by default
Finally, by simply re-ordering the list of the formatters that were defined in the services builder, the web host will default to the formatter you position at the top of the list (ie position 0).
More information can be found in this .NET Web Development and Tools Blog entry
You have predefined methods for most common status codes.
Ok(result) returns 200 with response
CreatedAtRoute returns 201 + new resource URL
NotFound returns 404
BadRequest returns 400 etc.
See BaseController.cs and Controller.cs for a list of all methods.
But if you really insist you can use StatusCode to set a custom code, but you really shouldn't as it makes code less readable and you'll have to repeat code to set headers (like for CreatedAtRoute).
public ActionResult IsAuthenticated()
{
return StatusCode(200, "123");
}
With ASP.NET Core 2.0, the ideal way to return object from Web API (which is unified with MVC and uses same base class Controller) is
public IActionResult Get()
{
return new OkObjectResult(new Item { Id = 123, Name = "Hero" });
}
Notice that
It returns with 200 OK status code (it's an Ok type of ObjectResult)
It does content negotiation, i.e. it'll return based on Accept header in request. If Accept: application/xml is sent in request, it'll return as XML. If nothing is sent, JSON is default.
If it needs to send with specific status code, use ObjectResult or StatusCode instead. Both does the same thing, and supports content negotiation.
return new ObjectResult(new Item { Id = 123, Name = "Hero" }) { StatusCode = 200 };
return StatusCode( 200, new Item { Id = 123, Name = "Hero" });
or even more fine grained with ObjectResult:
Microsoft.AspNetCore.Mvc.Formatters.MediaTypeCollection myContentTypes = new Microsoft.AspNetCore.Mvc.Formatters.MediaTypeCollection { System.Net.Mime.MediaTypeNames.Application.Json };
String hardCodedJson = "{\"Id\":\"123\",\"DateOfRegistration\":\"2012-10-21T00:00:00+05:30\",\"Status\":0}";
return new ObjectResult(hardCodedJson) { StatusCode = 200, ContentTypes = myContentTypes };
If you specifically want to return as JSON, there are couple of ways
//GET http://example.com/api/test/asjson
[HttpGet("AsJson")]
public JsonResult GetAsJson()
{
return Json(new Item { Id = 123, Name = "Hero" });
}
//GET http://example.com/api/test/withproduces
[HttpGet("WithProduces")]
[Produces("application/json")]
public Item GetWithProduces()
{
return new Item { Id = 123, Name = "Hero" };
}
Notice that
Both enforces JSON in two different ways.
Both ignores content negotiation.
First method enforces JSON with specific serializer Json(object).
Second method does the same by using Produces() attribute (which is a ResultFilter) with contentType = application/json
Read more about them in the official docs. Learn about filters here.
The simple model class that is used in the samples
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
The easiest way I came up with is :
var result = new Item { Id = 123, Name = "Hero" };
return new JsonResult(result)
{
StatusCode = StatusCodes.Status201Created // Status code here
};
This is my easiest solution:
public IActionResult InfoTag()
{
return Ok(new {name = "Fabio", age = 42, gender = "M"});
}
or
public IActionResult InfoTag()
{
return Json(new {name = "Fabio", age = 42, gender = "M"});
}
Awesome answers I found here and I also tried this return statement see StatusCode(whatever code you wish) and it worked!!!
return Ok(new {
Token = new JwtSecurityTokenHandler().WriteToken(token),
Expiration = token.ValidTo,
username = user.FullName,
StatusCode = StatusCode(200)
});
Instead of using 404/201 status codes using enum
public async Task<IActionResult> Login(string email, string password)
{
if (string.IsNullOrWhiteSpace(email) || string.IsNullOrWhiteSpace(password))
{
return StatusCode((int)HttpStatusCode.BadRequest, Json("email or password is null"));
}
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
return StatusCode((int)HttpStatusCode.BadRequest, Json("Invalid Login and/or password"));
}
var passwordSignInResult = await _signInManager.PasswordSignInAsync(user, password, isPersistent: true, lockoutOnFailure: false);
if (!passwordSignInResult.Succeeded)
{
return StatusCode((int)HttpStatusCode.BadRequest, Json("Invalid Login and/or password"));
}
return StatusCode((int)HttpStatusCode.OK, Json("Sucess !!!"));
}
Controller action return types in ASP.NET Core web API
02/03/2020
6 minutes to read
+2
By Scott Addie Link
Synchronous action
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById(int id)
{
if (!_repository.TryGetProduct(id, out var product))
{
return NotFound();
}
return product;
}
Asynchronous action
[HttpPost]
[Consumes(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
await _repository.AddProductAsync(product);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
Please refer below code, You can manage multiple status code with different type JSON
public async Task<HttpResponseMessage> GetAsync()
{
try
{
using (var entities = new DbEntities())
{
var resourceModelList = entities.Resources.Select(r=> new ResourceModel{Build Your Resource Model}).ToList();
if (resourceModelList.Count == 0)
{
return this.Request.CreateResponse<string>(HttpStatusCode.NotFound, "No resources found.");
}
return this.Request.CreateResponse<List<ResourceModel>>(HttpStatusCode.OK, resourceModelList, "application/json");
}
}
catch (Exception ex)
{
return this.Request.CreateResponse<string>(HttpStatusCode.InternalServerError, "Something went wrong.");
}
}
What I do in my Asp Net Core Api applications it is to create a class that extends from ObjectResult and provide many constructors to customize the content and the status code.
Then all my Controller actions use one of the costructors as appropiate.
You can take a look at my implementation at:
https://github.com/melardev/AspNetCoreApiPaginatedCrud
and
https://github.com/melardev/ApiAspCoreEcommerce
here is how the class looks like(go to my repo for full code):
public class StatusCodeAndDtoWrapper : ObjectResult
{
public StatusCodeAndDtoWrapper(AppResponse dto, int statusCode = 200) : base(dto)
{
StatusCode = statusCode;
}
private StatusCodeAndDtoWrapper(AppResponse dto, int statusCode, string message) : base(dto)
{
StatusCode = statusCode;
if (dto.FullMessages == null)
dto.FullMessages = new List<string>(1);
dto.FullMessages.Add(message);
}
private StatusCodeAndDtoWrapper(AppResponse dto, int statusCode, ICollection<string> messages) : base(dto)
{
StatusCode = statusCode;
dto.FullMessages = messages;
}
}
Notice the base(dto) you replace dto by your object and you should be good to go.
I got this to work. My big issue was my json was a string (in my database...and not a specific/known Type).
Ok, I finally got this to work.
////[Route("api/[controller]")]
////[ApiController]
////public class MyController: Microsoft.AspNetCore.Mvc.ControllerBase
////{
//// public IActionResult MyMethod(string myParam) {
string hardCodedJson = "{}";
int hardCodedStatusCode = 200;
Newtonsoft.Json.Linq.JObject job = Newtonsoft.Json.Linq.JObject.Parse(hardCodedJson);
/* "this" comes from your class being a subclass of Microsoft.AspNetCore.Mvc.ControllerBase */
Microsoft.AspNetCore.Mvc.ContentResult contRes = this.Content(job.ToString());
contRes.StatusCode = hardCodedStatusCode;
return contRes;
//// } ////end MyMethod
//// } ////end class
I happen to be on asp.net core 3.1
#region Assembly Microsoft.AspNetCore.Mvc.Core, Version=3.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
//C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\3.1.0\ref\netcoreapp3.1\Microsoft.AspNetCore.Mvc.Core.dll
I got the hint from here :: https://www.jianshu.com/p/7b3e92c42b61
The cleanest solution I have found is to set the following in my ConfigureServices method in Startup.cs (In my case I want the TZ info stripped. I always want to see the date time as the user saw it).
services.AddControllers()
.AddNewtonsoftJson(o =>
{
o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Unspecified;
});
The DateTimeZoneHandling options are Utc, Unspecified, Local or RoundtripKind
I would still like to find a way to be able to request this on a per-call bases.
something like
static readonly JsonMediaTypeFormatter _jsonFormatter = new JsonMediaTypeFormatter();
_jsonFormatter.SerializerSettings = new JsonSerializerSettings()
{DateTimeZoneHandling = DateTimeZoneHandling.Unspecified};
return Ok("Hello World", _jsonFormatter );
I am converting from ASP.NET and there I used the following helper method
public static ActionResult<T> Ok<T>(T result, HttpContext context)
{
var responseMessage = context.GetHttpRequestMessage().CreateResponse(HttpStatusCode.OK, result, _jsonFormatter);
return new ResponseMessageResult(responseMessage);
}

ASP.net API get call returns controller class, not data

I've created an API in ASP.net. My Sencha SPA is attempting to get data from it. Unfortunately the get call is returning the controller class rather than the data I want. Any ideas?
Here is the get method:
private firstAPIContext db = new firstAPIContext();
// GET: api/AccountModels
public IQueryable<Account> GetAccountModels()
{
return db.Accounts;
}
// GET: api/AccountModels/5
[ResponseType(typeof(Account))]
public async Task<IHttpActionResult> GetAccountModel(string username)
{
Account accountModel = await db.Accounts.FindAsync(username);
if (accountModel == null)
{
return NotFound();
}
return Ok(accountModel);
}
Here is the rest proxy code from Sencha:
Ext.define('MyApp.store.AccountsStore', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.AccountModel',
'Ext.data.proxy.Rest'
],
constructor: function(cfg) {
var me = this;
cfg = cfg || {};
me.callParent([Ext.apply({
storeId: 'AccountsStore',
model: 'MyApp.model.AccountModel',
proxy: {
type: 'rest',
url: '/Controllers/AccountsController.cs'
}
}, cfg)]);
}
});
Please make sure that you have added a JSON Reader on your Sencha Store.So that you can read the response send by the server.Also make sure that you are sending JSON data back from the server for this add the following lines in you Global.axpx
var config=GlobalConfiguration.Configuration;
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
And also change the url to like this
url: '/Controllers/AccountsController/'
This seems to be the main reason for me because you have added an extension
.cs thats why when u call the method it returns the whole controller class instead of the data.Try to remove this extension too and do all what i have suggest.Hope it will works fine

Angular on top of ASP.NET MVC, displaying Errors

We are putting an angular front end on an existing asp.net c# MVC applicaiton. In our server code, we extensilvely use custom exceptions to return buisness rule errors.
Is there a best practice or slickest way to handle an exception on an mvc controller or an webApi controller (actually bullbing up from the buisness layer) and getting it across to angular and displaying it in a "user error" popup? How are folks solving this problem?
Other guys already gave great answers, but I want to elaborate my approach since I guess it will be covering both ends (frontend and server) with more details.
Here's my complete approach to error and exception handling in WebAPI + AngularJS applications.
Step 1. WebApi controllers in Server side
I have a specific DTO for communicating Validation Errors to the client, since I believe they are different from Exceptions. An exception will result in a 500 error, where a validation result should result in 400 (Bad Request) error.
So, here's my ApiValidationResult class:
public class ApiValidationResult
{
public List<ApiValidationError> Errors { get; set; }
public static ApiValidationResult Failure(string errorKey)
{
return new ApiValidationResult {Errors = new List<ApiValidationError> {new ApiValidationError(errorKey)}};
}
// You can add a bunch of utility methods here
}
public class ApiValidationError
{
public ApiValidationError()
{
}
public ApiValidationError(string errorKey)
{
ErrorKey = errorKey;
}
// More utility constructors here
public string PropertyPath { get; set; }
public string ErrorKey { get; set; }
public List<string> ErrorParameters { get; set; }
}
I always use my own base class for WebApi (and MVC) controllers, so I can use them to add handy result method, such as this:
public abstract class ExtendedApiController : ApiController
{
protected IHttpActionResult ValidationError(string error)
{
return new ValidationErrorResult(ApiValidationResult.Failure(error), this);
}
// More utility methods can go here
}
It uses a custom IHttpActionResult that I've created specifically for this purpose:
public class ValidationErrorResult : NegotiatedContentResult<ApiValidationResult>
{
public ValidationErrorResult(ApiValidationResult content, IContentNegotiator contentNegotiator, HttpRequestMessage request, IEnumerable<MediaTypeFormatter> formatters)
: base(HttpStatusCode.BadRequest, content, contentNegotiator, request, formatters)
{
}
public ValidationErrorResult(ApiValidationResult content, ApiController controller)
: base(HttpStatusCode.BadRequest, content, controller)
{
}
}
As a result, I can cleanly use codes such as this in my controller actions:
[HttpPost]
public IHttpActionResult SomeAction(SomeInput input)
{
// Do whatever...
if (resultIsValid)
{
return Ok(outputObject);
}
return ValidationResult(errorMessage);
}
Step 2. Handling unexpected exceptions
As I said, I believe that only real unhandled Exceptions should result in a 500 (Internal server error) responses.
Such unhandled exceptions are automatically converted to a 500 result by WebApi. The only thing I need to do about them, is to log them. So, I create an implementation of IExceptionLogger interface and register it like this:
GlobalConfiguration.Configuration.Services.Add(typeof(IExceptionLogger), new UnhandledExceptionLogger());
Step 3. Intercepting and showing errors in Client side
AngularJS allows intercepting all HTTP calls sent from $http service. I use this to centralize all message popups. Here's my interceptor code:
appModule.factory("errorsHttpInterceptor", [
"$q", "$rootScope", "$injector",
($q: ng.IQService, $rootScope: IAppRootScopeService, $injector) => {
return {
'responseError': rejection => {
// Maybe put the error in $rootScope and show it in UI
// Maybe use a popup
// Maybe use a 'toast'
var toastr = $injector.get('toastr');
toastr.error(...);
return $q.reject(rejection);
}
};
}
]);
You can do all sorts of things in the interceptor, such as logging debug messages, or applying key to display-string translation of error codes. You can also distinguish between 500 and 400 errors, and display different types of error messages.
I use toastr library which I think shows a nice UI and is very handy in API level.
Finally, I register the interceptor like this:
appModule.config([
'$httpProvider',
($httpProvider: ng.IHttpProvider) => {
$httpProvider.interceptors.push('errorsHttpInterceptor');
}
]);
The syntax is in TypeScript, which is very similar to JavaScript and I'm sure you can figure out what it means.
Typically, I Have been doing this kind of thing within our WEB API, returning the correct status code is key with this, this is of course completely agnostic as to which of the many front end frameworks you want to use.
public IHttpActionResult Get(DateTime? updatesAfter = null)
{
try
{
// Do something here.
return this.Ok(result);
}
catch (Exception ex) // Be more specific if you like...
{
return this.InternalServerError(ex);
throw;
}
}
The helper methods that are now shipped with Web Api v2 ApiControllers are excellent...
this.BadRequest()
this.InternalServerError()
this.Ok()
this.Unauthorized()
this.StatusCode()
this.NotFound()
Some of them (such as InternalServerError) allow you to pass an exception or message (or simply an object) as a param.
Typically as with any front end framework or library there will be a fail or error callback that you can provide when initialising the ajax call to your API method, this will be called in scenarios where error status codes are returned.
We have just finished a large MVC app with an Angular frontend.
We just let errors flow, eg any webpages just get an error has but without the stack trace (not the yellow screen, the nice Error has *** but within your master page etc)
Web API calls just return the correct HTTP status. They can include some details if you wish.
But, you dont want to lose these errors, so we just installed elmah with
Install-Package elmah
and, it JUST WORKS, errors just end up in the log, users get told something as not worked etc.. NO extra work needed
To make our UI nicer we did the following, for
Unhandled errors
MVC Pages
Just let MVC front end do its job, it will tell the user in a nice way something has gone wrong.
Angular web calls
in the .error function alert the user eg
}).error(function (data, status, headers, config) {
$scope.addAlert('danger', 'Error deleting autosave details');
}
Let errors stack, if you don't to lose an error due to an error overwriting it. addAlert just writes to an array that is data bound to on the front end.
Handled Errors
If you are handling them, then you have managed what happens, but to log these
MVC
If you wish to just log them, the elmah API has a single call for this
ErrorSignal.FromCurrentContext().Raise(exception );
If you are using error attributes then you can use this
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new ElmahHandledErrorLoggerFilter());
filters.Add(new HandleErrorAttribute());
}
}
public class ElmahHandledErrorLoggerFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
// Log only handled exceptions, because all other will be caught by ELMAH anyway.
if (context.ExceptionHandled)
ErrorSignal.FromCurrentContext().Raise(context.Exception);
}
}
Also, for general logging to the ELMAH framework checkout https://github.com/TrueNorthIT/Elmah
Another approach is returning Content.
Here is how I do it (end-to-end). From my API:
return Content(HttpStatusCode.<statuscode>, "ResponseContent: " + "my_custom_error");
Here, HttpStatusCode. and "my_custom_error" can be the response returned from another API Layer. In that case, I simply read the response from that layer and pass it to the client side.
//If I'm getting output from another API/Layer then I pass it's output like this
var output = response.Content.ReadAsStringAsync().Result;
return Content(response.StatusCode, "ResponseContent: " + output);
For more details on HttpStatusCodes, please refer HttpStatusCode Enumeration
And in the Angular code, I read it like this:
$http({ method: methodType, url: endpoint })
.then(function (response) {
response.status; //gets you the HttpStatusCode to play with
response.data; //gets you the ReponseContent section
}, function (response) {
response.status; //gets you the HttpStatusCode
response.data; //gets you the ReponseContent section
});
Make sure while making http calls the responseType is not set to 'JSON'. Since the data returned by the API is not in JSON format at this stage.
From $http service in AngularJS, the response object has these properties:
data – {string|Object} – The response body transformed with the
transform functions.
status – {number} – HTTP status code of the response.
headers – {function([headerName])} – Header getter function.
config – {Object} – The configuration object that was used to
generate the request.
statusText – {string} – HTTP status text of the response.
For my API Controllers I return HttpResponseMessage. Please see my example below. I hope this helps.
On the response message you can also pass back your object to the front end.
WEB API
// Get all
[HttpGet]
public HttpResponseMessage Get()
{
try
{
return Request.CreateResponse(HttpStatusCode.OK, myObject);
}
catch (Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Error Message");
}
}
Angular JS
// Get Request on Page Load
$http({
url: '/api/department/',
method: 'GET',
}).success(function (data) {
$scope.departments = data;
}).error(function (error) {
$scope.error = error;
});
HTML
<div class="alert alert-danger animate-show" role="alert" ng-show="error != null">{{ error }} </div>
<div class="alert alert-success animate-show" role="alert" ng-show="success != null">{{ success }}</div>

DNN WebAPI POST always returns a 404 Not Found Error

I have no problem with GET requests when I try the WebAPI method of doing services in DNN. However, I can't for the life of me get a POST request to return anything but a HTTP 404 Not Found error. Both the GET and POST methods are in the same controller.
I reviewed similar questions and confirmed that my folder names are standard, IIS is not running application folders, and I don't have an additional web.config.
I also installed dnnGlimpse and verified that my route was registered.
This is DNN 7.3.4 and a clean project - it's not from a VS template.
This is an example of my route mapper.
public class RouteMapper : IServiceRouteMapper
{
public void RegisterRoutes(IMapRoute mapRouteManager)
{
mapRouteManager.MapHttpRoute("MyModule", "default", "{controller}/{action}", new[] { "MyCompany.Modules.MyModule.Controllers" });
}
}
This is an example of my post method. I can't even hit a breakpoint anywhere in this method. This class is in a folder named "Controllers."
namespace MyCompany.Modules.MyModule.Controllers
{
public class ExampleController : DnnApiController
{
[AllowAnonymous]
[ValidateAntiForgeryToken]
[HttpPost]
public HttpResponseMessage CustomObjectType(string value)
{
try
{
var oType = CustomObjectTypeHelper.GetObjectType(value);
switch (oType)
{
case CustomObjectType.Type1:
return Request.CreateResponse(HttpStatusCode.OK, "type1");
case CustomObjectType.Type2:
return Request.CreateResponse(HttpStatusCode.OK, "type2");
case CustomObjectType.Type3:
return Request.CreateResponse(HttpStatusCode.OK, "type3");
case CustomObjectType.Type4:
return Request.CreateResponse(HttpStatusCode.OK, "type4");
default:
throw new ArgumentOutOfRangeException("value", "Value format does not match a supported custom object type.");
}
}
catch(Exception ex)
{
Exceptions.LogException(ex);
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError,
"Card number format does not match a supported card type.");
}
}
}
}
Here is an example of how I have called it.
var sf = $.ServicesFramework(<%=ModuleId %>);
var serviceUrl = sf.getServiceRoot('MyModule');
function getObjectType() {
var strNumber = $('<%=txtNumber.ClientID %>').val();
try
{
$.ajax({
type: "POST",
cache: false,
url: serviceUrl + "Example/CustomObjectType",
beforeSend: sf.setModuleHeaders,
data: strNumber
}).success(function(data, textStatus, jqXHR) {
alert(data);
}).fail(function (xhr, result, status) {
alert("Uh-oh, something broke: " + status);
});
} catch (e) {
//Shouldn't do this but it's just for testing
alert(e.stack);
}
}
Actually, this ended up being the issue where WebAPI doesn't like to use simple values with POST. Once I changed this to expect a view model, the method is found.
The answer is in the question below:
Simple controller which takes POST is not found

Categories