$ location / การสลับระหว่าง html5 และ hashbang mode / link rewriting


179

ฉันรู้สึกว่า Angular จะเขียน URL ที่ปรากฏในแอตทริบิวต์ href ของ anchor tags ภายใน tempaltes เพื่อให้ทำงานได้ไม่ว่าจะอยู่ในโหมด html5 หรือโหมด hashbang เอกสารสำหรับการให้บริการสถานที่ดูเหมือนว่าจะบอกว่า HTML การเชื่อมโยงการเขียนใหม่จะดูแลสถานการณ์ hashbang ฉันคาดหวังว่าเมื่อไม่ได้อยู่ในโหมด HTML5 จะมีการแทรกแฮชและในโหมด HTML5 พวกเขาจะไม่

อย่างไรก็ตามดูเหมือนว่าไม่มีการเขียนใหม่เกิดขึ้น ตัวอย่างต่อไปนี้ไม่อนุญาตให้ฉันเพียงแค่เปลี่ยนโหมด ลิงก์ทั้งหมดในแอปพลิเคชันจะต้องเขียนใหม่ด้วยมือ (หรือได้มาจากตัวแปรตอนรันไทม์ฉันต้องเขียน URL ทั้งหมดด้วยตนเองขึ้นอยู่กับโหมดหรือไม่?

ฉันไม่เห็นการเขียน url ฝั่งไคลเอ็นต์ที่เกิดขึ้นใน Angular 1.0.6, 1.1.4 หรือ 1.1.3 ดูเหมือนว่าค่า href ทั้งหมดจะต้องได้รับการเติมด้วย # / สำหรับโหมด hashbang และ / สำหรับโหมด html5

จำเป็นต้องกำหนดค่าบางอย่างเพื่อทำให้เกิดการเขียนใหม่หรือไม่? ฉันอ่านเอกสารผิดหรือเปล่า? ทำอย่างอื่นที่โง่?

นี่คือตัวอย่างเล็ก ๆ :

<head>
    <script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.1.3/angular.js"></script>
</head>

<body>
    <div ng-view></div>
    <script>
        angular.module('sample', [])
            .config(
        ['$routeProvider', '$locationProvider',
            function ($routeProvider, $locationProvider) {

                //commenting out this line (switching to hashbang mode) breaks the app
                //-- unless # is added to the templates
                $locationProvider.html5Mode(true);

                $routeProvider.when('/', {
                    template: 'this is home. go to <a href="https://stackoverflow.com/about"/>about</a>'
                });
                $routeProvider.when('/about', {
                    template: 'this is about. go to <a href="https://stackoverflow.com/"/>home</a'
                });
            }
        ])
            .run();
    </script>
</body>

ภาคผนวก: ในการอ่านคำถามของฉันอีกครั้งฉันเห็นว่าฉันใช้คำว่า "เขียนใหม่" โดยไม่มีความชัดเจนว่าใครและเมื่อไหร่ที่ฉันต้องการจะเขียนใหม่ คำถามคือเกี่ยวกับวิธีการให้Angularเขียน URL ใหม่เมื่อแสดงเส้นทางและวิธีตีความเส้นทางในรหัส JS อย่างสม่ำเสมอทั้งสองโหมด มันไม่เกี่ยวกับวิธีทำให้เว็บเซิร์ฟเวอร์ทำการเขียนคำร้องขอที่เข้ากันได้กับ HTML5


1
นี่คือวิธีการแก้สำหรับเชิงมุม 1.6
Mistalis

คำตอบ:


361

เอกสารไม่ชัดเจนเกี่ยวกับการกำหนดเส้นทาง AngularJS พูดถึงโหมด Hashbang และ HTML5 ในความเป็นจริงการกำหนดเส้นทาง AngularJS ทำงานในสามโหมด:

  • โหมด Hashbang
  • โหมด HTML5
  • Hashbang ในโหมด HTML5

สำหรับแต่ละโหมดจะมีคลาส LocationUrl ตามลำดับ (LocationHashbangUrl, LocationUrl และ LocationHashbangInHTML5Url)

ในการจำลองการเขียน URL ใหม่คุณต้องตั้งค่า html5mode เป็น true และตกแต่งคลาส $ sniffer ดังนี้:

$provide.decorator('$sniffer', function($delegate) {
  $delegate.history = false;
  return $delegate;
});

ตอนนี้ฉันจะอธิบายในรายละเอียดเพิ่มเติม:

โหมด Hashbang

การกำหนดค่า:

$routeProvider
  .when('/path', {
    templateUrl: 'path.html',
});
$locationProvider
  .html5Mode(false)
  .hashPrefix('!');

เป็นกรณีนี้เมื่อคุณจำเป็นต้องใช้ URL ที่มีแฮชในไฟล์ HTML ของคุณเช่น

<a href="index.html#!/path">link</a>

ในเบราว์เซอร์คุณต้องใช้ลิงค์ต่อไปนี้: http://www.example.com/base/index.html#!/base/path

ตามที่คุณเห็นในโหมด Hashbang ล้วนลิงก์ทั้งหมดในไฟล์ HTML จะต้องเริ่มต้นด้วยฐานเช่น "index.html #!"

โหมด HTML5

การกำหนดค่า:

$routeProvider
  .when('/path', {
    templateUrl: 'path.html',
  });
$locationProvider
  .html5Mode(true);

คุณควรตั้งฐานในไฟล์ HTML

<html>
  <head>
    <base href="/">
  </head>
</html>

ในโหมดนี้คุณสามารถใช้ลิงก์โดยไม่มี # ในไฟล์ HTML

<a href="/path">link</a>

ลิงก์ในเบราว์เซอร์:

http://www.example.com/base/path

Hashbang ในโหมด HTML5

โหมดนี้เปิดใช้งานเมื่อเราใช้โหมด HTML5 จริง ๆ แต่ในเบราว์เซอร์ที่เข้ากันไม่ได้ เราสามารถจำลองโหมดนี้ในเบราว์เซอร์ที่เข้ากันได้โดยตกแต่งบริการ $ ดมกลิ่นและตั้งค่าประวัติเป็นเท็จ

การกำหนดค่า:

$provide.decorator('$sniffer', function($delegate) {
  $delegate.history = false;
  return $delegate;
});
$routeProvider
  .when('/path', {
    templateUrl: 'path.html',
  });
$locationProvider
  .html5Mode(true)
  .hashPrefix('!');

ตั้งฐานในไฟล์ HTML:

<html>
  <head>
    <base href="/">
  </head>
</html>

ในกรณีนี้ลิงก์สามารถเขียนได้โดยไม่มีแฮชในไฟล์ HTML

<a href="/path">link</a>

ลิงก์ในเบราว์เซอร์:

http://www.example.com/index.html#!/base/path

ขอบคุณสำหรับคำอธิบายโดยละเอียด @jupiter ฉันจะดูว่าฉันสามารถข้ามปังและเก็บแฮชและหลอกให้ Angular ไม่ต้องการ URL สองชุดขึ้นอยู่กับโหมด!
laurelnaiad

1
ฉันคิดว่าฉันไม่เข้าใจปัญหาของคุณอย่างเหมาะสม ทำไมคุณไม่ใช้แค่ URL โดยไม่มีแฮช พวกเขาจะทำงานในเบราว์เซอร์ที่สนับสนุน API ประวัติและเบราว์เซอร์ที่ไม่สนับสนุน API ประวัติ AngularJS จะใส่ # version ลงในแถบตำแหน่งเมื่อคุณคลิกที่เบราว์เซอร์ที่ไม่รองรับ API ประวัติเนื่องจาก AngularJS สกัดกั้นการคลิกที่ลิงก์
ดาวพฤหัสบดี

ฉันกำลังทำงานกับเฟรมเวิร์กที่ควรรองรับทั้งสองโหมด ผู้เขียนแอปควรเลือกอย่างใดอย่างหนึ่งโดยไม่ต้องกังวลว่ามีแฮชในเทมเพลตและ / หรือการเปลี่ยนแปลงในการตีความเส้นทางที่สัมพันธ์กันหรือไม่ ฉันหวังว่าเคล็ดลับของคุณจะช่วยให้เป็นจริงได้ว่า "สิ่งที่คุณทำคือเปลี่ยนโหมด" (แม้ว่าวิธีแก้ปัญหาในทางปฏิบัติคือ "ตั้งค่าโหมดเป็น html5 จากนั้นก็โกหกเชิงมุมเกี่ยวกับความสามารถของเบราว์เซอร์")
laurelnaiad

1
ฉันจะได้รับ$provideไม่ได้กำหนด?
Petrus Theron

1
@pate - คุณจำเป็นต้องใส่คำสั่ง $ ให้ในฟังก์ชั่นปรับแต่งของคุณเมื่อคุณติดตั้งมัณฑนากร
laurelnaiad

8

ผู้อ่านที่ไม่มีอนาคตถ้าคุณใช้Angular 1.6คุณต้องเปลี่ยนhashPrefix:

appModule.config(['$locationProvider', function($locationProvider) {
    $locationProvider.html5Mode(true);
    $locationProvider.hashPrefix('');
}]);

อย่าลืมตั้งค่าฐานใน HTML ของคุณ<head>:

<head>
    <base href="/">
    ...
</head>

hereข้อมูลเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลง


3
ขอบคุณ @Malalis คำตอบของคุณทำงานได้ดี แต่ปัญหาเมื่อฉันรีเฟรชหน้าหลังจากการกำหนดเส้นทางหน้าไม่พบข้อผิดพลาดเกิดขึ้น โปรดช่วยฉัน ..
Ashish Mehta

@AshishMehta สวัสดี Ashish ฉันแนะนำให้คุณอ่านคำตอบนี้มันอาจแก้ปัญหาของคุณได้ บาย! :)
Mistalis

