Regex for route work incorrect and return error 404 - regex

I have custom regex for match correct username:
^(?=.{5,20}$)[a-zA-Z](?:[a-zA-Z0-9_]*[a-zA-Z0-9])?$
See demo
Here you can see my routes list:
Route::middleware(['userActivity'])->group(function () {
Route::group(['prefix' => '{nickname}','where' => ['nickname' => '^(?=.{5,20}$)[a-zA-Z](?:[a-zA-Z0-9_]*[a-zA-Z0-9])?$']], function ($nickname) {
Route::name('user.')->namespace('User')->group(function () {
Route::middleware(['auth', 'company'])->group(function () {
Route::namespace('Vacancy')->group( function () {
Route::prefix('vacancy')->name('vacancy.')->group( function () {
Route::get('/manage', "VacancyController#manage")->name('manage');
Route::post('/save', "VacancyController#save")->name('save');
});
});
});
});
});
});
In this case when I go to route user.vacancy.manage:
http://website.com/user_1544080981/vacancy/manage
Return error:
404 Page Not Found
When I change my regex to:
^(?=.{5,30}$)[a-zA-Z](?:[a-zA-Z0-9_]*[a-zA-Z0-9])?$
Note: Changed in regex only min and max length from {5,20} to {5,30}
Generaly when I see to part of url after domain name url length == 30
user_1544081143/vacancy/manage
But regex must work only for user nickname instead of to part url without domain name. Where I have any errors in my routes?

Regular expression, this is a better way then above in my opinion.
Route::any('{all}', function(){
return 'It Works';
})->where('all', '.*');
Using the Route::fallback method, you may define a route that will be executed when no other route matches the incoming request. Typically, unhandled requests will automatically render a "404" page via your application's exception handler. However, since you may define the fallback route within your routes/web.php file, all middleware in the web middleware group will apply to the route. Of course, you are free to add additional middleware to this route as needed:
Route::fallback(function () {
//
});

Related

How to exclude "/api" from "any"-route in Lumen router (per regex)?

I have a problem with the Lumen router (web.php):
My project includes vue.js with the vue router, so I want to point all routes to the router, what is indeed working fine.
$router->get('{path:.*}', function () {
return view('app');
});
My problem is: I also have a few api routes, handled by Lumen/controllers:
$router->group(['prefix' => 'api'], function ($router) {
$router->group(['prefix' => 'authors'], function ($router) {
$router->get('/', 'AuthorController#showAllAuthors');
$router->get('/id/{id}', 'AuthorController#showAuthorById');
});
});
Well, the route localhost/api/authors just works fine.
But localhost/api/authors/1 returns the app..
I was thinking of putting in an exception to the vue route:
$router->get('{path:^(?!api).*$}'
..but this will lead to a NotFoundHttpException.
Is something wrong with the regex?
It should exclude all routes that start with /api.
You're really close. Regex happens after the get/post statements in laravel's routes. Like so:
$router->get('/{catch?}', function () {
return view('app');
})->where('catch', '^(?!api).*$');
Here's the docs for reference:
https://laravel.com/docs/5.8/routing#parameters-regular-expression-constraints
Edit: Lumen specific should be solved in a group prefix.
$router->get('/{route:.*}/', function () {
return view('app');
});

express js route matching url that doesn't start with a string

I want my route to match URL that doesn't start with "/api/"
For example, "/", "/home", "/profile" will trigger the route, but "/api/user/get" and "/api/xx" will not.
I have tried the following regular expression and its modifications:
app.get('/^(?!api)', function(req, res) {})
This kind of router organization is usually not solved with regex's in
the route matching, but rather with the concepts of routers and default
matching.
The router concept allows you to organize your routes into logical
units, such as a file that contains your API routes and file that
contains your other routes.
The default route matches routes that haven't already been matched.
So in your case you might have a main app that includes a router for the
API calls, a router for other calls, and then a default route that
matches everything else.
The default route is usually used to return a 404, but you could use it
to capture "all routes that don't begin with "/api", realizing that no
pages would 404.
Here's some working skeleton code to illustrate:
var express = require('express');
var app = express();
// The "non-API Router is likely to exist in a separate file
var nonAPIrouter = express.Router();
// More routes unrelated to APIs
//nonAPIRouter.get();
//////////////////////////////////////////
// The API router may be in it's own file, too.
var APIrouter = express.Router();
// Routes that start with /API
APIrouter.get('/api*', function (req, res) {
res.send('Hello API!');
});
///////////////////////////////////////
// The main app includes both routers.
app.use(APIrouter);
app.use(nonAPIrouter);
// Default route for requests not matched above
app.use(function(req, res) {
res.status(404).end('error');
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
Mark's answer pointed me to Router(), that helped me come up with a solution. It may not be elegant, but it works for me. I defined a middleware and do regex (I simplified to /a) there. If it's no match then do its job, otherwise pass over to /a/***
app.use(function(req, res, next) {
if (!req.url.match(/\/a\/*/g)) {
res.sendFile('index.html'));
}
else {
next();
}
});
var router = express.Router();
router.get(...)
router.post(...)
app.use('/a', router);

