Web Api Controller Method
[Route("change-gross-profit")]
[HttpPost]
public IHttpActionResult UpdateGrossProfit(decimal grossProfit)
{
var customerGrossProfit = grossProfit / 100;
Context.UpdateCustomerCost();
return Ok();
}
Angular Http Call
public updateGrossProfit(grossProfit: number): Observable<void> {
const url = UrlTemplate.populate('api/customer/total', 'change-gross-profit', {});
return this._http.post<void>(url, grossProfit ,{withCredentials: true});
}
I am getting the 404 Method Not Found error.
I have also tried by passing data using JSON.stringify({'grossProfit' : grossProfit}) (Getting un-supported content-type error.)
Currently workaround is, I can create a class with required property and then pass that class instance as a body which I know that it will work. But I would like to avoid it as there is only one parameter that I want to pass into POST controller method so.
Any idea how can I pass the single parameter as an argument to Post controller method from Angular without creating a custom object?
You are passing the number just fine.
I tried the same with this code below and it works fine.
let no : number =5;
this.http.post<any>("https://xxxxx/postDecimal", no).subscribe(result => {
console.log(result);
});
This is my backend code:
[HttpPost("postDecimal")]
[ProducesResponseType(typeof(int), 200)]
[ProducesResponseType(typeof(string), 400)]
[ProducesResponseType(typeof(string), 403)]
public async Task<IActionResult> PostDecimal(decimal x)
{
return Ok(x);
}
Is your url fine? Since you got 404 Method not found error.
Related
I would like to filter a list of vehicles, by their makeId using httpGet. The URL I would expect to use is:
https://localhost:5001/api/vehicle?makeId=2
Below, I will define the DTO and controller methods I used for this task:
FilterDto
public class FilterDTO
{
public int? MakeId { get; set; }
}
Below are the 2 HTTPGet methods in my controller class. I expect the first method to be called.
[HttpGet]
public async Task<IEnumerable<VehicleDTO>> Get(FilterDTO filterDto)
{
var filter = _mapper.Map<Filter>(filterDto);
var vehicles = await _vehicleRepository.GetAll(filter);
return _mapper.Map<IEnumerable<VehicleDTO>>(vehicles);
}
[HttpGet("{id}")]
public async Task<ActionResult<VehicleDTO>> Get(long id)
{
var vehicle = await _vehicleRepository.GetWithRelated(id);
if (vehicle == default)
{
return BadRequest("Vehicle not found");
}
var result = _mapper.Map<VehicleDTO>(vehicle);
return Ok(result);
}
With the above code, when I call the URL above, in Postman I get a 400 Error, saying "The input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0."
I get the same result for https://localhost:5001/api/vehicle
If I change the first Get method like below, I am able to get a response:
[HttpGet]
public async Task<IEnumerable<VehicleDTO>> Get(int? makeId)
{
var filter = new Filter { MakeId = makeId};
var vehicles = await _vehicleRepository.GetAll(filter);
return _mapper.Map<IEnumerable<VehicleDTO>>(vehicles);
}
After this (lengthy) introduction, my questions are:
Why does HttpGet support 'int?' but not the data transfer object 'FilterDto'?
Should I be using a different verb instead of HttpGet?
I might have to filter in the future for some other types (say customerId). Is there any way I can change the method to support custom objects, like FilterDto, ideally without changing the verb?
Change your code as follow:
[HttpGet]
public async Task<IEnumerable<VehicleDTO>> Get([FromQuery] FilterDTO filterDto)
{
var filter = _mapper.Map<Filter>(filterDto);
var vehicles = await _vehicleRepository.GetAll(filter);
return _mapper.Map<IEnumerable<VehicleDTO>>(vehicles);
}
and call it like:
baseUrl/Controller/Get?MarkId=1
Take a look at the docs.
Basically the primitive types are supported, but the controller has no idea how to convert your web request data into C# object. You need to explicitly tell it how you want this custom object to be created out of web request.
You may have in mind that HttpGet methods are only able to receive primitiveTypes (string, int, short, datetime -using a specific format-) because the arguments are being sent through query string, for example:
myAddres.com/api/mymethod?id=5&filter1=value1&filter2=value2
Having this consideration in mind you'll notice there's no way to send any object because you need to use a json or another notation, remember querystring has a limit and because of that is better using "argument=value" notation.
On the other hand PUT and POST are able to send their data through a "body" property where you may use a json notation and this way you may create almost any object on your Backend side.
If you need to use an object as an argument it is a better idea using POST or PUT (better POST than PUT).
I was searching around but I couldn't find a working answer for my issue. I saw a similar question but we had different results. My issue is I have 2 controllers. The first controller has a POST action that I want to return a CreatedAtRoute to a GET action inside a different controller. I tested the GET action in Postman and it works so my only issue is getting the CreatedAtRoute to work.
My first controller:
[HttpPost]
public async Task<IActionResult> Submit(AssessmentAttemptVM attempt)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
//Do database related stuff
await _context.SaveChangesAsync();
return CreatedAtRoute("GetAssessmentResult", new { id = studentAttempt.Id }, studentAttempt);
}
My second controller:
[HttpGet("{id}", Name = "GetAssessmentResult")]
public async Task<ActionResult<AssessmentResultVM>> GetById(int id)
{
//Get my ViewModel -- This works if I try to access it without using the CreatedAtRoute method
return resultVM;
}
The picture below shows what Postman responds with when I try to Post. I verified that the data gets added to the database so only the CreatedAtRoute method is the only I can think of that isn't making this work for me..
EDIT
Controller Route Attributes:
[ApiController]
[Route("api/view/assessmentresult/")]
public class AssessmentResultsController: ControllerBase
{
[ApiController]
[Route("api/take/assessment")]
public class StudentAssessmentController : ControllerBase
{
I found the cause. The last parameter for CreatedAtRoute and CreatedAtAction required an object similar to the destination controller. It went over my head because I was sending models prior to what I did now which used a ViewModel.
Well That wasn't the main reason I couldn't get a response though. It was because of an execption where the object I'm passing ended up having recursive references because I was sending a model that wasn't properly formatted to be sent over as JSON.
I used this to make it work, taken from the MS Docs site:
CreatedAtAction(String, String, Object, Object) Where the last parameter should be the object you want to the api to send over.
PS: I also didn't notice immediately because when I debugged the project, it didn't crash and had to read the logs. I'm a noob so I really didn't know that it's possible for an exception to occur without the project crashing in debug mode.
Can i send to the action method new record and then get the new id by the same method
public class HomeController : Controller
{
[HttpGet]
[HttpPost]
public JsonResult _sendConfirmation'(string subject,string mail)
{
Some Code--Some Code---Some Code
return Json(new { Success = true, id = newCreatedMailId });
}
}
Getting the id by jquery
$.getJSON('/Mails/_sendConfirmation', function (comingData) {
alert("success" + data);
jQuery.get('/Mails/_getNewMailSendConfirmation', { id: comingData }, function (data) {
jQuery('#myModal').modal('show');
jQuery('#myModal .modal-body').html(data);
});
enter code here
So your Ajax request is looking for an action named _sendConfirmation on your Mails controller and not finding it but you've chose. To just show us your home controller index action so we have no idea if the appropriate controller exists or not with that.
As far as having it decorated with a get and a post, when you do this you are essentially telling the action to look for data on the request in two different places which will most likely not end well for you unless you plan on doing that switch logic yourself inside the action. Your code would probably be a bit less error prone if you seperated the action and once extracted the data, you call the same helper methods to work your data.
I'm working with MVC 4 Web API and I have this dummy ValueProvider:
DummyValueProvider.cs
class DummyValueProvider : IValueProvider
{
public DummyValueProvider()
{
}
public bool ContainsPrefix(string prefix)
{
return true;
}
public ValueProviderResult GetValue(string key)
{
return new ValueProviderResult("testing", "testing", System.Globalization.CultureInfo.InvariantCulture);
}
}
class DummyValueProviderFactory : System.Web.Http.ValueProviders.ValueProviderFactory
{
public override IValueProvider GetValueProvider(System.Web.Http.Controllers.HttpActionContext actionContext)
{
return new DummyValueProvider();
}
}
This ValueProvider should return true for any key asked, so it will always supply a value to the model binder when it needs. The ValueProvider is registered in the WebApiConfig like this:
WebApiConfig.cs
config.Services.Add(typeof(ValueProviderFactory), new DummyValueProviderFactory());
The code compiles and runs fine.
I also have this action in the Account API controller:
AccountController.cs
public HttpResponseMessage Register(string foo) { ... }
The action gets called fine when I call it like below:
/register?foo=bar
And foo is filled with bar as expected; but if I call:
/register
The server returns 404 with the message No HTTP resource was found that matches the request URI 'http://localhost:14459/register'.
Also, I put breakpoints inside methods ContainsPrefix() and GetValue(), but they never get triggered.
What am I doing wrong? Shouldn't DummyValueProvider be providing the value testing to parameter foo?
Try this
public HttpResponseMessage Get([ValueProvider(typeof(DummyValueProviderFactory))] string foo) {... }
I higly suggest you to read this recent article to customize Web Api Binding.
Update:
After reading the article the OP was able to discover the solution. It was that using the parameter attribute [ModelBinder] was required for it to work. This was because unless the parameter is annotated, [FromUri] is assumed. Once annotated with [ModelBinder] the registered handlers are executed.
I am trying to integrate AngularJS with Spring MVC; but I am not able to post parameters to spring controller as RequestBody. Can some one help me to achieve the same. Below is brief flow of my program.
After doing data entry TodoNewController gets executed. From here I am calling user-defined method "create" which I have defined in services.js. As per the flow after this it should call create method of TodoController.java along with input params; but it is not happening. Can some one let me know what is wrong with the code. Below is the code for same.
controller.js
function TodoNewController($scope, $location, Todo) {
$scope.submit = function () {
Todo.create($scope.todo, function (todo) {
$location.path('/');
});
};
$scope.gotoTodoListPage = function () {
$location.path("/")
};
}
services.js
angular.module('todoService', ['ngResource']).
factory('Todo', function ($resource) {
return $resource('rest/todo/:id', {}, {
'create': {method:'PUT'}
});
});
TodoController.java
#Controller
public class TodoController {
private static final AtomicLong todoIdGenerator = new AtomicLong(0);
private static final ConcurrentSkipListMap<Long, Todo> todoRepository = new ConcurrentSkipListMap<Long, Todo>();
#RequestMapping(value = "/todo", method = RequestMethod.PUT)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void create(#RequestBody Todo todo) {
long id = todoIdGenerator.incrementAndGet();
todo.setId(id);
todoRepository.put(id, todo);
}
}
Spring expects application/x-www-form-urlencoded as the Content-Type of the request. You may try inject $http into your service and invoke $http.defaults.headers.put["Content-Type"] = "application/x-www-form-urlencoded"; at the beginning of it.
Modify the request mapping to match the actual mapping /rest/todo and change the databinding to use #ModelAttribute.
#RequestMapping(value = "/rest/todo", method = RequestMethod.PUT)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void create(#ModelAttribute Todo todo) {
long id = todoIdGenerator.incrementAndGet();
todo.setId(id);
todoRepository.put(id, todo);
}
Isolate the problem first. Is it Spring or Angular that's causing the issue? I suggest you install a Rest client plugin either in Chrome or FireFox. Then create a PUT request and enter the correct endpoint URL. If you're able to receive the correct response, then it means your Angular request is constructed incorrectly.
Now, run your Angular-based client. Make a PUT request. Inspect the parameters and request sent (in Chrome, you can use Developer tools) and see if it matches the request you sent earlier. If it does, then it should work. If not, then you know the problem.
Also, your Angular resource:
$resource('rest/todo/:id')
has a different URL than what you have in your Spring controller
#RequestMapping(value = "/todo", method = RequestMethod.PUT)
So the first one is like 'rest/todo/1' and the latter is '/todo'. I don't think those would match.