0

นี่ใช้เวลาซักพักนึงแล้วนี่คือวิธีที่ฉันใช้งาน - การกำหนดเส้นทาง ASP ของ WebAPI โดยไม่ใช้ # สำหรับ SEO

  1. เพิ่มไปที่ Index.html - base href = "/">
  2. เพิ่ม $ locationProvider.html5Mode (จริง); เพื่อ app.config

  3. ฉันต้องการตัวควบคุมบางตัว (ซึ่งอยู่ในตัวควบคุมภายในบ้าน) เพื่อไม่ให้อัปโหลดภาพดังนั้นฉันจึงเพิ่มกฎนั้นลงใน RouteConfig

         routes.MapRoute(
            name: "Default2",
            url: "Home/{*.}",
            defaults: new { controller = "Home", action = "SaveImage" }
        );
  4. ใน Global.asax เพิ่มสิ่งต่อไปนี้ - ตรวจสอบให้แน่ใจว่าละเว้น API และเส้นทางการอัพโหลดรูปภาพให้พวกเขาทำงานได้ตามปกติมิฉะนั้นเปลี่ยนเส้นทางทุกอย่างอื่น

     private const string ROOT_DOCUMENT = "/Index.html";
    
    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
        var path = Request.Url.AbsolutePath;
        var isApi = path.StartsWith("/api", StringComparison.InvariantCultureIgnoreCase);
        var isImageUpload = path.StartsWith("/home", StringComparison.InvariantCultureIgnoreCase);
    
        if (isApi || isImageUpload)
            return;
    
        string url = Request.Url.LocalPath;
        if (!System.IO.File.Exists(Context.Server.MapPath(url)))
            Context.RewritePath(ROOT_DOCUMENT);
    }
  5. ตรวจสอบให้แน่ใจว่าใช้ $ location.url ('/ XXX') และไม่ใช่ window.location ... เพื่อเปลี่ยนเส้นทาง

  6. อ้างอิงไฟล์ CSS ด้วยพา ธ สัมบูรณ์

