i have mapping like this:
#URLMapping(id = "edituser", pattern = "/edituser/#{ id: userBean.userId}", viewId = "/faces/pages/users/editUser.xhtml")
and i want to redirect to it from an action method, so i tried the following:
return "pretty:edituser/" + userObj.getId();
but it didn't work, it reloads current page, please advise, thanks.
In your case something like this should work:
return "/faces/pages/users/editUser.xhtml?faces-redirect=true&id=" + userObj.getId();
Another option would be to obtain the UserBean, set the id property and then return pretty:editust. Something like this:
public class Whatever {
#Inject
private UserBean userBean;
public String action() {
// do something
userBean.setUserUd( someId );
return "pretty:edituser";
}
}
Related
i have successfully configured Spring Boot 2.0.4 to use spring-security with HDIV.
I decided to use jquery datatable as table rendering technology.
Here come the problem...
For each row of my datatable i'd like to create a detail link signed with _HDIV_STATE_ parameter how can i generate a valid link while iterating my item list in a controller?
A generic Controller:
#Controller
public class ItemController {
....
#GetMapping(value = "/test")
public #ResponseBody test() {
List<Item> items = service.getList();
items.foreach(item -> {
item.setDetailUrl(HDIV_GENERATED_URL);
})
}
...
}
Thanks
You can inject the LinkUrlProcessor class in the controller.
#autowired
LinkUrlProcessor linkUrlProcessor;
And invoke processUrl method.
String processedUrl linkUrlProcessor.processUrl(request, originalUrl);
The processedUrl will contain the _HDIV_STATE_ parameter.
Ok, this solution work fine only with ulrs with pathvariable.
#Autowired
ServletContext context;
public #ResponseBody String test() {
int id = 1;
LinukUrlProcessor lup = HDIVUtil.getLinkUrlProcessor(context);
RequestContextHolder rch =HDIVUtil.getRequestContext(context);
//This works perfectly
String processedWithPath = lup.processUrl(rch, "/test" + id);
//This produce a _HVID_STATE_ but query param always 0
String processedWithQuery = lup.processUrl(rch, "/test?id=" + id);
return ....
}
Any further help generating valid url with query parameters?
I am needing to simultaneously support a query-parameter based route (/api/models?id=1) and a route based one (/api/models/1) while still allowing unambiguous access to the models collection (/api/models)?
My controller looks (something) like this:
[Route("/api/{controller}")]
public class ModelsController : Controller
{
[HttpGet]
public Models[] GetModels([FromQuery]QueryOptions queryOptions)
{
//...
}
[HttpGet("{id:int}")]
public Model Get([FromRoute] int id)
{
//...
}
[HttpGet("?{id:int}")]
public Model Get2Try1([FromQuery] int id)
{
//Fails with ": The literal section '?' is invalid.
//Literal sections cannot contain the '?' character."
//Which makes sense after some reading...
}
[HttpGet]
public Model Get2Try2([FromQuery] int id)
{
//Fails with "AmbiguousActionException: Multiple actions matched.
//The following actions matched route data and had all constraints satisfied:
//GetModels and Get2Try2"
//Which I think I understand as well...the absence of optional params
//means ambiguous routing...
}
[HttpGet] //What here?
public Model Get2Try3([FromQuery] int id) //and/or here?
{
}
}
I feel like there should be some way to (with declarative routing) accomplish this. Has anyone done anything along these lines?
Also, current code base is ASP.NET Core (RC1) to be upgraded to RTM/1.0 shortly. Details on either side are likely similar, but am interested in either/both.
I've found that the following works:
[HttpGet, Route("{id?}")]
... the key being mainly the '?'. You don't need any [FromX] in the method signature, this does the trick and caters for both query string and route parameter passing.
Unfortunately Swagger UI doesn't like it and expects some explicit parameter to work out of the box (https://github.com/domaindrivendev/Ahoy/issues/47 or https://github.com/domaindrivendev/Ahoy/issues/182), but that's another story :)
I had the same problem.
There aren't solutions that works (against wep api .net) with web api core.
If we set [Route("{id}")] and [Route("")] doesn't work; if we set only [Route("{id?}")] the query parameter is empty if I use querystring.
So, I've used a workround.
I used [Route("{id?}")], but inside the function I get param from Request.Query
Example
public T Cast<T>(string input)
{
T output = default(T);
if (string.IsNullOrWhiteSpace(input))
return output;
input = input.Trim();
try
{
Type typeToCastTo = typeof(T);
if (typeof(T).IsGenericType)
typeToCastTo = typeToCastTo.GenericTypeArguments[0];
if (typeToCastTo.IsEnum)
{
if (Enum.IsDefined(typeToCastTo, input))
return (T)Enum.Parse(typeToCastTo, input);
return output;
}
object value = Convert.ChangeType(input, typeToCastTo, CultureInfo.InvariantCulture);
return (value == null) ? output : (T)value;
}
catch
{
return output;
}
}
public void MapQuerystringParams<T>(ref T param, string name)
{
var q = Request.Query[name].FirstOrDefault();
if (q != null)
{
var cast = Cast<T>(q);
if (!cast.Equals(default(T)))
param = cast;
}
}
[Route("api/[controller]/[action]")]
[ApiController]
public class ActivityController : ControllerBase
{
//examples of call
//https://localhost:44345/api/Activity/GetActivityByCode/7000
//https://localhost:44345/api/Activity/GetActivityByCode/?Id=7000
[HttpGet]
[Route("{Id?}")]
public IActionResult GetActivityByCode(int Id)
{
MapQuerystringParams(ref Id, "Id"); //this take param from querystring if exists
ActivityBusiness business = new ActivityBusiness(new BusinessInitializer { config = configuration });
ActivityDTOModel activity = business.GetActivityByCode(Id);
return Ok(activity);
}
}
Ideally in the domain design if you can have one method serving one specific function then great. Recently I had to faithfully implement a legacy API and it wasn't an option for me to decompose the design of my API.
If you are suffering from ambiguous routes in MVC6 and need to differentiate unique Routes given specific QueryStrings that have been supplied at one single POST method. Then IActionConstraint can help! Here is some example code of me using it :
using System;
using Microsoft.AspNetCore.Mvc.ActionConstraints;
namespace Automation.Api.Service.Attributes
{
public class RoutingSpecificAttribute : Attribute, IActionConstraint
{
private string _keyParam;
public RoutingSpecificAttribute(string routingParameter)
{
this._keyParam = routingParameter;
}
public int Order
{
get
{
return 0;
}
}
public bool Accept(ActionConstraintContext context)
{
if (this._keyParam == null) { return true; }
switch (this._keyParam)
{
case "name": return context.RouteContext.HttpContext.Request.Query.ContainsKey(this._keyParam);
case "noquerystring": return context.RouteContext.HttpContext.Request.Query.Count == 0;
default:
return false;
}
}
}
}
This one method in the API that I needed to author both serviced a separate create + update functions based on the existence of a couple of QueryStrings: name & version.
So to help disambiguate you can distinctly decorate each of the methods within your controllers within said controller class [RoutingSpecific("noquerystring")] or [RoutingSpecific("name")] to help differentiate.
MSDN class description
Example implementation - see Entropy github
For anyone that happens to stumble upon this as I have,
Using .Net Core 3.1 the following works:
Web Controller Method
[HttpGet("something/{id}")]
public IActionResult Get([FromRoute] id, [FromQuery] OptionalParams optionalParams)
{
// do stuff
}
Query Parameter Container
public class OptionalParams
{
[FromQuery(Name = "colour_of_thing")]
public string Colour { get; set; }
[FromQuery(Name = "shape_of_thing")]
public string Shape { get; set; }
[FromQuery(Name = "some_other_filter")]
public string SomeOtherFilter { get; set; }
}
Url
var id = Guid.NewGuid();
var colour = "red";
var shape = "circle";
var url = $"Http://localhost:5000/something/{id}?colour_of_thing={colour}&shape_of_thing={shape}";
I have following code that is accepting a form submission
[ActionName("TestingTemp"), AcceptVerbs(HttpVerbs.Post)]
public ActionResult TestingTemp(FormCollection result)
{
string cat = "";
return View("Try");
}
Now the problem is even though it seems to load "Try" page, things break on the page because it doesn't fire the following code (which does get properly fired if I directly go to Try page).
public ActionResult Try()
{
ViewData["Test"] = DataLayer.Test(0, 10);
return View();
}
Also the url contains TestingTemp where it should contain Try, if you know what I mean.
I think what you are looking for is RedirectToAction. It will redirect to your other method and rewrite the URL.
[ActionName("TestingTemp"), AcceptVerbs(HttpVerbs.Post)]
public ActionResult TestingTemp(FormCollection result)
{
string cat = "";
return RedirectToAction("Try");
}
the preferred way is to use redirectToAction but if u do want to go that way then u have to put the required data that u r doing in Try method like
[ActionName("TestingTemp"), AcceptVerbs(HttpVerbs.Post)]
public ActionResult TestingTemp(FormCollection result)
{
string cat = "";
ViewData["Test"] = DataLayer.Test(0, 10);
return View("Try");
}
but as i said this way is not preferred i.e repeating ur code in each action rather u can just write something like
[ActionName("TestingTemp"), AcceptVerbs(HttpVerbs.Post)]
public ActionResult TestingTemp(FormCollection result)
{
string cat = "";
return RedirectToAction("Try");
}
I have 2 actions on a controller:
public class CalculatorsController : Controller
{
//
// GET: /Calculators/
public ActionResult Index()
{
return RedirectToAction("Accounting");
}
public ActionResult Accounting()
{
var combatants = Models.Persistence.InMemoryCombatantPersistence.GetCombatants();
Debug.Assert(combatants != null);
var bvm = new BalanceViewModel(combatants);
Debug.Assert(bvm!=null);
Debug.Assert(bvm.Combatants != null);
return View(bvm);
}
}
When the Index method is called, I get a null model coming out. When the Accounting method is called directly via it's url, I get a hydrated model.
This is less an answer than a workaround. I am not sure why you are getting a null model as it looks like it should work. In fact, I can confirm the behavior you are seeing when I try it out myself. [EDIT: I discovered a flaw in my initial test code that was causing my own null model. Now that that is corrected, my test works fine using RedirectToAction.] If there is a reason for it, I don't know it off the top of my head.
Now for the workaround...I assume that you are doing it this way since the default route sends all traffic to http://www.domain.com/Calculators to "Index". So why not create a new route like this:
routes.MapRoute(
"Accounting",
"Calculators/{action}/",
new { controller = "Calculators", action = "Accounting" }
);
This route specifies the default action to the Calculators controller will be "Accounting" instead of Index.
Your view for the Action Accounting expects a model. (the BalanceViewModel). The index action method does not have a instance of the BalanceViewModel.
There are a number of ways you can solve this. In your View (aspx page) you can check for nulls...
Or in the index action method, you create a new instance of a BalanceViewModel and store it in TempData, and then retrieve this in your view when your model is null.
Or in your action method, you could also call return View("Accounting", new BalanceViewModel()) instead of using redirect to action.
EDIT: Example Code -
If you want to share this functinality, create a private method like this:
public class CalculatorsController : Controller {
// GET: /Calculators/
public ActionResult Index() {
return View(GetBalanceViewModel());
}
public ActionResult Accounting() {
return View(GetBalanceViewModel());
}
private BalanceViewModel GetBalanceViewModel() {
var combatants = Models.Persistence.InMemoryCombatantPersistence.GetCombatants();
Debug.Assert(combatants != null);
var bvm = new BalanceViewModel(combatants);
Debug.Assert(bvm != null);
Debug.Assert(bvm.Combatants != null);
return bvm;
}
}
Have you seen this Question?
I have an action that returns either a FileContentResult or a NotModifiedResult, which is a custom result type that returns HTTP 304 to indicate that the requested resource has not been modified, like this:
[ReplaceMissingPicture(Picture = "~/Content/Images/nothumbnail.png", MimeType = "image/png")]
public ActionResult Thumbnail(int id)
{
var item = Service.GetItem(id);
var requestTag = Request.Headers["If-None-Match"] ?? string.Empty;
var tag = Convert.ToBase64String(item.Version.ToArray());
if (tag == requestTag)
{
return new NotModifiedResult();
}
if (item.Thumbnail != null)
{
var thumbnail = item.Thumbnail.ToArray();
var mime = item.PictureMime;
Response.AppendHeader("ETag", tag);
return File(thumbnail, mime);
}
else
{
return null;
}
}
This action needs to access the Response object, which is of course not present during testing, so that makes this action untestable. I could add conditional statements around it, so that it runs during testing, but then I can't test for the headers being set correctly.
What would be a solution to this problem?
FYI, the ReplaceMissingPicture filter returns a specific resource in case null was returned from this action, to keep the MapPath() call out of the controller for the very same reason.
The first step would be to create an interface which simplifies the services you need:-
public interface IHeaders
{
public string GetRequestHeader(string headerName);
public void AppendResponseHeader(string headerName, string headerValue);
}
Now create a default implementation:-
public Headers : IHeaders
{
public string GetRequestHeader(string headerName)
{
return HttpContext.Current.Request[headerName];
}
public void AppendResponseHeader(string headerName, string headerValue)
{
HttpContext.Current.Response.AppendHeader(headerName, headerValue);
}
}
Now add a new field to your Controller:-
private IHeaders myHeadersService;
add new constructor to you controller:-
public MyController(IHeaders headersService)
{
myHeadersService = headersService;
}
modify or add the default constructor:-
public MyController()
{
myHeadersService = new Headers();
}
now in your Action code use myHeadersService instead of the Response and Request objects.
In your tests create your own implementation of the IHeaders interface to emulate/test the Action code and pass that implementation when constructing the Controller.
How about creating a subclass of FileResult--say ETagFileResult--that in its ExecuteResult() method sets the ETag header, and then defaults to the base class implementation? You can test that class with a mocked context (as you presumably are with your NotModifiedResult) to be sure that it's doing the right thing. And remove the entire complication from the testing of the controller.
Failing that, it's possible to set a mocked context on the controller in your test (after instantiating the class, before calling the action method). See this question, for instance. But that seems like more work.
(Also, by the way, it looks like you're quoting the tag value twice there: once when tag is set, and once more when you actually set the header....)