UriPathExtensionMapping in MVC 4 - asp.net-mvc-routing

How do I use UriPathExtensionMapping in MVC4? I've added the mapping in the formatter such that:
MediaTypeMappings.Add(new UriPathExtensionMapping("json", new MediaTypeHeaderValue("application/json"))
But I can't use the extension on my route unless I add a verb, such as:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}.{extension}"
);
Then it'll recognize my desired media type, but it also starts expecting "extension" as a parameter on the action. Example:
public object Get(string extension)
{
}
Instead of just:
public object Get()
{
}
How can I resolve this?
Thanks!

I don't remember if I noticed this problem, but it might be because I explicitly specified the default value for the extension like:
var rF = routes.MapHttpRoute(
name: "DefaultApi.v1.format",
routeTemplate: "api/1/{controller}/{action}.{format}/{*id}",
defaults: new { id = RouteParameter.Optional, format = "json", version = 1 }
);
*note format = "json" in the line beginning with defaults.
I did also notice that this sometimes doesn't work on requests with trailing parameters for id, like ~/api/1/values/get.json/4.

I can't repro this problem.
You are right that, to use UriPathExtensionMapping, you will need to specify the {controller}.{ext} in your route, like what you described above. If you only specify the {controller}, and uri looks like ~/home.json, then the controller token will be mapped to home.json, which is probably not what you wanted.
However, you should not need to require an "extension" parameter in your action. If you continue seeing this problem, can you post your entire repro, including your controller, your routes and configuration set up with custom formatter? thank you.

Related

Retrieve content element field from within a plugin template?

I am modifying the template of a plugin, and I want to retrieve a field from the content element.
Using f:debug I see the only data available is from the plugin itself, and none from the content element.
Is there any way I can perhaps insert the field I need in the plugin settings?
eg. something like:
plugin.tx_plugin.settings {
contentUid = TEXT
contentUid.field = uid
}
The best way I can think of to do this is with a custom ViewHelper. Something like:
namespace MyVendor\MyExtension\ViewHelpers;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
class ContentUidViewHelper extends AbstractViewHelper
{
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
return $configurationManager->getContentObject()->data['uid'];
}
}
In your Fluid template:
<mynamespace:contentUid />
This will get the uid of the content element, but you can get any field this way. Just change the key of the data array to the field you need.
In the corresponding method (like the listAction or showAction) of the controller you can get the data of the content element in the following way:
$contentObject = $this->configurationManager->getContentObject();
$this->view->assign('contentObjectData', $contentObject->data);
As far as I know, you can't get to that data using typoscript, but I've never needed it anyway since I've been using the above code in the controller.
settings do not have stdWrap-type per default, but only string. So you can not use cObjects as values.
For one (or a few) settings, you could do the stdWrap-processing in your method/class yourself:
$settingsAsTypoScriptArray = $this->objectManager->get(TypoScriptService::class)->convertPlainArrayToTypoScriptArray($this->settings);
$contentObj = $this->configurationManager->getContentObject();
if ($contentObj === null) {
$contentObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
}
// now override the values in the settings array with the processed value
$contentUid = (int)$contentObj->stdWrap($settingsAsTypoScriptArray['contentUid'], $settingsAsTypoScriptArray['contentUid.']);
If you wanna have many settings to be stdWraped, have a look into EXT:news. Georg implemented an interesting concept via useStdWrap configuration.

Create OpportunityProduct using REST

I'm trying to create a new OpportunityProduct using this rest call:
XRMServices/2011/OrganizationData.svc/OpportunityProductSet
{"ProductId":"ef71ce8e-1ef3-e211-b252-984be17c47e4","Quantity":123,"ProductDescription":"Added from code - TEST123","OpportunityId":"8bdb3525-7274-e311-a90b-6c3be5be5f78"}
The call returns:
The request should be a valid top-level resource object.
This indicates that my arguments is not correct. I see to possible reasons for this:
I'm missing some required properties (How can I figure out which is required?)
It is not possible to do using rest
(both guids are returned via other call and should be correct)
Any help would be great!
Thanks
Larsi
For lookup references to other entities you need to specify both the GUID and the type. You also need to include the UOM when creating an Opportunity Product based on an existing Product. Your object should look something like this:
var entity = {};
entity.Quantity = parseFloat(4).toFixed(2);
entity.OpportunityId = {
Id: '69BB2236-B57F-E311-BB6D-6C3BE5A881A4',
LogicalName: 'opportunity'
};
entity.ProductId = {
Id: 'C8138483-DF81-E311-B542-6C3BE5A8362C',
LogicalName: 'product'
};
entity.UoMId = {
Id: 'BE0FB859-7E90-4B3E-B501-3AB3CD4DC8FC',
LogicalName: 'uom'
};

Why is this IgnoreRoute call matching these requests?

