1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-30 18:34:36 +01:00

HttpRequest::resource() returns current matched resource

This commit is contained in:
Nikolay Kim 2018-04-01 17:37:22 -07:00
parent b2e771df2c
commit 17c27ef42d
11 changed files with 191 additions and 106 deletions

View File

@ -9,6 +9,8 @@
* Use more ergonomic `actix_web::Error` instead of `http::Error` for `ClientRequestBuilder::body()`
* Add `HttpRequest::resource()`, returns current matched resource
* Router cannot parse Non-ASCII characters in URL #137
* Fix long client urls #129

View File

@ -68,6 +68,7 @@ smallvec = "0.6"
time = "0.1"
encoding = "0.2"
language-tags = "0.2"
lazy_static = "1.0"
url = { version="1.7", features=["query_encoding"] }
cookie = { version="0.10", features=["percent-encode"] }
brotli2 = { version="^0.3.2", optional = true }

View File

@ -34,16 +34,14 @@ use db::{CreateUser, DbExecutor};
/// State with DbExecutor address
struct State {
struct App {
db: Addr<Syn, DbExecutor>,
}
/// Async request handler
fn index(req: HttpRequest<State>) -> Box<Future<Item=HttpResponse, Error=Error>> {
let name = &req.match_info()["name"];
fn index(name: Path<(String,)>, state: State<App>) -> FutureResponse<HttpResponse> {
// send async `CreateUser` message to a `DbExecutor`
req.state().db.send(CreateUser{name: name.to_owned()})
state.db.send(CreateUser{name: name.into_inner()})
.from_err()
.and_then(|res| {
match res {
@ -72,7 +70,7 @@ fn main() {
App::with_state(State{db: addr.clone()})
// enable logger
.middleware(middleware::Logger::default())
.resource("/{name}", |r| r.method(http::Method::GET).a(index))})
.resource("/{name}", |r| r.method(http::Method::GET).with2(index))})
.bind("127.0.0.1:8080").unwrap()
.start();

View File

@ -4,8 +4,8 @@ use std::cell::RefCell;
use std::collections::HashMap;
use handler::Reply;
use router::{Router, Pattern};
use resource::Resource;
use router::{Router, Resource};
use resource::{ResourceHandler};
use header::ContentEncoding;
use handler::{Handler, RouteHandler, WrapHandler};
use httprequest::HttpRequest;
@ -27,10 +27,10 @@ pub struct HttpApplication<S=()> {
pub(crate) struct Inner<S> {
prefix: usize,
default: Resource<S>,
default: ResourceHandler<S>,
encoding: ContentEncoding,
router: Router,
resources: Vec<Resource<S>>,
resources: Vec<ResourceHandler<S>>,
handlers: Vec<(String, Box<RouteHandler<S>>)>,
}
@ -103,10 +103,10 @@ struct ApplicationParts<S> {
state: S,
prefix: String,
settings: ServerSettings,
default: Resource<S>,
resources: Vec<(Pattern, Option<Resource<S>>)>,
default: ResourceHandler<S>,
resources: Vec<(Resource, Option<ResourceHandler<S>>)>,
handlers: Vec<(String, Box<RouteHandler<S>>)>,
external: HashMap<String, Pattern>,
external: HashMap<String, Resource>,
encoding: ContentEncoding,
middlewares: Vec<Box<Middleware<S>>>,
}
@ -126,7 +126,7 @@ impl App<()> {
state: (),
prefix: "/".to_owned(),
settings: ServerSettings::default(),
default: Resource::default_not_found(),
default: ResourceHandler::default_not_found(),
resources: Vec::new(),
handlers: Vec::new(),
external: HashMap::new(),
@ -156,7 +156,7 @@ impl<S> App<S> where S: 'static {
state,
prefix: "/".to_owned(),
settings: ServerSettings::default(),
default: Resource::default_not_found(),
default: ResourceHandler::default_not_found(),
resources: Vec::new(),
handlers: Vec::new(),
external: HashMap::new(),
@ -236,16 +236,16 @@ impl<S> App<S> where S: 'static {
/// }
/// ```
pub fn resource<F>(mut self, path: &str, f: F) -> App<S>
where F: FnOnce(&mut Resource<S>) + 'static
where F: FnOnce(&mut ResourceHandler<S>) + 'static
{
{
let parts = self.parts.as_mut().expect("Use after finish");
// add resource
let mut resource = Resource::default();
let mut resource = ResourceHandler::default();
f(&mut resource);
let pattern = Pattern::new(resource.get_name(), path);
let pattern = Resource::new(resource.get_name(), path);
parts.resources.push((pattern, Some(resource)));
}
self
@ -253,7 +253,7 @@ impl<S> App<S> where S: 'static {
/// Default resource is used if no matched route could be found.
pub fn default_resource<F>(mut self, f: F) -> App<S>
where F: FnOnce(&mut Resource<S>) + 'static
where F: FnOnce(&mut ResourceHandler<S>) + 'static
{
{
let parts = self.parts.as_mut().expect("Use after finish");
@ -305,7 +305,8 @@ impl<S> App<S> where S: 'static {
panic!("External resource {:?} is registered.", name.as_ref());
}
parts.external.insert(
String::from(name.as_ref()), Pattern::new(name.as_ref(), url.as_ref()));
String::from(name.as_ref()),
Resource::external(name.as_ref(), url.as_ref()));
}
self
}

View File

@ -554,8 +554,8 @@ impl<'de> de::VariantAccess<'de> for UnitVariant {
mod tests {
use futures::{Async, Future};
use super::*;
use router::{Router, Pattern};
use resource::Resource;
use router::{Router, Resource};
use resource::ResourceHandler;
use test::TestRequest;
use server::ServerSettings;
@ -580,10 +580,10 @@ mod tests {
fn test_request_extract() {
let mut req = TestRequest::with_uri("/name/user1/?id=test").finish();
let mut resource = Resource::<()>::default();
let mut resource = ResourceHandler::<()>::default();
resource.name("index");
let mut routes = Vec::new();
routes.push((Pattern::new("index", "/{key}/{value}/"), Some(resource)));
routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource)));
let (router, _) = Router::new("", ServerSettings::default(), routes);
assert!(router.recognize(&mut req).is_some());
@ -639,10 +639,10 @@ mod tests {
#[test]
fn test_extract_path_signle() {
let mut resource = Resource::<()>::default();
let mut resource = ResourceHandler::<()>::default();
resource.name("index");
let mut routes = Vec::new();
routes.push((Pattern::new("index", "/{value}/"), Some(resource)));
routes.push((Resource::new("index", "/{value}/"), Some(resource)));
let (router, _) = Router::new("", ServerSettings::default(), routes);
let mut req = TestRequest::with_uri("/32/").finish();

View File

@ -17,7 +17,7 @@ use percent_encoding::percent_decode;
use body::Body;
use info::ConnectionInfo;
use param::Params;
use router::Router;
use router::{Router, Resource};
use payload::Payload;
use handler::FromRequest;
use httpmessage::HttpMessage;
@ -39,6 +39,7 @@ pub struct HttpInnerMessage {
pub addr: Option<SocketAddr>,
pub payload: Option<Payload>,
pub info: Option<ConnectionInfo<'static>>,
pub resource: i16,
}
impl Default for HttpInnerMessage {
@ -57,6 +58,7 @@ impl Default for HttpInnerMessage {
payload: None,
extensions: Extensions::new(),
info: None,
resource: -1,
}
}
}
@ -93,9 +95,15 @@ impl HttpInnerMessage {
self.addr = None;
self.info = None;
self.payload = None;
self.resource = -1;
}
}
lazy_static!{
static ref RESOURCE: Resource = Resource::default();
}
/// An HTTP Request
pub struct HttpRequest<S=()>(SharedHttpInnerMessage, Option<Rc<S>>, Option<Router>);
@ -120,6 +128,7 @@ impl HttpRequest<()> {
addr: None,
extensions: Extensions::new(),
info: None,
resource: -1,
}),
None,
None,
@ -318,6 +327,22 @@ impl<S> HttpRequest<S> {
self.2.as_ref()
}
/// This method returns reference to matched `Resource` object.
#[inline]
pub fn resource(&self) -> &Resource {
let idx = self.as_ref().resource;
if idx >= 0 {
if let Some(ref router) = self.2 {
return router.get_resource(idx as usize)
}
}
&*RESOURCE
}
pub(crate) fn set_resource(&mut self, idx: usize) {
self.as_mut().resource = idx as i16;
}
/// Peer socket address
///
/// Peer address is actual socket address, if proxy is used in front of
@ -544,8 +569,8 @@ impl<S> fmt::Debug for HttpRequest<S> {
mod tests {
use super::*;
use http::{Uri, HttpTryFrom};
use router::Pattern;
use resource::Resource;
use router::Resource;
use resource::ResourceHandler;
use test::TestRequest;
use server::ServerSettings;
@ -607,10 +632,10 @@ mod tests {
fn test_request_match_info() {
let mut req = TestRequest::with_uri("/value/?id=test").finish();
let mut resource = Resource::<()>::default();
let mut resource = ResourceHandler::<()>::default();
resource.name("index");
let mut routes = Vec::new();
routes.push((Pattern::new("index", "/{key}/"), Some(resource)));
routes.push((Resource::new("index", "/{key}/"), Some(resource)));
let (router, _) = Router::new("", ServerSettings::default(), routes);
assert!(router.recognize(&mut req).is_some());
@ -623,9 +648,9 @@ mod tests {
assert_eq!(req2.url_for("unknown", &["test"]),
Err(UrlGenerationError::RouterNotAvailable));
let mut resource = Resource::<()>::default();
let mut resource = ResourceHandler::<()>::default();
resource.name("index");
let routes = vec!((Pattern::new("index", "/user/{name}.{ext}"), Some(resource)));
let routes = vec!((Resource::new("index", "/user/{name}.{ext}"), Some(resource)));
let (router, _) = Router::new("/", ServerSettings::default(), routes);
assert!(router.has_route("/user/test.html"));
assert!(!router.has_route("/test/unknown"));
@ -645,26 +670,27 @@ mod tests {
fn test_url_for_with_prefix() {
let req = TestRequest::with_header(header::HOST, "www.rust-lang.org").finish();
let mut resource = Resource::<()>::default();
let mut resource = ResourceHandler::<()>::default();
resource.name("index");
let routes = vec![(Pattern::new("index", "/user/{name}.{ext}"), Some(resource))];
let routes = vec![(Resource::new("index", "/user/{name}.{ext}"), Some(resource))];
let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes);
assert!(router.has_route("/user/test.html"));
assert!(!router.has_route("/prefix/user/test.html"));
let req = req.with_state(Rc::new(()), router);
let url = req.url_for("index", &["test", "html"]);
assert_eq!(url.ok().unwrap().as_str(), "http://www.rust-lang.org/prefix/user/test.html");
assert_eq!(url.ok().unwrap().as_str(),
"http://www.rust-lang.org/prefix/user/test.html");
}
#[test]
fn test_url_for_external() {
let req = HttpRequest::default();
let mut resource = Resource::<()>::default();
let mut resource = ResourceHandler::<()>::default();
resource.name("index");
let routes = vec![
(Pattern::new("youtube", "https://youtube.com/watch/{video_id}"), None)];
(Resource::external("youtube", "https://youtube.com/watch/{video_id}"), None)];
let (router, _) = Router::new::<()>("", ServerSettings::default(), routes);
assert!(!router.has_route("https://youtube.com/watch/unknown"));

View File

@ -60,6 +60,8 @@ extern crate bitflags;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate futures;
extern crate futures_cpupool;
extern crate tokio_io;
@ -174,12 +176,12 @@ pub mod dev {
pub use body::BodyStream;
pub use context::Drain;
pub use json::JsonBody;
pub use info::ConnectionInfo;
pub use handler::{Handler, Reply, FromRequest};
pub use route::Route;
pub use resource::Resource;
pub use json::JsonBody;
pub use router::{Router, Pattern};
pub use router::{Router, Resource};
pub use resource::ResourceHandler;
pub use param::{FromParam, Params};
pub use httpmessage::{UrlEncoded, MessageBody};
pub use httpresponse::HttpResponseBuilder;

View File

@ -10,7 +10,7 @@
//! 3. Call [finish](struct.Cors.html#method.finish) to retrieve the constructed backend.
//!
//! Cors middleware could be used as parameter for `App::middleware()` or
//! `Resource::middleware()` methods. But you have to use `Cors::register()` method to
//! `ResourceHandler::middleware()` methods. But you have to use `Cors::register()` method to
//! support *preflight* OPTIONS request.
//!
//!
@ -52,7 +52,7 @@ use http::{self, Method, HttpTryFrom, Uri, StatusCode};
use http::header::{self, HeaderName, HeaderValue};
use error::{Result, ResponseError};
use resource::Resource;
use resource::ResourceHandler;
use httpmessage::HttpMessage;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
@ -212,10 +212,10 @@ impl Cors {
/// This method register cors middleware with resource and
/// adds route for *OPTIONS* preflight requests.
///
/// It is possible to register *Cors* middleware with `Resource::middleware()`
/// It is possible to register *Cors* middleware with `ResourceHandler::middleware()`
/// method, but in that case *Cors* middleware wont be able to handle *OPTIONS*
/// requests.
pub fn register<S: 'static>(self, resource: &mut Resource<S>) {
pub fn register<S: 'static>(self, resource: &mut ResourceHandler<S>) {
resource.method(Method::OPTIONS).h(|_| HttpResponse::Ok());
resource.middleware(self);
}

View File

@ -31,16 +31,16 @@ use httpresponse::HttpResponse;
/// "/", |r| r.method(http::Method::GET).f(|r| HttpResponse::Ok()))
/// .finish();
/// }
pub struct Resource<S=()> {
pub struct ResourceHandler<S=()> {
name: String,
state: PhantomData<S>,
routes: SmallVec<[Route<S>; 3]>,
middlewares: Rc<Vec<Box<Middleware<S>>>>,
}
impl<S> Default for Resource<S> {
impl<S> Default for ResourceHandler<S> {
fn default() -> Self {
Resource {
ResourceHandler {
name: String::new(),
state: PhantomData,
routes: SmallVec::new(),
@ -48,10 +48,10 @@ impl<S> Default for Resource<S> {
}
}
impl<S> Resource<S> {
impl<S> ResourceHandler<S> {
pub(crate) fn default_not_found() -> Self {
Resource {
ResourceHandler {
name: String::new(),
state: PhantomData,
routes: SmallVec::new(),
@ -68,7 +68,7 @@ impl<S> Resource<S> {
}
}
impl<S: 'static> Resource<S> {
impl<S: 'static> ResourceHandler<S> {
/// Register a new route and return mutable reference to *Route* object.
/// *Route* is used for route configuration, i.e. adding predicates, setting up handler.
@ -97,7 +97,7 @@ impl<S: 'static> Resource<S> {
/// This is shortcut for:
///
/// ```rust,ignore
/// Resource::resource("/", |r| r.route().filter(pred::Get()).f(index)
/// Application::resource("/", |r| r.route().filter(pred::Get()).f(index)
/// ```
pub fn method(&mut self, method: Method) -> &mut Route<S> {
self.routes.push(Route::default());
@ -109,7 +109,7 @@ impl<S: 'static> Resource<S> {
/// This is shortcut for:
///
/// ```rust,ignore
/// Resource::resource("/", |r| r.route().h(handler)
/// Application::resource("/", |r| r.route().h(handler)
/// ```
pub fn h<H: Handler<S>>(&mut self, handler: H) {
self.routes.push(Route::default());
@ -121,7 +121,7 @@ impl<S: 'static> Resource<S> {
/// This is shortcut for:
///
/// ```rust,ignore
/// Resource::resource("/", |r| r.route().f(index)
/// Application::resource("/", |r| r.route().f(index)
/// ```
pub fn f<F, R>(&mut self, handler: F)
where F: Fn(HttpRequest<S>) -> R + 'static,
@ -136,7 +136,7 @@ impl<S: 'static> Resource<S> {
/// This is shortcut for:
///
/// ```rust,ignore
/// Resource::resource("/", |r| r.route().with(index)
/// Application::resource("/", |r| r.route().with(index)
/// ```
pub fn with<T, F, R>(&mut self, handler: F)
where F: Fn(T) -> R + 'static,
@ -147,7 +147,7 @@ impl<S: 'static> Resource<S> {
self.routes.last_mut().unwrap().with(handler)
}
/// Register a middleware
/// Register a resource middleware
///
/// This is similar to `App's` middlewares, but
/// middlewares get invoked on resource level.
@ -157,7 +157,7 @@ impl<S: 'static> Resource<S> {
pub(crate) fn handle(&mut self,
mut req: HttpRequest<S>,
default: Option<&mut Resource<S>>) -> Reply
default: Option<&mut ResourceHandler<S>>) -> Reply
{
for route in &mut self.routes {
if route.check(&mut req) {

View File

@ -6,9 +6,9 @@ use std::collections::HashMap;
use regex::{Regex, escape};
use percent_encoding::percent_decode;
use error::UrlGenerationError;
use param::Params;
use resource::Resource;
use error::UrlGenerationError;
use resource::ResourceHandler;
use httprequest::HttpRequest;
use server::ServerSettings;
@ -19,8 +19,8 @@ pub struct Router(Rc<Inner>);
struct Inner {
prefix: String,
prefix_len: usize,
named: HashMap<String, (Pattern, bool)>,
patterns: Vec<Pattern>,
named: HashMap<String, (Resource, bool)>,
patterns: Vec<Resource>,
srv: ServerSettings,
}
@ -28,7 +28,8 @@ impl Router {
/// Create new router
pub fn new<S>(prefix: &str,
settings: ServerSettings,
map: Vec<(Pattern, Option<Resource<S>>)>) -> (Router, Vec<Resource<S>>)
map: Vec<(Resource, Option<ResourceHandler<S>>)>)
-> (Router, Vec<ResourceHandler<S>>)
{
let prefix = prefix.trim().trim_right_matches('/').to_owned();
let mut named = HashMap::new();
@ -64,6 +65,10 @@ impl Router {
&self.0.srv
}
pub(crate) fn get_resource(&self, idx: usize) -> &Resource {
&self.0.patterns[idx]
}
/// Query for matched resource
pub fn recognize<S>(&self, req: &mut HttpRequest<S>) -> Option<usize> {
if self.0.prefix_len > req.path().len() {
@ -75,6 +80,7 @@ impl Router {
for (idx, pattern) in self.0.patterns.iter().enumerate() {
if pattern.match_with_params(p.as_ref(), req.match_info_mut()) {
req.set_resource(idx);
return Some(idx)
}
}
@ -108,11 +114,7 @@ impl Router {
I: AsRef<str>,
{
if let Some(pattern) = self.0.named.get(name) {
if pattern.1 {
pattern.0.path(None, elements)
} else {
pattern.0.path(Some(&self.0.prefix), elements)
}
pattern.0.resource_path(self, elements)
} else {
Err(UrlGenerationError::ResourceNotFound)
}
@ -125,6 +127,7 @@ impl Clone for Router {
}
}
#[derive(Debug, Clone, PartialEq)]
enum PatternElement {
Str(String),
@ -137,25 +140,48 @@ enum PatternType {
Dynamic(Regex, Vec<String>),
}
/// Reslource type describes an entry in resources table
#[derive(Clone)]
pub struct Pattern {
pub struct Resource {
tp: PatternType,
name: String,
pattern: String,
elements: Vec<PatternElement>,
external: bool,
}
impl Pattern {
/// Parse path pattern and create new `Pattern` instance.
impl Default for Resource {
fn default() -> Resource {
Resource {
tp: PatternType::Static("".to_owned()),
name: "".to_owned(),
pattern: "".to_owned(),
elements: Vec::new(),
external: false,
}
}
}
impl Resource {
/// Parse path pattern and create new `Resource` instance.
///
/// Panics if path pattern is wrong.
pub fn new(name: &str, path: &str) -> Self {
Pattern::with_prefix(name, path, "/")
Resource::with_prefix(name, path, "/")
}
/// Parse path pattern and create new `Pattern` instance with custom prefix
/// Construct external resource
///
/// Panics if path pattern is wrong.
pub fn external(name: &str, path: &str) -> Self {
let mut resource = Resource::with_prefix(name, path, "/");
resource.external = true;
resource
}
/// Parse path pattern and create new `Resource` instance with custom prefix
pub fn with_prefix(name: &str, path: &str, prefix: &str) -> Self {
let (pattern, elements, is_dynamic) = Pattern::parse(path, prefix);
let (pattern, elements, is_dynamic) = Resource::parse(path, prefix);
let tp = if is_dynamic {
let re = match Regex::new(&pattern) {
@ -170,20 +196,21 @@ impl Pattern {
PatternType::Static(pattern.clone())
};
Pattern {
Resource {
tp,
pattern,
elements,
name: name.into(),
pattern: path.to_owned(),
external: false,
}
}
/// Returns name of the pattern
/// Name of the resource
pub fn name(&self) -> &str {
&self.name
}
/// Returns path of the pattern
/// Path pattern of the resource
pub fn pattern(&self) -> &str {
&self.pattern
}
@ -219,14 +246,15 @@ impl Pattern {
}
}
/// Build pattern path.
pub fn path<U, I>(&self, prefix: Option<&str>, elements: U) -> Result<String, UrlGenerationError>
/// Build reousrce path.
pub fn resource_path<U, I>(&self, router: &Router, elements: U)
-> Result<String, UrlGenerationError>
where U: IntoIterator<Item=I>,
I: AsRef<str>,
{
let mut iter = elements.into_iter();
let mut path = if let Some(prefix) = prefix {
format!("{}/", prefix)
let mut path = if !self.external {
format!("{}/", router.prefix())
} else {
String::new()
};
@ -309,15 +337,15 @@ impl Pattern {
}
}
impl PartialEq for Pattern {
fn eq(&self, other: &Pattern) -> bool {
impl PartialEq for Resource {
fn eq(&self, other: &Resource) -> bool {
self.pattern == other.pattern
}
}
impl Eq for Pattern {}
impl Eq for Resource {}
impl Hash for Pattern {
impl Hash for Resource {
fn hash<H: Hasher>(&self, state: &mut H) {
self.pattern.hash(state);
}
@ -331,13 +359,20 @@ mod tests {
#[test]
fn test_recognizer() {
let routes = vec![
(Pattern::new("", "/name"), Some(Resource::default())),
(Pattern::new("", "/name/{val}"), Some(Resource::default())),
(Pattern::new("", "/name/{val}/index.html"), Some(Resource::default())),
(Pattern::new("", "/file/{file}.{ext}"), Some(Resource::default())),
(Pattern::new("", "/v{val}/{val2}/index.html"), Some(Resource::default())),
(Pattern::new("", "/v/{tail:.*}"), Some(Resource::default())),
(Pattern::new("", "{test}/index.html"), Some(Resource::default()))];
(Resource::new("", "/name"),
Some(ResourceHandler::default())),
(Resource::new("", "/name/{val}"),
Some(ResourceHandler::default())),
(Resource::new("", "/name/{val}/index.html"),
Some(ResourceHandler::default())),
(Resource::new("", "/file/{file}.{ext}"),
Some(ResourceHandler::default())),
(Resource::new("", "/v{val}/{val2}/index.html"),
Some(ResourceHandler::default())),
(Resource::new("", "/v/{tail:.*}"),
Some(ResourceHandler::default())),
(Resource::new("", "{test}/index.html"),
Some(ResourceHandler::default()))];
let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes);
let mut req = TestRequest::with_uri("/name").finish();
@ -375,8 +410,8 @@ mod tests {
#[test]
fn test_recognizer_2() {
let routes = vec![
(Pattern::new("", "/index.json"), Some(Resource::default())),
(Pattern::new("", "/{source}.json"), Some(Resource::default()))];
(Resource::new("", "/index.json"), Some(ResourceHandler::default())),
(Resource::new("", "/{source}.json"), Some(ResourceHandler::default()))];
let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes);
let mut req = TestRequest::with_uri("/index.json").finish();
@ -389,8 +424,8 @@ mod tests {
#[test]
fn test_recognizer_with_prefix() {
let routes = vec![
(Pattern::new("", "/name"), Some(Resource::default())),
(Pattern::new("", "/name/{val}"), Some(Resource::default()))];
(Resource::new("", "/name"), Some(ResourceHandler::default())),
(Resource::new("", "/name/{val}"), Some(ResourceHandler::default()))];
let (rec, _) = Router::new::<()>("/test", ServerSettings::default(), routes);
let mut req = TestRequest::with_uri("/name").finish();
@ -406,8 +441,8 @@ mod tests {
// same patterns
let routes = vec![
(Pattern::new("", "/name"), Some(Resource::default())),
(Pattern::new("", "/name/{val}"), Some(Resource::default()))];
(Resource::new("", "/name"), Some(ResourceHandler::default())),
(Resource::new("", "/name/{val}"), Some(ResourceHandler::default()))];
let (rec, _) = Router::new::<()>("/test2", ServerSettings::default(), routes);
let mut req = TestRequest::with_uri("/name").finish();
@ -423,22 +458,22 @@ mod tests {
#[test]
fn test_parse_static() {
let re = Pattern::new("test", "/");
let re = Resource::new("test", "/");
assert!(re.is_match("/"));
assert!(!re.is_match("/a"));
let re = Pattern::new("test", "/name");
let re = Resource::new("test", "/name");
assert!(re.is_match("/name"));
assert!(!re.is_match("/name1"));
assert!(!re.is_match("/name/"));
assert!(!re.is_match("/name~"));
let re = Pattern::new("test", "/name/");
let re = Resource::new("test", "/name/");
assert!(re.is_match("/name/"));
assert!(!re.is_match("/name"));
assert!(!re.is_match("/name/gs"));
let re = Pattern::new("test", "/user/profile");
let re = Resource::new("test", "/user/profile");
assert!(re.is_match("/user/profile"));
assert!(!re.is_match("/user/profile/profile"));
}
@ -447,7 +482,7 @@ mod tests {
fn test_parse_param() {
let mut req = HttpRequest::default();
let re = Pattern::new("test", "/user/{id}");
let re = Resource::new("test", "/user/{id}");
assert!(re.is_match("/user/profile"));
assert!(re.is_match("/user/2345"));
assert!(!re.is_match("/user/2345/"));
@ -461,7 +496,7 @@ mod tests {
assert!(re.match_with_params("/user/1245125", req.match_info_mut()));
assert_eq!(req.match_info().get("id").unwrap(), "1245125");
let re = Pattern::new("test", "/v{version}/resource/{id}");
let re = Resource::new("test", "/v{version}/resource/{id}");
assert!(re.is_match("/v1/resource/320120"));
assert!(!re.is_match("/v/resource/1"));
assert!(!re.is_match("/resource"));
@ -471,4 +506,24 @@ mod tests {
assert_eq!(req.match_info().get("version").unwrap(), "151");
assert_eq!(req.match_info().get("id").unwrap(), "adahg32");
}
#[test]
fn test_request_resource() {
let routes = vec![
(Resource::new("r1", "/index.json"), Some(ResourceHandler::default())),
(Resource::new("r2", "/test.json"), Some(ResourceHandler::default()))];
let (router, _) = Router::new::<()>("", ServerSettings::default(), routes);
let mut req = TestRequest::with_uri("/index.json")
.finish_with_router(router.clone());
assert_eq!(router.recognize(&mut req), Some(0));
let resource = req.resource();
assert_eq!(resource.name(), "r1");
let mut req = TestRequest::with_uri("/test.json")
.finish_with_router(router.clone());
assert_eq!(router.recognize(&mut req), Some(1));
let resource = req.resource();
assert_eq!(resource.name(), "r2");
}
}

View File

@ -27,7 +27,7 @@ use application::{App, HttpApplication};
use param::Params;
use router::Router;
use payload::Payload;
use resource::Resource;
use resource::ResourceHandler;
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use server::{HttpServer, IntoHttpHandler, ServerSettings};
@ -352,7 +352,7 @@ impl<S: 'static> TestApp<S> {
/// Register resource. This method is similar
/// to `App::resource()` method.
pub fn resource<F>(&mut self, path: &str, f: F) -> &mut TestApp<S>
where F: FnOnce(&mut Resource<S>) + 'static
where F: FnOnce(&mut ResourceHandler<S>) + 'static
{
self.app = Some(self.app.take().unwrap().resource(path, f));
self