[HttpPost]
public ActionResult accountchange(int id, int accountid, bool activate)
{
// Operations
return Json(new { Model = model, Result = true, Message = "Changed Successfully });
}
$('#accountchange-button').click(function () {
alert("");
$.post("url/accountchange/", {id:1, accountid:1, activate:1},
function (data) { $('.result').html(data); }, "json");
});
always got:
POST http://localhost/account/accountchange/ 500 (Internal Server Error)
f.support.ajax.f.ajaxTransport.sendjquery.min.js:4
f.extend.ajaxjquery.min.js:4
f.each.f.(anonymous function)jquery.min.js:4
(anonymous function)demo:105
f.event.dispatchjquery.min.js:3
f.event.add.h.handle.i
any idea?
In your code, I don't see model being defined. That's my best guess as to why your application is returning a 500 application error. As previously mentioned in the comment, you can use the inspect element utility to figure out what the server is actually complaining about.
[HttpPost]
public ActionResult accountchange(int id, int accountid, bool activate)
{
// Model = model is most likely undefined
return Json(new { Model = model, Result = true, Message = "Changed Successfully });
}
Have you set up your routing in Global.asax to accept the 3 parameters you are passing?
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}/{accountId}/{activate}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, accountId = UrlParameter.Optional, activate = UrlParameter.Optional }
);
Related
I'm developing ASP Core Web API using dotnet core v3.1.
I'm using JWT tokens for authentication. And for authorization I use the [Authorize] attribute.
How can I create my own response if the user is not logged in (while trying to access the action marked with the [Authorize] attribute) or the user's token is not authenticated.
I came across a solution using a custom authorization attribute inherited from the default one. And in this example, the HandleUnauthorizedRequest method is overridden. But I don't see such a method inside the AuthorizeAttribute class.
Is there a way to create custom unauthorized responses with http body?
Since you are using JWT bearer authentication, one way to override the default Challenge logic (which executes to handle 401 Unauthorized concerns) is to hook a handler to the JwtBearerEvents.OnChallenge callback in Startup.ConfigureServices:
services.AddAuthentication().AddJwtBearer(options =>
{
// Other configs...
options.Events = new JwtBearerEvents
{
OnChallenge = async context =>
{
// Call this to skip the default logic and avoid using the default response
context.HandleResponse();
// Write to the response in any way you wish
context.Response.StatusCode = 401;
context.Response.Headers.Append("my-custom-header", "custom-value");
await context.Response.WriteAsync("You are not authorized! (or some other custom message)");
}
};
});
This will override the default challenge logic in JwtBearerHandler.HandleChallengeAsync, which you can find here for reference purposes.
The default logic does not write any content to response (it only sets the status code and set some headers). So to keep using the default logic and add content on top of it, you can use something like this:
options.Events = new JwtBearerEvents
{
OnChallenge = context =>
{
context.Response.OnStarting(async () =>
{
// Write to the response in any way you wish
await context.Response.WriteAsync("You are not authorized! (or some other custom message)");
});
return Task.CompletedTask;
}
};
For .net core 5 web api project with jwt authentication use this middleware in Configure method of Startup.cs for show ErrorDto in Swagger:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "LoginService v1"));
}
app.ConfigureExceptionHandler();
app.UseHttpsRedirection();
app.UseRouting();
// Unauthorized (401) MiddleWare
app.Use(async (context, next) =>
{
await next();
if (context.Response.StatusCode == (int)HttpStatusCode.Unauthorized) // 401
{
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(new ErrorDto()
{
StatusCode = 401,
Message = "Token is not valid"
}.ToString());
}
});
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
ErrorDto :
public class ErrorDto
{
public int StatusCode { get; set; }
public string Message { get; set; }
public override string ToString()
{
return JsonSerializer.Serialize(this);
}
}
This is what I came up with for responding with the same ProblemDetails you would get from returning Unauthorized() in an ApiController:
.AddJwtBearer(options =>
{
// Other configs...
options.Events = new JwtBearerEvents
{
OnChallenge = async context =>
{
// Call this to skip the default logic and avoid using the default response
context.HandleResponse();
var httpContext = context.HttpContext;
var statusCode = StatusCodes.Status401Unauthorized;
var routeData = httpContext.GetRouteData();
var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());
var factory = httpContext.RequestServices.GetRequiredService<ProblemDetailsFactory>();
var problemDetails = factory.CreateProblemDetails(httpContext, statusCode);
var result = new ObjectResult(problemDetails) { StatusCode = statusCode };
await result.ExecuteResultAsync(actionContext);
}
};
});
Simple usecase - get PartialView dynamically from AJAX call to update div in my main page after input select (dropdownlist) changed value.
Steps I took:
Created view (only, wihtout PageModel) with model declared with #model ViewModelCreateOperation.
Created checkbox on main page:
<select class="form-control" asp-items="#(new SelectList(Model.allExistingOperations))" onchange="PopulateForm(this.value); return false;"></select>
created scripts on main page:
<script>
function PopulateForm(value) {
var dataToPost = "{ operationName:" + value + "}";;
$.ajax({
type: "post",
url: '#Url.Content("/MeaningOfLifeRoutedName")',
data: dataToPost ,
contentType : 'application/json; charset=UTF-8',
success: function (data) {
$('#lubieplacki').html(data);
},
error: function (xhr, ajaxOptions, thrownError) {
if (xhr.status == 404) {
alert(thrownError);
}
}
});
}
</script>
created Controller in Controllers folder to return PartialView (becouse I cannot use "return PartialView("someview", someModel)" with PageModel already used as a inherit class.
namespace MyMysteriousApplication.Controllers
{
[Route("MeaningOfLifeRoutedName")]
public class MeaningOfLifeChangesController : Controller
{
private readonly MyMysteriousApplication.Models.TTSCDBContext _context;
public MeaningOfLifeChangesController(MyMysteriousApplication.Models.TTSCDBContext context)
{
_context = context;
}
public ViewModelCreateOperation viewModelCreateOperation { get; set; }
public IActionResult Index()
{
return RedirectToPage("../Index");
}
[HttpPost]
public ActionResult getMeaningOfLife(string operationName)
{
viewModelCreateOperation = new ViewModelCreateOperation();
viewModelCreateOperation = new ViewModelCreateOperation();
viewModelCreateOperation._entitiesSelectListItem = _context.Entities
.Select(a => new Microsoft.AspNetCore.Mvc.Rendering.SelectListItem()
{
Value = a.Id.ToString(),
Text = a.EntityName
}).OrderByDescending(u => u.Text)
.ToList();
viewModelCreateOperation.MeaningOfLifeChanges = _context.MeaningOfLifeChanges.Where(u => u.OperationName.Contains(operationName)).OrderBy(u => u.ChangeId).FirstOrDefault();
return PartialView("../projectManagement/partialViewCreateNewMOL", viewModelCreateOperation);
}
}
}
Primary question:
I got null in parameters - I don't get why:
Bonus question:
I couldn't invoke my controller in any way (tried "/MeaningOfLifeChangeController/getMeaningOfLife" or "/MeaningOfLifeChange/getMeaningOfLife", with "~/MeaningOfLifeChangeController/getMeaningOfLife" and others combinations), so I added [Route("MeaningOfLifeRoutedName")] and [HttpPost] before method. I don't get why...
in Startup I have added controllers to initialize (JSON is for other stuff(API)):
services.AddControllersWithViews().
AddJsonOptions(options =>
{
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.PropertyNamingPolicy = null;
options.JsonSerializerOptions.MaxDepth = 150;
}).AddRazorRuntimeCompilation();
It's not my answer, but Jiadong Meng helped me in ASP .NET Forums. I'm posting His answer:
Since the data you want to send is just a string type data, you need to stringify it like below.
var dataToPost = JSON.stringify(value);
Then in your Action, you should also add [FromBody] attribute.
public ActionResult getMeaningOfLife([FromBody]string operationName)
I am unable to get a json response from my controller action. The network shows as a post which is correct as I am posting a file to the server, however, needs a JSON response sent back to my view.
public JsonResult Upload(HttpPostedFileBase file, int id)
{
Homes thishomes= _db.Homes.FirstOrDefault(t => t.Id == id);
FileUploader fileupload = new FileUploader();
fileupload.PostIt(file.InputStream);
return Json(new { success = true, response = "File uploaded.", JsonRequestBehavior.AllowGet });
}
JQUERY using Dropzonejs:
Dropzone.options.DropzoneForm = {
paramName: "file",
maxFilesize: 2000,
maxFiles: 28,
dictMaxFilesExceeded: "Custom max files msg",
init: function () {
this.on("success", function () {
alert("Added file");
})
}
};
Can anyone see an this issue?
Try to write [HttpPost] attribute over your action. Also "The network shows as a post which is correct" if its post then you don't need JsonRequestBehavior.AllowGet
when you are returning Json to your request
I have been trying to pass an object as a Post parameter to .NET asmx web service. I can pass primitive types such as int, string as parameters but I would like to pass the whole object because my class contains a lot of properties and it is very time consuming to pass each property individually.
My c# web service code is:
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public ContactBLL AddContact(ContactBLL Contact)
{
//add contact and return the contact object
}
I have added following statement at the top of the web service class:
[System.Web.Script.Services.ScriptService]
I have a second function in the web service which I call when my page loads in order to get a json ContactBLL object.
[WebMethod(EnableSession = true)]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public ContactBLL GetContact(int ContactID)
{
//return contact
}
I use following functions in my factory to call asmx web methods:
factory.GetContactInfo = function (ContactID) {
return $http.post(serviceBase + 'GetContact', { ContactID: ContactID }).then(function (results) {
return results.data;
});
};
factory.InsertContact = function (Contact) {
return $http.post(serviceBase + 'AddContact', { ContactBLL: Contact }).then(function (results) {
return results.data;
});
};
In my controller, the GetContact function is called when the page loads and it returns the correct data to initialise the Contact object. I then call AddContact function and pass the object to factory function. The control doesn't get to the web service and I see the 500 message in chrome with the following message:
Message: "Invalid web service call, missing value for parameter: 'Contact'."
Here is the code for the controller:
var ContactController = function ($scope, $location, $http, $window, ContactService) {
var Contact;
Initialise();
function Initialise() {
Contact = {};
GetContact(-1);
}
function GetContact(ContactID) {
ContactService.GetContactInfo(ContactID)
.then(function (data) {
//do something
}, function (error) {
$window.alert('Sorry, an error occurred: ' + error.data.message);
});
}
$scope.AddContactRecord = function () {
ContactService.InsertContact(Contact)
.then(function (data) {
//do something
}, function (error) {
$window.alert('Sorry, an error occurred: ' + error.data.message);
});
}
}
Please let me know if I am doing anything wrong or an easy way for passing tens of properties via Post call. The GetContact call works fine, however, I get error on InsertContact call.
I have found the reason for the error. I was passing the datatype (ContactBLL) instead of the name (Contact) of the paramter in the AddContact function of my factory. The correct code is below:
factory.InsertContact = function (Contact) {
return $http.post(serviceBase + 'AddContact', { Contact: Contact }).then(function (results) {
return results.data;
});
};
I've run into a problem while building a new MVC WebApi project where my post actions do not appear to be working correctly.
I have the following action:
//--
//-- POST: /api/groups/subscribe/1/groups
[HttpPost]
public GroupResponse Subscribe(int id, List<int> groups )
{
var response = new GroupResponse();
var manager = new UserManagement();
try
{
response.Status = 1;
var subscribedGroups = manager.GetSubscribedGroups(id).Select(g => g.GroupId).ToList();
foreach (var subscribedGroup in subscribedGroups.Where(groups.Contains))
{
groups.Remove(subscribedGroup);
}
//-- group is a reserved word # escapes this and treats it as a regular variable
foreach (var #group in groups.Where(g => !manager.JoinGroup(id, g)))
{
response.Status = 2;
response.ErrorMessage = Constants.SUBSCRIBE_FAIL;
}
}
catch (Exception)
{
response.Status = 2;
response.ErrorMessage = Constants.SUBSCRIBE_FAIL;
return response;
}
return response;
}
When I attempt to consume this action from rest kit I get the following error message:
{
"Message":"No HTTP resource was found that matches the request URI 'http://localhost:50393/api/groups/subscribe'.",
"MessageDetail":"No action was found on the controller 'Groups' that matches the request."
}
I've tried executing the action via fiddler however it looks like the api is ignoring my data being sent to the api which is confusing me at the moment.
When I attempt to use the api as follows: /api/groups/subscribe?id=1 then the api action is executed, however I'm unable to pass the the required list.
I've also setup a route to try and handle this, but it doesn't appear to be helping out at all:
config.Routes.MapHttpRoute(
"subscribe",
"api/groups/subscribe/{id}/{groups}",
new { controller = "Groups", action = "Subscribe", id = RouteParameter.Optional, groups = RouteParameter.Optional
});
Additional info:
When testing with fiddler I am composing my own requests as follows:
Request Headers:
User-Agent: Fiddler
Host: localhost:50393
Content-Length: 29
Content-Type: application/json; charset=utf-8
Request Body:
{"id":1,"groups":[1,2,3,4,5]}
You can try this, tested now with fiddler.
The controller:
public class AuthenticationController : ApiController
{
[AcceptVerbs("POST")]
public void Subscribe(SubscribeClass data)
{
//Do your stuff here
int id = data.id;
List<int> groups = data.groups;
//data contains values
}
public class SubscribeClass
{
public int id { get; set; }
public List<int> groups { get; set; }
}
}
The WebApiConfig:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "AuthenticateUser",
routeTemplate: "{controller}/{action}",
defaults: new { },
constraints: new { controller = "Authentication", action = "Subscribe" }
);
}
The JSON object send via Fiddler:
{ "id": 1 , "groups": [1, 2, 3, 4, 5] }
In the Headers section in Fiddler make sure to add this header for your scenario:
Content-Type: application/json; charset=utf-8
and then the POST URL
http://localhost/api/Authentication/Subscribe
Hope this helps.