Ember-CLI-Mirage enforcing JSON:API?

Stumped on a couple failures and want to know if I'm understanding Mirage correctly:
1.In ember-cli-mirage, am I correct that the server response I define should reflect what my actual server is returning? For example:
this.get('/athletes', function(db, request) {
let athletes = db.athletes || [];
return {
athletes: athletes,
meta: { count: athletes.length }
}
});
I am using custom serializers and the above matches the format of my server response for a get request on this route, however, on two tests I'm getting two failures with this error: normalizeResponse must return a valid JSON API document: meta must be an object
2.Is mirage enforcing the json:api format, and is it doing so because of the way I'm setting up the tests?
For example, I have several tests that visit the above /athletes route, yet my failures occur when I use an async call like below. I would love to know the appropriate way to correctly overwrite the server response behavior, as well as why the normalizeResponse error appears in the console for 2 tests but only causes the one below to fail.
test('contact params not sent with request after clicking .showglobal', function(assert) {
assert.expect(2);
let done = assert.async();
server.createList('athlete', 10);
//perform a search, which shows all 10 athletes
visit('/athletes');
fillIn('.search-inner input', "c");
andThen(() => {
server.get('/athletes', (db, request) => {
assert.notOk(params.hasOwnProperty("contacts"));
done();
});
//get global athletes, which I thought would now be intercepted by the server.get call defined within the andThen block
click('button.showglobal');
});
});
Result:
✘ Error: Assertion Failed: normalizeResponse must return a valid JSON API document:
* meta must be an object
expected true
I tried changing my server response to a json:api format as suggested in the last example here but this looks nothing like my actual server response and causes my tests to fail since my app doesn't parse a payload with this structure. Any tips or advice must appreciated.
You are correct. Are the failures happening for the mock you've shown above? It looks to me like that would always return meta as an object, so verify the response is what you think it should be by looking in the console after the request is made.
If you'd like to see responses during a test, enter server.logging = true in your test:
test('I can view the photos', function() {
server.logging = true;
server.createList('photo', 10);
visit('/');
andThen(function() {
equal( find('img').length, 10 );
});
});
No, Mirage is agnostic about your particular backend, though it does come with some defaults. Again I would try enabling server.logging here to debug your tests.
Also, when writing asserts against the mock server, define the route handlers at the beginning of the test, as shown in the example from the docs.
I was able to get my second test to pass based on Sam's advice. My confusion was how to assert against the request params for a route that I have to visit and perform actions on. I was having to visit /athletes, click on different buttons, and each of these actions was sending separate requests (and params) to the /athletes route. That's is why I was trying to redefine the route handler within the andThen block (i.e. after I had already visited the route using the route definition in my mirage/config file).
Not in love with my solution, but the way I handled it was to move my assertion out of route handler and instead assign the value of the request to a top-level variable. That way, in my final andThen() block, I was able to assert against the last call to the /athletes route.
assert.expect(1);
//will get assigned the value of 'request' on each server call
let athletesRequest;
//override server response defined in mirage/config in order to
//capture and assert against request/response after user actions
server.get('athletes', (db, request) => {
let athletes = db.athletes || [];
athletesRequest = request;
return {
athletes: athletes,
meta: { count: athletes.length }
};
});
//sends request to /athletes
visit('/athletes');
andThen(() => {
//sends request to /athletes
fillIn('.search-inner input', "ab");
andThen(function() {
//sends (final) request to /athletes
click('button.search');
andThen(function() {
//asserts against /athletes request made on click('button.search') assert.notOk(athletesRequest.queryParams.hasOwnProperty("contact"));
});
});
});
I'm still getting console errors related to meta is not an object, but they are not preventing tests from passing. Using the server.logging = true allowed me to see that meta is indeed an object in all FakeServer responses.
Thanks again to Sam for the advice. server.logging = true and pauseTest() make acceptance tests a lot easier to troubleshoot.

How to set regular expression parameter constraints for Route::group in Laravel 4?

