I have an Index page that allows users to see a list of Stocks. Users can add stocks to their watchlist by clicking an Add to Watchlist button. This leads to the Watchlist page, sending the User's ID and the stock they want to add to their watchlist so their watchlist can be persisted any time they log into their account.
I need the method to be HTTPPost because I don't want the user's ID as part of the query string, as this is sensitive info. The Watchlist action method serves the purpose of inserting the stock information and the user's ID as a record in a Watchlist table and also display the user's watchlist for them to see.
This is where the problem comes in. I keep getting an HTTP 405 Method Not Allowed error. I've checked my syntax in code and everything seems fine. I have a feeling it might have to do with passing two values (stock Ticker and user ID) instead of one.
I'll display relevant code in the order of operation.
Index.cshtml
@using Microsoft.AspNetCore.Identity
@model StockView.Models.StockExchangeViewModel
@inject UserManager<GenericUser> UserManager
@{
ViewData["Title"] = "Index";
var user = await UserManager.GetUserAsync(User);
var userID = await UserManager.GetUserIdAsync(user);
string UserId = (string)userID;
}
<h1>Stocks</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<form asp-controller="Stocks" asp-action="Index" method="get">
<p>
<select asp-for="StockExchange" asp-items="Model.Exchanges">
<option value="">All</option>
</select>
Title: <input type="text" name="SearchString" />
<input type="submit" value="Filter" />
</p>
</form>
<table >
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Stocks[0].Ticker)
</th>
<th>
@Html.DisplayNameFor(model => model.Stocks[0].Price)
</th>
<th>
@Html.DisplayNameFor(model => model.Stocks[0].Volume)
</th>
<th>
@Html.DisplayNameFor(model => model.Stocks[0].MarketCap)
</th>
<th>
@Html.DisplayNameFor(model => model.Stocks[0].Exchange)
</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Stocks)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Ticker)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.Volume)
</td>
<td>
@Html.DisplayFor(modelItem => item.MarketCap)
</td>
<td>
@Html.DisplayFor(modelItem => item.Exchange)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Ticker">Edit</a> |
<a asp-action="Details" asp-route-id="@item.Ticker">Details</a> |
<a asp-action="Delete" asp-route-id="@item.Ticker">Delete</a> |
<a href="@Url.Action("Watchlist", "Stocks", new { stockTicker = item.Ticker, userId = UserId})">Add To Watchlist</a>
<a href="@Url.Action("Chart", "Stocks", new {stockTicker = item.Ticker})">View Chart</a>
</td>
</tr>
}
</tbody>
</table>
StocksController.cs
//This method is meant to post into a Watchlist object which is persisted by the database
[HttpPost]
public async Task<IActionResult> Watchlist(string stockTicker, string userId)
{
try
{
var stock = from s in _context.Stocks
where s.Ticker == stockTicker
select new { s.Ticker, s.Price, s.Exchange };
Stocks tempStock = (Stocks)stock;
//Insert into watchlist using stock attributes: UserId, Ticker, Price, Exchange
await _context.Watchlists.AddAsync(new Watchlist
{
GenericUserId = userId,
Ticker = tempStock.Ticker,
Price = tempStock.Price,
Exchange = tempStock.Exchange
});
_context.SaveChanges();
var stocks = from s in _context.Watchlists
where s.GenericUserId == userId
select new { s.Ticker, s.Price, s.Exchange };
ViewBag.stocks = stocks;
return View();
}
catch (Exception ex)
{
return View("Error",
new ErrorViewModel
{
RequestId = ex.ToString(),
Description = "Error." ex.Message
});
}
}
I've tried changing the HTTP Attributes but I doubt that is the root of the problem, I've also tried to see if the action method performs any of the logic, and unfortunately, nothing is being persisted into the database at this time.
Everything seems to make sense, I'm not sure what I could be missing.
I can also provide any extra code that might paint a clearer picture.
CodePudding user response:
Just change the value of method
attribute of your form element as post
:
<form asp-controller="Stocks" asp-action="Index" method="post">
CodePudding user response:
The Anchor Tag can only send HttpGet
request and your Watchlist
action can only receive HttpPost request, This is the reason why you get HTTP Status Code 405 - Method Not Allowed
.
You can just remove [HttpPost]
attribute to let Watchlist
action receive Http Get request. Because your Anchor Tag will generate url like xxx/Stocks/Watchlist?stockTicker=xxxx&userId=xxxx
, If Watchlist
is Http Get method, It's parameter will bind value from query string. But this method may not fit your requirement because it will show userId in query string.
Another method is send Http Post request to access Watchlist
action. Because Anchor Tag can't send Post request, You can add some css style to make button look like a Anchor Tag.
.inline {
display: inline;
}
.link-button {
background: none;
border: none;
color: blue;
text-decoration: underline;
cursor: pointer;
font-size: 1em;
font-family: serif;
}
.link-button:focus {
outline: none;
}
.link-button:active {
color: red;
}
Then change your Add To Watchlist
Anchor Tag to:
<button type="button" id="Edit" value="item.Ticker" data-value="userId" onclick="Watchlist(this)">
Add To Watchlist
</button>
Then write a JavaScript code to send post in ajax.
<script>
function Watchlist(button) {
var result1 = button.value;
var result2 = button.getAttribute("data-value");
var formdata = new FormData();
formdata.append("stockTicker",result1);
formdata.append("userId", result2);
$.ajax({
type: 'POST',
url: '/Stocks/Watchlist',
data: formdata,
processData: false,
contentType: false
});
}
</script>