และไม่

<link href="app/content/bootstrapwc.css" rel="stylesheet" />

Final note - การทำแบบนี้ทำให้ฉันควบคุมได้อย่างเต็มที่และฉันไม่จำเป็นต้องทำอะไรกับเว็บ config

หวังว่านี่จะช่วยได้เพราะฉันใช้เวลาคิดสักครู่


0

ฉันต้องการเข้าถึงแอปพลิเคชันของฉันด้วยโหมด HTML5 และโทเค็นคงที่แล้วเปลี่ยนเป็นวิธี hashbang (เพื่อเก็บโทเค็นเพื่อให้ผู้ใช้สามารถรีเฟรชหน้าเว็บของเขาได้)

URL สำหรับการเข้าถึงแอปของฉัน:

http://myapp.com/amazing_url?token=super_token

จากนั้นเมื่อผู้ใช้โหลดหน้า:

http://myapp.com/amazing_url?token=super_token#/amazing_url

จากนั้นเมื่อผู้ใช้นำทาง:

http://myapp.com/amazing_url?token=super_token#/another_url

ด้วยวิธีนี้ฉันเก็บโทเค็นใน URL และคงสถานะไว้เมื่อผู้ใช้เรียกดู ฉันสูญเสียการมองเห็น URL เล็กน้อย แต่ไม่มีวิธีที่สมบูรณ์แบบในการทำเช่นนั้น

ดังนั้นอย่าเปิดใช้งานโหมด HTML5 แล้วเพิ่มตัวควบคุมนี้:

.config ($stateProvider)->
    $stateProvider.state('home-loading', {
         url: '/',
         controller: 'homeController'
    })
.controller 'homeController', ($state, $location)->
    if window.location.pathname != '/'
        $location.url(window.location.pathname+window.location.search).replace()
    else
        $state.go('home', {}, { location: 'replace' })
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.