For simple routes I know I can user where statement. But what about parameters in Route::group() prefix
<?php
Route::get('user/{id}', 'UserController#profile')->where('id', '[0-9]+');
Route::group(['prefix' => 'foo/{bar}'], function() {
// ...
})->where('bar', '[0-9a-Z]+'); // this won't work
I'm using laravel 5.5. I had same problem and find this question in search.
I've tried to use the solution defined by #lukasgeiter and faced a problem:
The value of
$group->getRoutes()
was not only the routes of current group.
But I fixed my problem by specifying condition in route group definition.
Route::group([
'prefix' => 'foo/{bar}',
'where' => ['bar' => '[0-9a-Z]+']
],
function() {
// ...
});
And it worked for me :)
Out of the box the laravel router doesn't support this. You can use the Enhanced Router package from Jason Lewis or a fork that enables support for Laravel 4.2
Alternatively you can do it yourself. You could basically add the where condition to every route inside the group:
Route::group(['prefix' => 'foo/{bar}'], function() {
Route::get('/', function(){
// ...
})->where('bar', '[0-9a-Z]+');
});
Or do it a bit more dynamic and add this at the bottom of your route group:
Route::group(['prefix' => 'foo/{bar}'], function($group) {
// ...
foreach($group->getRoutes() as $route){
$route->where('bar', '[0-9a-Z]+');
}
});
One of possible not perfect solution in my view will be
// Route Model Binding
Route::model('user', 'User');
// Route Constraint Pattern
Route::pattern('user', '[0-9]+');
// Route Definition
Route::get('anything/{user}', 'UserController#anyFunction');
.
.
Route::resource('user', 'UsersController');

Incorrect base URL for Backbone DELETE requests - uses relative instead of absolute URLs

TL;DR version:
Building a Phonegap app using Backbone, and have a model called Client and a collection called Clients. Using a Tastypie API to communicate with a separate server. When I run fetch(), the URL uses the correct absolute URL (something like http://127.0.0.1:8000/api/v1/client/1/, but when I run Client.destroy(), it uses a relative URL of file:///api/v1/client/1/. How can I make it use the absolute URL for deleting the object?
Long version:
I'm building a mobile app with Backbone.js that consumes a Django/Tastypie API, and I've run into some seemingly odd behaviour that I can't figure out.
I define a base URL for the server at the top of the file:
// Set the base URL for querying the API
baseUrl = 'http://127.0.0.1:8000/api/v1/';
I have the following model and collection:
// Client model
Client = Backbone.Model.extend({
urlRoot: baseUrl + 'client',
// Default values
defaults: {
id: '',
name: '',
mobile: '',
email: '',
notes: '',
operator: '',
date_client_joined: '',
address: '',
postcode: ''
}
});
// Client collection
Clients = Backbone.Collection.extend({
// Will hold Client objects
model: Client,
// Set URL
url: baseUrl + 'client/'
});
And the individual clients are rendered in a list using the following view:
// Client list item view
ClientListItemView = Backbone.View.extend({
tagName: 'li',
events: {
'click .delete': 'deleteclient'
},
render: function () {
// Render the client list item template
var template = _.template($('#client-list-item-template').html());
this.$el.html(template(this.model.toJSON()));
// Return the object
return this;
},
deleteclient: function () {
this.model.destroy();
return false;
}
});
Now, the app actually uses jQuery Mobile and each client has a Delete button next to it with a class of delete, so the deleteclient function is executed each time one of these buttons is clicked. I'm also using backbone-tastypie to iron out the inconsistencies between Backbone and Tastypie.
The deleteclient function is running, but it sends the HTTP DELETE request to a relative URL of file:///api/v1/client/1/ (as this is a Phonegap app, I'm just viewing the files locally). From the documentation setting urlRoot manually seems like the way to go, but doing so didn't seem to solve the issue. Running the fetch() function to populate the collection works absolutely fine, though - it uses the correct absolute URL.
So, my question is how can I override the default behaviour and ensure my HTTP DELETE request is sent to the correct URL?
By looking at your code it should work ok. The Model in backbone already has a url() function defined which should do this:
url: function() {
var base = _.result(this, 'urlRoot') || _.result(this.collection, 'url') || urlError();
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id);
},
Can you use the debugger to see if it enters inside this code and what is the result of it? Mainly check the values from the _.result() calls...
Anyway, you can override the url property in your models rather than passing it in every call to destroy():
Client = Backbone.Model.extend({
url: function () { return baseUrl + 'client/' + this.id + '/'; }
// other code...
});
I found a solution, though I'm not entirely happy with it:
deleteclient: function () {
if (confirm('Are you sure you wish to delete this client?')) {
// Destroy the model
this.model.destroy({
url: baseUrl + 'client/' + this.model.get('id') + '/'
});
// Remove the view
this.remove();
}
}
Basically, if I explicitly pass through the URL to destroy(), that does the trick. It's a little annoying that I can't find a more DRY way to do this, so I'm open to any other method of doing the same thing.