I've got a localization httphandler that's running in the context of my ASP.Net MVC2 Content folder (part of what it's doing is compiling .less files that are in /Content/css). My default route for this particular set of requests looks like this:
context.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
context.MapRoute(
"Area_default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { controller = new VirtualDirectoryConstraint("VDirectory1") },
new string[] { "Namespace.One.MVC" }
);
(As an aside - I don't think it's relevant, but just in case - the VirtualDirectoryConstraint rejects matches on this route if the request is not coming from the passed-in application path/virtual directory)
With this configuration a call to http://server.net/VDirectory1/Content/css/LessCompiler.axd fails because there's no ContentController class. All well and good.
When I add
context.Routes.IgnoreRoute("{Content}/{*pathInfo}");
that call succeeds, but subsequent calls to
http://server.net/VDirectory1/Localization/ClientScript/en-US.js
and
http://server.net/VDirectory1/Authorization/ClientScript
fail. Looking at Phil Haack's RouteDebugger tool, those calls are matching the Content IgnoreRoute route:
True {Content}/{*pathInfo} (null) (null) (null)
and are therefore not being routed to the LocalizationController and AuthorizationController, respectively.
Clearly I'm misunderstanding something about how the IgnoreRoute is supposed to be used and why that particular IgnoreRoute is matching those requests. What am I missing?
Shouldn't your IgnoreRoute use Content instead of {Content} ?
context.Routes.IgnoreRoute("Content/{*pathInfo}");
At the moment, {Content} is probably being expanded as a variable to nothing, which makes the pathinfo match everything.

How to change the separation character of Zend Url?

I use Zend URL view helper for building my urls. Everythings works exactly as I'd like to, except one thing: The character used for replacing spaces in the url is a plus (+). I'd like it to be a 'min' (-). How can I change this?
Example:
Now: /nl/nieuws/bericht/3/title/nieuwe**+affiches
Wish: /nl/nieuws/bericht/3/title/nieuwe-**affiches
Thanks in advcance!
This isn't in the documentation anywhere, but it appears that the Zend URL view helper can take a parameter in it's $urlOptions array called chainNameSeparator. No guarantee that's what you're looking for, but trying playing with that and see if it changes anything.
This is likely happening because, by default, Zend_View_Helper_Url will urlencode() what you send it, which would translate spaces into +. My suggestion to you would be to create a new view helper for the type of URL in your code that needs the special inflection.
Something like:
class Default_View_Helper_SpecialUrl extends Zend_View_Helper_Abstract
{
public function specialUrl(array $opts = array(), $name = null, $reset = false, $encode = true)
{
if (!empty($opts['whatever'])) {
$opts['whatever'] = str_replace(' ', '-', $opts['whatever']);
}
$router = Zend_Controller_Front::getInstance()->getRouter();
return $router->assemble($opts, $name, $reset, $encode);
}
}
This way the spaces are changed for whatever necessary route parameters before URL encoding happens by the router.

CodeIgniter: URIs and Forms

I'm implementing a search box using CodeIgniter, but I'm not sure about how I should pass the search parameters through. I have three parameters: the search string; product category; and the sort order. They're all optional. Currently, I'm sending the parameters through $_POST to a temporary method, which forwards the parameters to the regular URI form. This works fine. I'm using a weird URI format though:
http://site.com/products/search=computer,sort=price,cat=laptop
Does anyone have a better/cleaner format of passing stuff through?
I was thinking of passing it into the products method as arguments, but since the parameters are optional things would get messy. Should I suck it up, and just turn $_GET methods on? Thanks in advance!
Query Strings
You can enable query strings in CodeIgniter to allow a more standard search function.
Config.php
$config['enable_query_strings'] = FALSE;
Once enabled, you can accept the following in your app:
http://site.com/products/search?term=computer&sort=price&cat=laptop
The benefit here is that the user will find it easy to edit the URL to make a quick change to their search, and your search uses common search functionality.
The down side of this approach is that you are going against one of the design decisions of the CodeIgniter development team. However, my personal opinion is that this is OK provided that query strings are not used for the bulk of your content, only for special cases such as search queries.
A much better approach, and the method the CI developers intended, is to add all your search parameters to the URI instead of a query string like so:
http://site.com/products/search/term/computer/sort/price/cat/laptop
You would then parse all the URI segments from the 3rd segment ("term") forward into an array of key => value pairs with the uri_to_assoc($segment) function from the URI Class.
Class Products extends Controller {
...
// From your code I assume you are calling a search method.
function search()
{
// Get search parameters from URI.
// URI Class is initialized by the system automatically.
$data->search_params = $this->uri->uri_to_assoc(3);
...
}
...
}
This would give you easy access to all the search parameters and they could be in any order in the URI, just like a traditional query string.
$data->search_params would now contain an array of your URI segments:
Array
(
[term] => computer
[sort] => price
[cat] => laptop
)
Read more about the URI Class here: http://codeigniter.com/user_guide/libraries/uri.html
If you're using a fixed number of parameters, you can assign a default value to them and send it instead of not sending the parameter at all. For instance
http://site.com/products/search/all/somevalue/all
Next, in the controller you can ignore the parameter if (parameter == 'all'.)
Class Products extends Controller {
...
// From your code I assume that this your structure.
function index ($search = 'all', $sort = 'price', $cat = 'all')
{
if ('all' == $search)
{
// don't use this parameter
}
// or
if ('all' != $cat)
{
// use this parameter
}
...
}
...
}